version 0.0.4
This commit is contained in:
parent
34d572f928
commit
16995e5f9d
122
.gitignore
vendored
122
.gitignore
vendored
|
|
@ -1,119 +1,5 @@
|
|||
# User-specific stuff
|
||||
.idea/
|
||||
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
.idea
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
**/build/
|
||||
|
||||
# Common working directory
|
||||
run/
|
||||
runs/
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
build
|
||||
run
|
||||
run-data
|
||||
86
build.gradle
86
build.gradle
|
|
@ -11,6 +11,7 @@ buildscript {
|
|||
plugins {
|
||||
id 'eclipse'
|
||||
id 'idea'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.gradle' version '[6.0.16,6.2)'
|
||||
id 'org.parchmentmc.librarian.forgegradle' version '1.+'
|
||||
}
|
||||
|
|
@ -25,14 +26,14 @@ base {
|
|||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
|
||||
minecraft {
|
||||
mappings channel: mapping_channel, version: mapping_version
|
||||
|
||||
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
|
||||
copyIdeResources = true
|
||||
|
||||
runs {
|
||||
|
|
@ -52,6 +53,8 @@ minecraft {
|
|||
|
||||
var client = client {
|
||||
property 'forge.enabledGameTestNamespaces', mod_id
|
||||
property 'mixin.env.remapRefMap', 'true'
|
||||
property 'mixin.env.refMapRemappingFile', "${project.projectDir}/build/createSrgToMcp/output.srg"
|
||||
}
|
||||
|
||||
client_0 {
|
||||
|
|
@ -66,6 +69,8 @@ minecraft {
|
|||
|
||||
server {
|
||||
property 'forge.enabledGameTestNamespaces', mod_id
|
||||
property 'mixin.env.remapRefMap', 'true'
|
||||
property 'mixin.env.refMapRemappingFile', "${project.projectDir}/build/createSrgToMcp/output.srg"
|
||||
args '--nogui'
|
||||
}
|
||||
|
||||
|
|
@ -116,6 +121,14 @@ dependencies {
|
|||
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
|
||||
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
||||
compileOnly 'org.spongepowered:mixin:0.8.5'
|
||||
implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1"))
|
||||
|
||||
implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.4.1") {
|
||||
jarJar.ranged(it, "[0.4.1,)")
|
||||
})
|
||||
implementation fg.deobf("dev.kosmx.player-anim:player-animation-lib-forge:1.0.2-rc1+1.20")
|
||||
implementation fg.deobf("io.github.kosmx.bendy-lib:bendy-lib-forge:4.0.0")
|
||||
implementation fg.deobf("curse.maven:freecam-by-zergatul-618947:5402097")
|
||||
}
|
||||
|
||||
tasks.named('processResources', ProcessResources).configure {
|
||||
|
|
@ -156,6 +169,75 @@ tasks.named('jar', Jar).configure {
|
|||
finalizedBy 'reobfJar'
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc).configureEach {
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
options.addStringOption('Xdoclint:-missing', '-quiet')
|
||||
options.encoding = 'UTF-8'
|
||||
failOnError = false
|
||||
}
|
||||
|
||||
tasks.register('deobfJar', Jar) {
|
||||
from(sourceSets.main.output)
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
manifest {
|
||||
attributes(["Specification-Title" : mod_id,
|
||||
"Specification-Vendor" : mod_authors,
|
||||
"Specification-Version" : "1",
|
||||
"Implementation-Title" : project.name,
|
||||
"Implementation-Version" : project.jar.archiveVersion,
|
||||
"Implementation-Vendor" : mod_authors,
|
||||
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")])
|
||||
}
|
||||
dependsOn classes
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact deobfJar
|
||||
artifact javadocJar
|
||||
artifact sourcesJar
|
||||
|
||||
groupId = project.group
|
||||
artifactId = mod_id
|
||||
version = project.version
|
||||
pom {
|
||||
name = mod_id
|
||||
description = mod_description
|
||||
url = mod_url
|
||||
licenses {
|
||||
license {
|
||||
name = mod_license
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = "{mod_id}"
|
||||
name = mod_authors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = 'LTDNexus'
|
||||
url = 'https://nexus.bot.leisuretimedock.top/repository/maven-releases/'
|
||||
credentials {
|
||||
username = System.getenv('LTDNexusUsername') ?: ''
|
||||
password = System.getenv('LTDNexusPassword') ?: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradle.buildFinished {
|
||||
def javadocJar = file("build/libs/${mod_id}-${mod_version}-javadoc.jar")
|
||||
def sourcesJar = file("build/libs/${mod_id}-${mod_version}-sources.jar")
|
||||
if (javadocJar.exists()) ant.delete(file: javadocJar)
|
||||
if (sourcesJar.exists()) ant.delete(file: sourcesJar)
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ mapping_version=2023.09.03-1.20.1
|
|||
mod_id=sccore
|
||||
mod_name=SnowyCrescentCore
|
||||
mod_license=GNU AGPL 3.0
|
||||
mod_version=1.20.1-0.0.2
|
||||
mod_version=1.20.1-0.0.4
|
||||
mod_group_id=com.linearpast
|
||||
mod_authors=LostInLinearPast
|
||||
mod_description=A lib.
|
||||
mod_description=A lib about capability and player animator.
|
||||
mod_credits=
|
||||
mod_url=https://qm.qq.com/q/k0NVzvUdlC
|
||||
mod_url=https://github.com/Linearpast/SnowyCrescentCore
|
||||
|
|
|
|||
|
|
@ -1,28 +1,47 @@
|
|||
package com.linearpast.sccore;
|
||||
|
||||
|
||||
import com.linearpast.sccore.animation.registry.AnimationRegistry;
|
||||
import com.linearpast.sccore.capability.CapabilityUtils;
|
||||
import com.linearpast.sccore.example.ModCaps;
|
||||
import com.linearpast.sccore.network.Channel;
|
||||
import com.linearpast.sccore.core.ModChannel;
|
||||
import com.linearpast.sccore.core.ModCommands;
|
||||
import com.linearpast.sccore.core.ModConfigs;
|
||||
import com.linearpast.sccore.example.animation.ModAnimation;
|
||||
import com.linearpast.sccore.example.capability.ModCapability;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@Mod(SnowyCrescentCore.MODID)
|
||||
public class SnowyCrescentCore {
|
||||
|
||||
public static final Logger log = LoggerFactory.getLogger(SnowyCrescentCore.class);
|
||||
public static final String MODID = "sccore";
|
||||
public static final String ENABLE_EXAMPLES_PROPERTY_KEY = "sccore.enable_examples";
|
||||
|
||||
public SnowyCrescentCore() {
|
||||
IEventBus forgeBus = MinecraftForge.EVENT_BUS;
|
||||
CapabilityUtils.registerHandler(forgeBus);
|
||||
Channel.register();
|
||||
ModLoadingContext modLoadingContext = ModLoadingContext.get();
|
||||
modLoadingContext.registerConfig(ModConfig.Type.COMMON, ModConfigs.Common.SPEC);
|
||||
|
||||
if(!FMLEnvironment.production){
|
||||
ModCaps.register();
|
||||
ModCaps.addListenerToEvent(forgeBus);
|
||||
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
IEventBus forgeBus = MinecraftForge.EVENT_BUS;
|
||||
|
||||
CapabilityUtils.registerHandler(forgeBus);
|
||||
ModChannel.register();
|
||||
AnimationRegistry.register();
|
||||
AnimationRegistry.addAnimationListener(forgeBus, modBus);
|
||||
ModCommands.registerCommands(forgeBus, modBus);
|
||||
|
||||
if(!FMLEnvironment.production || Boolean.getBoolean(ENABLE_EXAMPLES_PROPERTY_KEY)) {
|
||||
ModCapability.register();
|
||||
ModCapability.addListenerToEvent(forgeBus);
|
||||
ModAnimation.register(forgeBus, modBus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
package com.linearpast.sccore.animation;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import com.linearpast.sccore.animation.entity.AnimationRideEntity;
|
||||
import com.linearpast.sccore.animation.mixin.IMixinKeyframeAnimationPlayer;
|
||||
import com.linearpast.sccore.animation.network.toclient.SyncAnimationPacket;
|
||||
import com.linearpast.sccore.animation.network.toserver.PlayAnimationRequestPacket;
|
||||
import com.linearpast.sccore.animation.network.toserver.PlayAnimationRidePacket;
|
||||
import com.linearpast.sccore.core.ModChannel;
|
||||
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
||||
import dev.kosmx.playerAnim.api.layered.KeyframeAnimationPlayer;
|
||||
import dev.kosmx.playerAnim.api.layered.ModifierLayer;
|
||||
import dev.kosmx.playerAnim.api.layered.modifier.AbstractFadeModifier;
|
||||
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
||||
import dev.kosmx.playerAnim.core.util.Ease;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class AnimationPlayer {
|
||||
public static void requestAnimationToServer(ResourceLocation layer, @Nullable ResourceLocation animation) {
|
||||
ModChannel.sendToServer(new PlayAnimationRequestPacket(layer, animation));
|
||||
}
|
||||
|
||||
public static boolean serverPlayAnimation(ServerPlayer serverPlayer, ResourceLocation layer, @Nullable ResourceLocation animation) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(serverPlayer).orElse(null);
|
||||
if(data == null) return false;
|
||||
if(animation != null) {
|
||||
return data.mergeAnimation(layer, animation);
|
||||
} else {
|
||||
return data.removeAnimation(layer);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean playAnimationWithRide(ServerPlayer serverPlayer, ResourceLocation layer, @Nullable ResourceLocation animation, boolean force){
|
||||
if(animation != null) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(serverPlayer).orElse(null);
|
||||
if(data == null) return false;
|
||||
data.setRideAnimLayer(layer);
|
||||
return AnimationRideEntity.create(serverPlayer, layer, animation, force);
|
||||
} else {
|
||||
serverPlayer.unRide();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void requestAnimationRideToServer(ResourceLocation layer, @Nullable ResourceLocation animation, boolean force) {
|
||||
ModChannel.sendToServer(new PlayAnimationRidePacket(layer, animation, force));
|
||||
}
|
||||
|
||||
public static void clearAnimation(ServerPlayer serverPlayer) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(serverPlayer).orElse(null);
|
||||
if(data == null) return;
|
||||
data.clearAnimations();
|
||||
}
|
||||
|
||||
public static void syncAnimation(ServerPlayer player, ServerPlayer target, ResourceLocation layer) {
|
||||
ModChannel.sendToPlayer(new SyncAnimationPacket(player.getUUID(), target.getUUID(), layer), player);
|
||||
ModChannel.sendToPlayer(new SyncAnimationPacket(player.getUUID(), target.getUUID(), layer), target);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void syncAnimation(AbstractClientPlayer clientPlayer, AbstractClientPlayer target, ResourceLocation layer) {
|
||||
try {
|
||||
ModifierLayer<IAnimation> modifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(clientPlayer).get(layer);
|
||||
ModifierLayer<IAnimation> targetModifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(target).get(layer);
|
||||
if(modifierLayer == null || targetModifierLayer == null) return;
|
||||
IMixinKeyframeAnimationPlayer animation = (IMixinKeyframeAnimationPlayer) modifierLayer.getAnimation();
|
||||
KeyframeAnimationPlayer targetAnimation = (KeyframeAnimationPlayer) targetModifierLayer.getAnimation();
|
||||
if(animation == null || targetAnimation == null) return;
|
||||
int currentTick = targetAnimation.getCurrentTick();
|
||||
animation.sccore$setCurrentTick(currentTick);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void playAnimation(AbstractClientPlayer clientPlayer, ResourceLocation layer, @Nullable ResourceLocation animation) {
|
||||
try {
|
||||
ModifierLayer<IAnimation> modifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(clientPlayer).get(layer);
|
||||
if(animation == null) {
|
||||
if(modifierLayer != null) {
|
||||
modifierLayer.replaceAnimationWithFade(
|
||||
AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
|
||||
null
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Animation anim = AnimationUtils.getAnimation(animation);
|
||||
if(anim == null) return;
|
||||
if(modifierLayer == null) return;
|
||||
KeyframeAnimation keyframeAnimation = anim.getAnimation();
|
||||
if(keyframeAnimation == null) return;
|
||||
Objects.requireNonNull(modifierLayer).replaceAnimationWithFade(
|
||||
AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
|
||||
new KeyframeAnimationPlayer(keyframeAnimation)
|
||||
);
|
||||
}catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Failed to play animation : {}", animation, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
package com.linearpast.sccore.animation;
|
||||
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import com.linearpast.sccore.animation.data.Ride;
|
||||
import com.linearpast.sccore.animation.entity.AnimationRideEntity;
|
||||
import com.linearpast.sccore.animation.event.AnimationLayerRegistry;
|
||||
import com.linearpast.sccore.animation.event.EntityRendererRegistry;
|
||||
import com.linearpast.sccore.animation.event.client.CameraAnglesModify;
|
||||
import com.linearpast.sccore.animation.event.client.ClientPlayerTick;
|
||||
import com.linearpast.sccore.animation.network.toserver.RefreshAnimationPacket;
|
||||
import com.linearpast.sccore.animation.registry.AnimationEntities;
|
||||
import com.linearpast.sccore.animation.registry.AnimationRegistry;
|
||||
import com.linearpast.sccore.core.ModChannel;
|
||||
import com.linearpast.sccore.core.ModLazyRun;
|
||||
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
||||
import dev.kosmx.playerAnim.api.layered.KeyframeAnimationPlayer;
|
||||
import dev.kosmx.playerAnim.api.layered.ModifierLayer;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Animation Util. May be you can call it Api.
|
||||
*/
|
||||
public class AnimationUtils {
|
||||
public static final String AnimModId = "playeranimator";
|
||||
public static final ModLazyRun ANIMATION_RUNNER = new ModLazyRun(AnimModId) {
|
||||
@Override
|
||||
public void addCommonListener(IEventBus forgeBus, IEventBus modBus) {
|
||||
AnimationEntities.register(modBus);
|
||||
modBus.addListener(AnimationLayerRegistry::onCommonSetUp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClientListener(IEventBus forgeBus, IEventBus modBus) {
|
||||
forgeBus.addListener(CameraAnglesModify::changeCameraView);
|
||||
modBus.addListener(AnimationLayerRegistry::onClientSetup);
|
||||
modBus.addListener(EntityRendererRegistry::registerEntityRenderer);
|
||||
forgeBus.addListener(ClientPlayerTick::onPlayerTick);
|
||||
forgeBus.addListener(ClientPlayerTick::delayRuns);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Play animation.
|
||||
* If run in Dist.CLIENT, the serverPlayer can be null.
|
||||
* If animation be null, it will remove animation on layer.
|
||||
* </pre>
|
||||
* @param serverPlayer Target player
|
||||
* @param layer Target layer
|
||||
* @param animation Animation
|
||||
* @return If success
|
||||
*/
|
||||
public static boolean playAnimation(@Nullable ServerPlayer serverPlayer, ResourceLocation layer, @Nullable ResourceLocation animation) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(isAnimationLayerPresent(layer) && (animation == null || isAnimationPresent(animation))) {
|
||||
if(serverPlayer != null) {
|
||||
return AnimationPlayer.serverPlayAnimation(serverPlayer, layer, animation);
|
||||
}else {
|
||||
AnimationPlayer.requestAnimationToServer(layer, animation);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Play animation with ride. Player will ride an entity, then play animation.
|
||||
* When player unride, animation will be remove.
|
||||
* If run in Dist.CLIENT, the serverPlayer can be null.
|
||||
* If animation be null, it will call function: {@link ServerPlayer#unRide()}
|
||||
* If player is riding and the "force" is false, it will return false
|
||||
* </pre>
|
||||
* @param serverPlayer Target player
|
||||
* @param layer Target layer
|
||||
* @param animation Animation
|
||||
* @param force If force to ride and play animation
|
||||
* @return If success
|
||||
*/
|
||||
public static boolean playAnimationWithRide(@Nullable ServerPlayer serverPlayer, ResourceLocation layer, @Nullable ResourceLocation animation, boolean force) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(isAnimationLayerPresent(layer) && (animation == null || isAnimationPresent(animation))) {
|
||||
Animation anim = AnimationUtils.getAnimation(animation);
|
||||
if(anim != null && anim.getRide() == null) return false;
|
||||
if(serverPlayer != null) {
|
||||
if(serverPlayer.getVehicle() != null && force) serverPlayer.unRide();
|
||||
else if(serverPlayer.getVehicle() != null) return false;
|
||||
AnimationPlayer.playAnimationWithRide(serverPlayer, layer, animation, true);
|
||||
} else {
|
||||
AnimationPlayer.requestAnimationRideToServer(layer, animation, force);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove animation.
|
||||
* @see AnimationUtils#playAnimation
|
||||
* @param serverPlayer Target player
|
||||
* @param layer Target layer
|
||||
* @return If success
|
||||
*/
|
||||
public static boolean removeAnimation(@Nullable ServerPlayer serverPlayer, ResourceLocation layer) {
|
||||
return playAnimation(serverPlayer, layer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get animation which is playing now on player. <br>
|
||||
* If layer is null, it will return the first playing animation which can be found.
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @return Playing animation resource location
|
||||
*/
|
||||
@Nullable
|
||||
public static ResourceLocation getAnimationPlaying(Player player, @Nullable ResourceLocation layer) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return null;
|
||||
if(layer == null){
|
||||
for (ResourceLocation value : data.getAnimations().values()) {
|
||||
if(value != null) return value;
|
||||
}
|
||||
} else if (isAnimationLayerPresent(layer)) {
|
||||
if(data.isAnimationPresent(layer)){
|
||||
return data.getAnimation(layer);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if layer exist animation which is playing.
|
||||
* <p>
|
||||
* Only in dist client
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @return If layer exist animation which is playing
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isClientAnimationPlaying(AbstractClientPlayer player, @Nullable ResourceLocation layer) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
try {
|
||||
Set<ResourceLocation> resourceLocations = new HashSet<>();
|
||||
if(layer == null) resourceLocations = AnimationLayerRegistry.getAnimLayers().keySet();
|
||||
else resourceLocations.add(layer);
|
||||
for (ResourceLocation location : resourceLocations) {
|
||||
ModifierLayer<IAnimation> animationModifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(player).get(location);
|
||||
if(animationModifierLayer == null) continue;
|
||||
KeyframeAnimationPlayer animation = (KeyframeAnimationPlayer) animationModifierLayer.getAnimation();
|
||||
if(animation == null) return false;
|
||||
int currentTick = animation.getCurrentTick();
|
||||
int stopTick = animation.getStopTick();
|
||||
return currentTick <= stopTick;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync animation tick to client
|
||||
* @param player Player
|
||||
* @param target Target player
|
||||
* @param layer Target layer
|
||||
*/
|
||||
public static void syncAnimation(ServerPlayer player, ServerPlayer target, ResourceLocation layer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> AnimationPlayer.syncAnimation(player, target, layer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync animation tick on client
|
||||
* @param player Player
|
||||
* @param target Target player
|
||||
* @param layer Target layer
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void syncAnimation(AbstractClientPlayer player, AbstractClientPlayer target, ResourceLocation layer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> AnimationPlayer.syncAnimation(player, target, layer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Join animation.
|
||||
* @param player Will join player
|
||||
* @param target Joined player
|
||||
* @param force If force
|
||||
* @return If success
|
||||
*/
|
||||
public static boolean joinAnimation(ServerPlayer player, ServerPlayer target, boolean force) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
Entity vehicle = target.getVehicle();
|
||||
if(!(vehicle instanceof AnimationRideEntity rideEntity)) return false;
|
||||
int playerCount = rideEntity.getPlayers().size();
|
||||
Animation animation = rideEntity.getAnimation();
|
||||
if(animation == null) return false;
|
||||
Ride ride = animation.getRide();
|
||||
if(ride == null) return false;
|
||||
int maxCount = ride.getComponentAnimations().size();
|
||||
if(playerCount >= maxCount) return false;
|
||||
return player.startRiding(vehicle, force);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Start animation together...
|
||||
* The max participants' count is depend on your animation.
|
||||
* Max count = Size of {@link Ride#getComponentAnimations()}
|
||||
* </pre>
|
||||
* @param player Leader
|
||||
* @param layer Target layer
|
||||
* @param animation Animation location
|
||||
* @param force If force start leader
|
||||
* @param participants Participants
|
||||
*/
|
||||
public static void startAnimationTogether(
|
||||
ServerPlayer player,
|
||||
ResourceLocation layer,
|
||||
ResourceLocation animation,
|
||||
boolean force,
|
||||
ServerPlayer ... participants
|
||||
) {
|
||||
AnimationUtils.playAnimationWithRide(player, layer, animation, force);
|
||||
for (ServerPlayer participant : participants) {
|
||||
AnimationUtils.joinAnimation(participant, player, force);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach animation
|
||||
* @param player Player
|
||||
*/
|
||||
public static void detachAnimation(ServerPlayer player) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
if(player.getVehicle() instanceof AnimationRideEntity) {
|
||||
player.unRide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear Player animations.
|
||||
* @param serverPlayer Target player
|
||||
*/
|
||||
public static void clearAnimation(ServerPlayer serverPlayer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> AnimationPlayer.clearAnimation(serverPlayer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if layer exist and has been register.
|
||||
* @param layer Target layer
|
||||
* @return If layer exist and has been register
|
||||
*/
|
||||
public static boolean isAnimationLayerPresent(ResourceLocation layer) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> AnimationLayerRegistry.isLayerPresent(layer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if animation exist and has been register.
|
||||
* @param location Animation resource location
|
||||
* @return If animation exist and has been register
|
||||
*/
|
||||
public static boolean isAnimationPresent(ResourceLocation location) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> AnimationRegistry.isAnimationPresent(location));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an animation through static function
|
||||
* @param location Animation resource location
|
||||
* @param animation Animation data
|
||||
*/
|
||||
public static void registerAnimation(ResourceLocation location, Animation animation) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> AnimationRegistry.registerAnimation(location, animation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an animation layer through static function. <br>
|
||||
* The number is bigger and the priority is higher. <br>
|
||||
* It must run before these events : <br>
|
||||
* {@link net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent} <br>
|
||||
* {@link net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent}
|
||||
* @param location Layer location key
|
||||
* @param priority Layer priority,
|
||||
*/
|
||||
public static void registerAnimationLayer(ResourceLocation location, int priority) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> AnimationLayerRegistry.registerPlayerAnimation(location, priority));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get animation data from animation resource location. <br>
|
||||
* You will get null if you use it too early. <br>
|
||||
* Suggest only use it in game has loaded.
|
||||
* @param location Animation resource location
|
||||
* @return Animation data
|
||||
*/
|
||||
@Nullable
|
||||
public static Animation getAnimation(ResourceLocation location) {
|
||||
return AnimationRegistry.getAnimation(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LyingType when there are animations which playing on player. <br>
|
||||
* And It will return the first which be found.
|
||||
* @param player Target player
|
||||
* @return The first LyingType it find.
|
||||
*/
|
||||
@Nullable
|
||||
public static Animation.LyingType getSideView(Player player) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return null;
|
||||
Animation.LyingType lyingType = null;
|
||||
for (ResourceLocation value : data.getAnimations().values()) {
|
||||
Animation animation = getAnimation(value);
|
||||
if(animation == null) return null;
|
||||
Animation.LyingType type = animation.getLyingType();
|
||||
if(type == null) continue;
|
||||
switch (type) {
|
||||
case FRONT,BACK -> {}
|
||||
case LEFT,RIGHT -> lyingType = animation.getLyingType();
|
||||
}
|
||||
}
|
||||
return lyingType;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HeightModifier when there are animations which playing on player. <br>
|
||||
* And It will return the first which be found.
|
||||
* @param player Target player
|
||||
* @return The first HeightModifier it find.
|
||||
*/
|
||||
public static float getHeightModifier(Player player) {
|
||||
Float result = ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if (data == null) return 1.0f;
|
||||
float heightModifier = 1.0f;
|
||||
for (ResourceLocation value : data.getAnimations().values()) {
|
||||
Animation animation = getAnimation(value);
|
||||
if (animation == null) continue;
|
||||
float animationHeightModifier = animation.getHeightModifier();
|
||||
heightModifier = Math.min(heightModifier, animationHeightModifier);
|
||||
}
|
||||
return heightModifier;
|
||||
});
|
||||
return result == null ? 1.0f : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if animation is playing <br>
|
||||
* if not, it will remove the animation resource location on client
|
||||
* <p>
|
||||
* Only in dist client
|
||||
* @param clientPlayer Target player
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void refreshAnimation(AbstractClientPlayer clientPlayer) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(clientPlayer).orElse(null);
|
||||
if(data == null) return;
|
||||
Set<ResourceLocation> oldLayers = new HashSet<>(data.getAnimations().keySet());
|
||||
oldLayers.forEach(layer -> {
|
||||
if (isClientAnimationPlaying(clientPlayer, layer)) {
|
||||
oldLayers.remove(layer);
|
||||
}
|
||||
});
|
||||
ModChannel.sendToServer(new RefreshAnimationPacket(oldLayers));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
package com.linearpast.sccore.animation.capability;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.animation.event.AnimationLayerRegistry;
|
||||
import com.linearpast.sccore.animation.network.toclient.AnimationCapabilityPacket;
|
||||
import com.linearpast.sccore.capability.CapabilityUtils;
|
||||
import com.linearpast.sccore.capability.data.ICapabilitySync;
|
||||
import com.linearpast.sccore.capability.data.player.SimplePlayerCapabilitySync;
|
||||
import com.linearpast.sccore.capability.network.SimpleCapabilityPacket;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AnimationDataCapability extends SimplePlayerCapabilitySync implements IAnimationCapability {
|
||||
public static final ResourceLocation key = new ResourceLocation(SnowyCrescentCore.MODID, "animation_data");
|
||||
|
||||
public static final String AnimMap = "AnimMap";
|
||||
public static final String RideAnimLayer = "RideAnimLayer";
|
||||
|
||||
private final Map<ResourceLocation, ResourceLocation> animMap = new HashMap<>();
|
||||
private ResourceLocation rideAnimLayer;
|
||||
|
||||
@Override
|
||||
public void mergeAnimations(Map<ResourceLocation, ResourceLocation> animations) {
|
||||
animations.forEach((key, value) -> {
|
||||
if (AnimationLayerRegistry.getAnimLayers().containsKey(key)) {
|
||||
if (AnimationUtils.isAnimationPresent(value)) {
|
||||
this.animMap.put(key, value);
|
||||
setDirty(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeAnimation(ResourceLocation layer, ResourceLocation animation) {
|
||||
if (AnimationLayerRegistry.getAnimLayers().containsKey(layer)) {
|
||||
if (AnimationUtils.isAnimationPresent(animation)) {
|
||||
this.animMap.put(layer, animation);
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAnimation(ResourceLocation layer) {
|
||||
ResourceLocation remove = this.animMap.remove(layer);
|
||||
if(remove != null) {
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getAnimation(ResourceLocation layer) {
|
||||
return animMap.get(layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ResourceLocation, ResourceLocation> getAnimations() {
|
||||
return animMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAnimations() {
|
||||
this.animMap.clear();
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnimationPresent(ResourceLocation layer) {
|
||||
return animMap.containsKey(layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRideAnimLayer(ResourceLocation rideAnimLayer) {
|
||||
this.rideAnimLayer = rideAnimLayer;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getRideAnimLayer() {
|
||||
return rideAnimLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData) {
|
||||
IAnimationCapability data = (IAnimationCapability) oldData;
|
||||
this.animMap.clear();
|
||||
this.animMap.putAll(data.getAnimations());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag toTag(CompoundTag tag) {
|
||||
if(!animMap.isEmpty()) {
|
||||
CompoundTag animMapTag = new CompoundTag();
|
||||
animMap.forEach((string, animation) ->
|
||||
animMapTag.putString(string.toString(), animation.toString())
|
||||
);
|
||||
tag.put(AnimMap, animMapTag);
|
||||
}
|
||||
if(rideAnimLayer != null) tag.putString(RideAnimLayer, rideAnimLayer.toString());
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromTag(CompoundTag tag) {
|
||||
this.animMap.clear();
|
||||
if(tag.contains(AnimMap)) {
|
||||
CompoundTag animMapTag = tag.getCompound(AnimMap);
|
||||
animMapTag.getAllKeys().forEach(key -> this.animMap.put(
|
||||
new ResourceLocation(key),
|
||||
new ResourceLocation(animMapTag.getString(key))
|
||||
));
|
||||
}
|
||||
if(tag.contains(RideAnimLayer)) this.rideAnimLayer = new ResourceLocation(tag.getString(RideAnimLayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCapabilityPacket<Player> getDefaultPacket() {
|
||||
return new AnimationCapabilityPacket(serializeNBT());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachInit(Player player) {
|
||||
Map<ResourceLocation, ResourceLocation> map = new HashMap<>(this.animMap);
|
||||
map.forEach((key, value) -> {
|
||||
if(!AnimationUtils.isAnimationLayerPresent(key)) this.animMap.remove(key);
|
||||
if(!AnimationUtils.isAnimationPresent(value)) this.animMap.remove(key);
|
||||
if(key.equals(rideAnimLayer)) this.animMap.remove(key);
|
||||
});
|
||||
this.rideAnimLayer = null;
|
||||
}
|
||||
|
||||
public static Optional<IAnimationCapability> getCapability(Player player){
|
||||
return Optional.ofNullable(CapabilityUtils.getPlayerCapability(
|
||||
player, AnimationDataCapability.key, IAnimationCapability.class
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.linearpast.sccore.animation.capability.inter;
|
||||
|
||||
import com.linearpast.sccore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface IAnimationCapability extends ICapabilitySync<Player> {
|
||||
void mergeAnimations(Map<ResourceLocation, ResourceLocation> animations);
|
||||
boolean mergeAnimation(ResourceLocation layer, ResourceLocation animation);
|
||||
boolean removeAnimation(ResourceLocation layer);
|
||||
@Nullable ResourceLocation getAnimation(ResourceLocation layer);
|
||||
Map<ResourceLocation, ResourceLocation> getAnimations();
|
||||
void clearAnimations();
|
||||
boolean isAnimationPresent(ResourceLocation layer);
|
||||
|
||||
void setRideAnimLayer(ResourceLocation layer);
|
||||
ResourceLocation getRideAnimLayer();
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.linearpast.sccore.animation.command;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.command.argument.AnimationArgument;
|
||||
import com.linearpast.sccore.animation.command.argument.AnimationLayerArgument;
|
||||
import com.linearpast.sccore.animation.command.client.AnimationRefreshCommand;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class AnimationCommands {
|
||||
public static void commonCommandRegister(LiteralArgumentBuilder<CommandSourceStack> builder) {
|
||||
if(AnimationUtils.ANIMATION_RUNNER.isModLoaded()){
|
||||
LiteralArgumentBuilder<CommandSourceStack> anim = literal("anim");
|
||||
PlayAnimationCommand.register(anim);
|
||||
|
||||
builder.then(anim);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clientCommandRegister(LiteralArgumentBuilder<CommandSourceStack> builder) {
|
||||
if(AnimationUtils.ANIMATION_RUNNER.isModLoaded()) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> anim = literal("anim");
|
||||
AnimationRefreshCommand.register(anim);
|
||||
builder.then(anim);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerArguments(DeferredRegister<ArgumentTypeInfo<?, ?>> register) {
|
||||
register.register("animation",
|
||||
() -> ArgumentTypeInfos.registerByClass(
|
||||
AnimationArgument.class,
|
||||
SingletonArgumentInfo.contextFree(AnimationArgument::animation)
|
||||
)
|
||||
);
|
||||
register.register("layer",
|
||||
() -> ArgumentTypeInfos.registerByClass(
|
||||
AnimationLayerArgument.class,
|
||||
SingletonArgumentInfo.contextFree(AnimationLayerArgument::layer)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
package com.linearpast.sccore.animation.command;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.command.argument.AnimationArgument;
|
||||
import com.linearpast.sccore.animation.command.argument.AnimationLayerArgument;
|
||||
import com.linearpast.sccore.animation.entity.AnimationRideEntity;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class PlayAnimationCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand){
|
||||
animCommand.then(literal("play").then(argument("players", EntityArgument.players()).then(
|
||||
argument("layer", AnimationLayerArgument.layer())
|
||||
.then(argument("animation", AnimationArgument.animation())
|
||||
.requires(cs -> cs.hasPermission(2))
|
||||
.executes(context -> playAnimation(context, false))
|
||||
.then(argument("withRide", BoolArgumentType.bool())
|
||||
.executes(context -> playAnimation(
|
||||
context, BoolArgumentType.getBool(context, "withRide")
|
||||
))
|
||||
.then(argument("forced", BoolArgumentType.bool())
|
||||
.executes(context -> playAnimation(
|
||||
context, BoolArgumentType.getBool(context, "withRide"))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)));
|
||||
animCommand.then(literal("remove")
|
||||
.requires(cs -> cs.hasPermission(2))
|
||||
.executes(PlayAnimationCommand::clearAnimation)
|
||||
.then(argument("players", EntityArgument.players())
|
||||
.requires(cs -> cs.hasPermission(2))
|
||||
.executes(PlayAnimationCommand::clearAnimation)
|
||||
.then(argument("layer", AnimationLayerArgument.layer())
|
||||
.executes(PlayAnimationCommand::removeAnimation)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int playAnimation(CommandContext<CommandSourceStack> ctx, boolean withRide) {
|
||||
CommandSourceStack source = ctx.getSource();
|
||||
try {
|
||||
Collection<ServerPlayer> players = EntityArgument.getPlayers(ctx, "players");
|
||||
String animation = AnimationArgument.getAnimation(ctx, "animation").replace("$", ":");
|
||||
String layer = AnimationLayerArgument.getLayer(ctx, "layer").replace("$", ":");
|
||||
ResourceLocation layerLocation = new ResourceLocation(layer);
|
||||
ResourceLocation animLocation = new ResourceLocation(animation);
|
||||
boolean animationPresent = AnimationUtils.isAnimationPresent(animLocation);
|
||||
boolean layerPresent = AnimationUtils.isAnimationLayerPresent(layerLocation);
|
||||
if(!animationPresent) {
|
||||
source.sendFailure(Component.literal("Animation is not present.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
if(!layerPresent) {
|
||||
source.sendFailure(Component.literal("Layer is not present.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
Set<ServerPlayer> playerSet = Set.copyOf(players);
|
||||
playerSet.forEach(player -> {
|
||||
if(withRide) {
|
||||
boolean forced = false;
|
||||
try { forced = BoolArgumentType.getBool(ctx, "forced");}
|
||||
catch (Exception ignored) {}
|
||||
if(AnimationUtils.playAnimationWithRide(player, layerLocation, animLocation, forced)) {
|
||||
players.remove(player);
|
||||
}
|
||||
} else {
|
||||
if (AnimationUtils.playAnimation(player, layerLocation, animLocation)) {
|
||||
players.remove(player);
|
||||
}
|
||||
}
|
||||
});
|
||||
MutableComponent fail = Component.literal("Fail to play animation with: ");
|
||||
int successNum = playerSet.size() - players.size();
|
||||
if(successNum > 0) {
|
||||
MutableComponent success = Component.literal("Success to play animation with ")
|
||||
.append(successNum + "")
|
||||
.append(" player");
|
||||
if(successNum > 1) success.append("s.");
|
||||
else success.append(".");
|
||||
source.sendSuccess(() -> success.withStyle(ChatFormatting.GREEN), true);
|
||||
}
|
||||
List<ServerPlayer> list = players.stream().toList();
|
||||
if(!list.isEmpty()) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
fail.append(list.get(i).getName());
|
||||
if (i < list.size() - 1) {
|
||||
fail.append(", ");
|
||||
}
|
||||
}
|
||||
fail.append(".");
|
||||
source.sendFailure(fail.withStyle(ChatFormatting.RED));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.literal("Run command failure.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int removeAnimation(CommandContext<CommandSourceStack> ctx) {
|
||||
CommandSourceStack source = ctx.getSource();
|
||||
try {
|
||||
Collection<ServerPlayer> players = EntityArgument.getPlayers(ctx, "players");
|
||||
String layer = AnimationLayerArgument.getLayer(ctx, "layer").replace("$", ":");
|
||||
ResourceLocation layerLocation = new ResourceLocation(layer);
|
||||
boolean layerPresent = AnimationUtils.isAnimationLayerPresent(layerLocation);
|
||||
if(!layerPresent) {
|
||||
source.sendFailure(Component.literal("Layer is not present.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
Set<ServerPlayer> playerSet = Set.copyOf(players);
|
||||
playerSet.forEach(player -> {
|
||||
if(player.getVehicle() instanceof AnimationRideEntity rideEntity && rideEntity.getLayer().equals(layerLocation)) {
|
||||
player.unRide();
|
||||
players.remove(player);
|
||||
}
|
||||
if (AnimationUtils.removeAnimation(player, layerLocation)) {
|
||||
players.remove(player);
|
||||
}
|
||||
});
|
||||
MutableComponent fail = Component.literal("Fail to remove animation with: ");
|
||||
int successNum = playerSet.size() - players.size();
|
||||
if(successNum > 0) {
|
||||
MutableComponent success = Component.literal("Success to remove animation with ")
|
||||
.append(successNum + "")
|
||||
.append(" player");
|
||||
if(successNum > 1) success.append("s.");
|
||||
else success.append(".");
|
||||
source.sendSuccess(() -> success.withStyle(ChatFormatting.GREEN), true);
|
||||
}
|
||||
List<ServerPlayer> list = players.stream().toList();
|
||||
if(!list.isEmpty()) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
fail.append(list.get(i).getName());
|
||||
if (i < list.size() - 1) {
|
||||
fail.append(", ");
|
||||
}
|
||||
}
|
||||
fail.append(".");
|
||||
source.sendFailure(fail.withStyle(ChatFormatting.RED));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.literal("Run command failure.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int clearAnimation(CommandContext<CommandSourceStack> ctx) {
|
||||
CommandSourceStack source = ctx.getSource();
|
||||
try {
|
||||
Collection<ServerPlayer> players;
|
||||
try {players = EntityArgument.getPlayers(ctx, "players");}
|
||||
catch (Exception ignored) { players = Set.of(source.getPlayerOrException()); }
|
||||
Set.copyOf(players).forEach(player -> {
|
||||
if(player.getVehicle() instanceof AnimationRideEntity) {
|
||||
player.unRide();
|
||||
}
|
||||
AnimationUtils.clearAnimation(player);
|
||||
});
|
||||
source.sendSuccess(() -> Component.literal("Animation cleared.").withStyle(ChatFormatting.GREEN), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.literal("Run command failure.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.linearpast.sccore.animation.command.argument;
|
||||
|
||||
import com.linearpast.sccore.animation.registry.AnimationRegistry;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AnimationArgument implements ArgumentType<String> {
|
||||
private static final Supplier<Collection<String>> EXAMPLES = () -> AnimationRegistry.getAnimations().keySet().stream()
|
||||
.map(ResourceLocation::toString).collect(Collectors.toSet());
|
||||
private static final DynamicCommandExceptionType UNKNOWN_TYPE = new DynamicCommandExceptionType(
|
||||
animation -> Component.literal("Unknow animation : " + animation.toString())
|
||||
);
|
||||
|
||||
private final Set<String> animationNames;
|
||||
public AnimationArgument() {
|
||||
this.animationNames = AnimationRegistry.getAnimations().keySet().stream()
|
||||
.map(ResourceLocation::toString).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static AnimationArgument animation() {
|
||||
return new AnimationArgument();
|
||||
}
|
||||
|
||||
public static String getAnimation(CommandContext<CommandSourceStack> context, String name) {
|
||||
return context.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
return SharedSuggestionProvider.suggest(animationNames, builder);
|
||||
}
|
||||
|
||||
public Collection<String> getExamples() {
|
||||
return EXAMPLES.get();
|
||||
}
|
||||
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
int start = reader.getCursor();
|
||||
while (reader.canRead() && canRead(reader.peek())) {
|
||||
reader.skip();
|
||||
}
|
||||
String s = reader.getString().substring(start, reader.getCursor());
|
||||
if (!animationNames.contains(s)) {
|
||||
throw UNKNOWN_TYPE.create(s);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canRead(char peek) {
|
||||
boolean origin = StringReader.isAllowedInUnquotedString(peek);
|
||||
return origin || peek == ':';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.linearpast.sccore.animation.command.argument;
|
||||
|
||||
import com.linearpast.sccore.animation.event.AnimationLayerRegistry;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AnimationLayerArgument implements ArgumentType<String> {
|
||||
private static final Supplier<Collection<String>> EXAMPLES = () -> AnimationLayerRegistry.getAnimLayers().keySet().stream()
|
||||
.map(ResourceLocation::toString).collect(Collectors.toSet());
|
||||
private static final DynamicCommandExceptionType UNKNOWN_TYPE = new DynamicCommandExceptionType(
|
||||
layer -> Component.literal("Unknow layer : " + layer.toString())
|
||||
);
|
||||
|
||||
private final Set<String> animationLayers;
|
||||
public AnimationLayerArgument() {
|
||||
this.animationLayers = AnimationLayerRegistry.getAnimLayers().keySet().stream()
|
||||
.map(ResourceLocation::toString).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static AnimationLayerArgument layer() {
|
||||
return new AnimationLayerArgument();
|
||||
}
|
||||
|
||||
public static String getLayer(CommandContext<CommandSourceStack> context, String name) {
|
||||
return context.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
return SharedSuggestionProvider.suggest(animationLayers, builder);
|
||||
}
|
||||
|
||||
public Collection<String> getExamples() {
|
||||
return EXAMPLES.get();
|
||||
}
|
||||
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
int start = reader.getCursor();
|
||||
while (reader.canRead() && canRead(reader.peek())) {
|
||||
reader.skip();
|
||||
}
|
||||
String s = reader.getString().substring(start, reader.getCursor());
|
||||
if (!animationLayers.contains(s)) {
|
||||
throw UNKNOWN_TYPE.create(s);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canRead(char peek) {
|
||||
boolean origin = StringReader.isAllowedInUnquotedString(peek);
|
||||
return origin || peek == ':';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.linearpast.sccore.animation.command.client;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class AnimationRefreshCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand){
|
||||
animCommand.then(literal("refresh").executes(AnimationRefreshCommand::refresh));
|
||||
}
|
||||
|
||||
private static int refresh(CommandContext<CommandSourceStack> ctx){
|
||||
CommandSourceStack source = ctx.getSource();
|
||||
try {
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
LocalPlayer player = instance.player;
|
||||
if(player == null) throw new RuntimeException();
|
||||
AnimationUtils.refreshAnimation(player);
|
||||
source.sendSuccess(() -> Component.literal("Animation refreshed.").withStyle(ChatFormatting.GREEN), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.literal("Run command failure.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package com.linearpast.sccore.animation.data;
|
||||
|
||||
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Animation {
|
||||
private final ResourceLocation name;
|
||||
private KeyframeAnimation animation;
|
||||
private float heightModifier = 1.0f;
|
||||
private float camYaw;
|
||||
private float camPitch;
|
||||
private float camRoll;
|
||||
private float camY;
|
||||
private @Nullable Animation.LyingType lyingType;
|
||||
private @Nullable Ride ride;
|
||||
|
||||
public Animation(ResourceLocation name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public enum LyingType {
|
||||
RIGHT,
|
||||
LEFT,
|
||||
FRONT,
|
||||
BACK
|
||||
}
|
||||
|
||||
public Animation withLyingType(@Nullable Animation.LyingType lyingType) {
|
||||
this.lyingType = lyingType;
|
||||
if(lyingType == null) return this;
|
||||
this.camY = -1.3f;
|
||||
this.camPitch = -90.0f;
|
||||
this.heightModifier = 0.3f;
|
||||
switch (lyingType) {
|
||||
case RIGHT -> {
|
||||
this.camRoll = 90.0f;
|
||||
this.camYaw = 90.0f;
|
||||
}
|
||||
case LEFT -> {
|
||||
this.camRoll = -90.0f;
|
||||
this.camYaw = -90.0f;
|
||||
}
|
||||
case BACK -> this.camPitch = 90.0f;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Animation withHeightModifier(float heightModifier) {
|
||||
this.heightModifier = heightModifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Animation withCamYaw(float camYaw) {
|
||||
this.camYaw = camYaw;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Animation withCamPitch(float camPitch) {
|
||||
this.camPitch = camPitch;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Animation withCamRoll(float camRoll) {
|
||||
this.camRoll = camRoll;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Animation withCamY(float camY) {
|
||||
this.camY = camY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Animation withRide(Ride ride) {
|
||||
this.ride = ride;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setLyingType(@Nullable LyingType lyingType) {
|
||||
this.lyingType = lyingType;
|
||||
}
|
||||
|
||||
public float getCamRoll() {
|
||||
return camRoll;
|
||||
}
|
||||
|
||||
public float getCamPitch() {
|
||||
return camPitch;
|
||||
}
|
||||
|
||||
public float getCamYaw() {
|
||||
return camYaw;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KeyframeAnimation getAnimation() {
|
||||
return PlayerAnimationRegistry.getAnimation(name);
|
||||
}
|
||||
|
||||
public @Nullable Animation.LyingType getLyingType() {
|
||||
return lyingType;
|
||||
}
|
||||
|
||||
public float getCamY() {
|
||||
return camY;
|
||||
}
|
||||
|
||||
public float getHeightModifier() {
|
||||
return heightModifier;
|
||||
}
|
||||
|
||||
public @Nullable Ride getRide() {
|
||||
return ride;
|
||||
}
|
||||
|
||||
public ResourceLocation getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
87
src/main/java/com/linearpast/sccore/animation/data/Ride.java
Normal file
87
src/main/java/com/linearpast/sccore/animation/data/Ride.java
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package com.linearpast.sccore.animation.data;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Ride {
|
||||
private final List<ResourceLocation> componentAnimations = new ArrayList<>();
|
||||
private Vec3 offset = Vec3.ZERO;
|
||||
private int existTick;
|
||||
private float xRot;
|
||||
private float yRot;
|
||||
|
||||
|
||||
public static Ride create() {
|
||||
return new Ride();
|
||||
}
|
||||
|
||||
public Ride withOffset(Vec3 offset) {
|
||||
this.offset = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride withExistTick(int existTick) {
|
||||
this.existTick = existTick;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride withXRot(float xRot) {
|
||||
this.xRot = xRot;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride withYRot(float yRot) {
|
||||
this.yRot = yRot;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride setComponentAnimations(List<ResourceLocation> animations) {
|
||||
this.componentAnimations.clear();
|
||||
this.componentAnimations.addAll(animations);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride addComponentAnimation(ResourceLocation animation) {
|
||||
this.componentAnimations.add(animation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getXRot() {
|
||||
return xRot;
|
||||
}
|
||||
|
||||
public void setXRot(float xRot) {
|
||||
this.xRot = xRot;
|
||||
}
|
||||
|
||||
public float getYRot() {
|
||||
return yRot;
|
||||
}
|
||||
|
||||
public void setYRot(float yRot) {
|
||||
this.yRot = yRot;
|
||||
}
|
||||
|
||||
public Vec3 getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(Vec3 offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public int getExistTick() {
|
||||
return existTick;
|
||||
}
|
||||
|
||||
public void setExistTick(int existTick) {
|
||||
this.existTick = existTick;
|
||||
}
|
||||
|
||||
public List<ResourceLocation> getComponentAnimations() {
|
||||
return componentAnimations;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
package com.linearpast.sccore.animation.entity;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import com.linearpast.sccore.animation.data.Ride;
|
||||
import com.linearpast.sccore.animation.registry.AnimationEntities;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
import net.minecraftforge.server.ServerLifecycleHooks;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class AnimationRideEntity extends Entity {
|
||||
public AnimationRideEntity(Level pLevel) {
|
||||
super(AnimationEntities.RIDE.get(), pLevel);
|
||||
this.noPhysics = true;
|
||||
}
|
||||
|
||||
private static final String Animation = "Animation";
|
||||
private static final String PlayerUUID = "PlayerUUID";
|
||||
private static final String Layer = "Layer";
|
||||
|
||||
private final Set<ServerPlayer> players = new HashSet<>();
|
||||
private Animation animation;
|
||||
private ServerPlayer player;
|
||||
private ResourceLocation layer;
|
||||
public AnimationRideEntity(ServerPlayer pPlayer, ResourceLocation layer, Animation animation) {
|
||||
this(pPlayer.level());
|
||||
this.player = pPlayer;
|
||||
this.layer = layer;
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
public ResourceLocation getLayer() {
|
||||
return layer;
|
||||
}
|
||||
|
||||
public Set<ServerPlayer> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public Animation getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData() {}
|
||||
|
||||
@Override
|
||||
protected void readAdditionalSaveData(@NotNull CompoundTag pCompound) {
|
||||
String string = pCompound.getString(Animation);
|
||||
if(!string.isEmpty()) {
|
||||
ResourceLocation rl = new ResourceLocation(string);
|
||||
Animation anim = AnimationUtils.getAnimation(rl);
|
||||
if(anim != null) {
|
||||
this.animation = anim;
|
||||
}
|
||||
}
|
||||
|
||||
if(pCompound.contains(PlayerUUID)) {
|
||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||
if(server != null) {
|
||||
this.player = server.getPlayerList().getPlayer(pCompound.getUUID(PlayerUUID));
|
||||
}
|
||||
}
|
||||
|
||||
if(pCompound.contains(Layer)) {
|
||||
this.layer = new ResourceLocation(pCompound.getString(Layer));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAdditionalSaveData(@NotNull CompoundTag pCompound) {
|
||||
pCompound.putUUID(PlayerUUID, player.getUUID());
|
||||
pCompound.putString(Layer, layer.toString());
|
||||
if(animation != null) {
|
||||
pCompound.putString(Animation, animation.getName().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if(!this.level().isClientSide) {
|
||||
Ride ride = animation == null ? null : animation.getRide();
|
||||
if(!this.getPassengers().contains(player) || (ride != null && ride.getExistTick() > 0 && this.tickCount >= ride.getExistTick())) {
|
||||
if(player != null) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data != null) {
|
||||
AnimationUtils.removeAnimation(player, layer);
|
||||
data.setRideAnimLayer(null);
|
||||
}
|
||||
}
|
||||
if(ride != null) players.forEach(player -> {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
AnimationUtils.removeAnimation(player, layer);
|
||||
if(data != null) data.setRideAnimLayer(null);
|
||||
});
|
||||
this.remove(RemovalReason.DISCARDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPassengersRidingOffset() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canRide(@NotNull Entity entity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRiderSit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Packet<ClientGamePacketListener> getAddEntityPacket(){
|
||||
return NetworkHooks.getEntitySpawningPacket(this);
|
||||
}
|
||||
|
||||
public static boolean create(ServerPlayer pPlayer, ResourceLocation layer, ResourceLocation animation, boolean force) {
|
||||
Animation anim = AnimationUtils.getAnimation(animation);
|
||||
if(anim == null) return false;
|
||||
if(anim.getRide() == null) return false;
|
||||
AnimationRideEntity seat = new AnimationRideEntity(pPlayer, layer, anim);
|
||||
float xRot = anim.getRide().getXRot();
|
||||
float yRot = anim.getRide().getYRot();
|
||||
if(xRot == 0 && yRot == 0) seat.setRot(pPlayer.getXRot(), pPlayer.getYRot());
|
||||
else seat.setRot(yRot, xRot);
|
||||
Vec3 pos = pPlayer.position();
|
||||
pos.add(anim.getRide().getOffset());
|
||||
seat.setPos(pos.x, pos.y + 0.35f, pos.z);
|
||||
pPlayer.level().addFreshEntity(seat);
|
||||
pPlayer.startRiding(seat, force);
|
||||
return AnimationUtils.playAnimation(pPlayer, layer, animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void positionRider(@NotNull Entity pPassenger, @NotNull MoveFunction pCallback) {
|
||||
super.positionRider(pPassenger, pCallback);
|
||||
pPassenger.setYBodyRot(this.getYRot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPassengerTurned(@NotNull Entity pEntityToUpdate) {
|
||||
pEntityToUpdate.setYBodyRot(this.getYRot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Vec3 getDismountLocationForPassenger(@NotNull LivingEntity entity) {
|
||||
Ride ride = animation.getRide();
|
||||
if(ride != null) {
|
||||
Vec3 position = entity.position();
|
||||
return position.subtract(ride.getOffset());
|
||||
}
|
||||
return entity.position();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPassenger(@NotNull Entity entity) {
|
||||
int passengerNum = getPassengers().size();
|
||||
super.addPassenger(entity);
|
||||
if(passengerNum == 0) return;
|
||||
if(entity instanceof ServerPlayer serverPlayer) {
|
||||
Ride ride = animation.getRide();
|
||||
if(ride == null) return;
|
||||
List<ResourceLocation> componentAnimations = ride.getComponentAnimations();
|
||||
if(componentAnimations.isEmpty()) return;
|
||||
if(passengerNum > componentAnimations.size()) return;
|
||||
ResourceLocation location = componentAnimations.get(passengerNum - 1);
|
||||
AnimationUtils.playAnimation(serverPlayer, layer, location);
|
||||
AnimationUtils.syncAnimation(serverPlayer, player, layer);
|
||||
players.add(serverPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removePassenger(@NotNull Entity entity) {
|
||||
super.removePassenger(entity);
|
||||
if(entity instanceof ServerPlayer serverPlayer) {
|
||||
AnimationUtils.removeAnimation(serverPlayer, layer);
|
||||
AnimationDataCapability.getCapability(serverPlayer).ifPresent(data -> data.setRideAnimLayer(null));
|
||||
players.remove(serverPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canAddPassenger(@NotNull Entity pPassenger) {
|
||||
boolean isServerPlayer = pPassenger instanceof ServerPlayer;
|
||||
int size = players.size();
|
||||
Ride ride = animation.getRide();
|
||||
if(ride == null) return false;
|
||||
int maxSize = ride.getComponentAnimations().size();
|
||||
return isServerPlayer && size < maxSize;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.linearpast.sccore.animation.entity.renderer;
|
||||
|
||||
import com.linearpast.sccore.animation.entity.AnimationRideEntity;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class AnimationRideRenderer extends EntityRenderer<AnimationRideEntity> {
|
||||
public AnimationRideRenderer(EntityRendererProvider.Context pContext) {
|
||||
super(pContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResourceLocation getTextureLocation(@NotNull AnimationRideEntity pEntity) {
|
||||
return new ResourceLocation("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(@NotNull AnimationRideEntity pLivingEntity, @NotNull Frustum pCamera, double pCamX, double pCamY, double pCamZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(@NotNull AnimationRideEntity pEntity, float pEntityYaw, float pPartialTick, @NotNull PoseStack pPoseStack, @NotNull MultiBufferSource pBuffer, int pPackedLight) {}
|
||||
|
||||
@Override
|
||||
protected void renderNameTag(@NotNull AnimationRideEntity pEntity, @NotNull Component pDisplayName, @NotNull PoseStack pPoseStack, @NotNull MultiBufferSource pBuffer, int pPackedLight) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.linearpast.sccore.animation.event;
|
||||
|
||||
import com.linearpast.sccore.animation.event.create.AnimationLayerRegisterEvent;
|
||||
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
||||
import dev.kosmx.playerAnim.api.layered.ModifierLayer;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationFactory;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AnimationLayerRegistry {
|
||||
private static final Map<ResourceLocation, Integer> animLayers = new HashMap<>();
|
||||
|
||||
public static void onClientSetup(FMLClientSetupEvent event) {
|
||||
onCommonSetUp(null);
|
||||
event.enqueueWork(() -> animLayers.forEach((location, integer) ->
|
||||
PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory(location, integer,
|
||||
AnimationLayerRegistry::registerPlayerAnimation
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public static void onCommonSetUp(FMLCommonSetupEvent event) {
|
||||
AnimationLayerRegisterEvent layerEvent = new AnimationLayerRegisterEvent();
|
||||
ModLoader.get().postEvent(layerEvent);
|
||||
animLayers.putAll(layerEvent.getLayers());
|
||||
}
|
||||
|
||||
public static void registerPlayerAnimation(ResourceLocation location, int priority) {
|
||||
animLayers.put(location, priority);
|
||||
}
|
||||
|
||||
public static Map<ResourceLocation, Integer> getAnimLayers() {
|
||||
return animLayers;
|
||||
}
|
||||
|
||||
public static boolean isLayerPresent(ResourceLocation layer) {
|
||||
return animLayers.containsKey(layer);
|
||||
}
|
||||
|
||||
private static IAnimation registerPlayerAnimation(AbstractClientPlayer player) {
|
||||
return new ModifierLayer<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.linearpast.sccore.animation.event;
|
||||
|
||||
import com.linearpast.sccore.animation.entity.renderer.AnimationRideRenderer;
|
||||
import com.linearpast.sccore.animation.registry.AnimationEntities;
|
||||
import net.minecraftforge.client.event.EntityRenderersEvent;
|
||||
|
||||
public class EntityRendererRegistry {
|
||||
public static void registerEntityRenderer(EntityRenderersEvent.RegisterRenderers event) {
|
||||
event.registerEntityRenderer(AnimationEntities.RIDE.get(), AnimationRideRenderer::new);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.linearpast.sccore.animation.event.client;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import dev.kosmx.playerAnim.core.util.MathHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.client.event.ViewportEvent;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class CameraAnglesModify {
|
||||
private static float targetYaw = 0.0F;
|
||||
private static float targetPitch = 0.0F;
|
||||
private static float targetRoll = 0.0F;
|
||||
private static float currentYaw = 0.0F;
|
||||
private static float currentPitch = 0.0F;
|
||||
private static float currentRoll = 0.0F;
|
||||
|
||||
public static void changeCameraView(ViewportEvent.ComputeCameraAngles event){
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
|
||||
if (minecraft.options.getCameraType().isFirstPerson()) {
|
||||
LocalPlayer player = minecraft.player;
|
||||
if (player != null) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return;
|
||||
Animation animation = null;
|
||||
try {
|
||||
animation = data.getAnimations().values().stream()
|
||||
.map(AnimationUtils::getAnimation)
|
||||
.min(Comparator.comparingDouble(anim -> {
|
||||
if (anim == null) return 1.0f;
|
||||
return anim.getHeightModifier();
|
||||
})).orElse(null);
|
||||
}catch (Exception ignored){}
|
||||
|
||||
|
||||
if(animation != null) {
|
||||
targetPitch = animation.getCamPitch();
|
||||
targetYaw = animation.getCamYaw();
|
||||
targetRoll = animation.getCamRoll();
|
||||
} else {
|
||||
targetYaw = 0.0F;
|
||||
targetPitch = 0.0F;
|
||||
targetRoll = 0.0F;
|
||||
}
|
||||
}
|
||||
float var3 = Minecraft.getInstance().getDeltaFrameTime();
|
||||
float var4 = var3 / 5.0F;
|
||||
if (var4 == 0.0F) {
|
||||
var4 = 0.0022857143F;
|
||||
}
|
||||
|
||||
currentPitch = MathHelper.lerp(var4, currentPitch, targetPitch);
|
||||
currentYaw = MathHelper.lerp(var4, currentYaw, targetYaw);
|
||||
currentRoll = MathHelper.lerp(var4, currentRoll, targetRoll);
|
||||
event.setPitch(event.getPitch() + currentPitch);
|
||||
event.setYaw(event.getYaw() + currentYaw);
|
||||
event.setRoll(event.getRoll() + currentRoll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.linearpast.sccore.animation.event.client;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class ClientPlayerTick {
|
||||
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
|
||||
if (event.side.isClient() && event.phase == TickEvent.Phase.START) {
|
||||
Player player = event.player;
|
||||
if(player.tickCount % 10 != 0) return;
|
||||
if (!(player instanceof AbstractClientPlayer clientPlayer)) return;
|
||||
AnimationUtils.refreshAnimation(clientPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<Runnable, Map.Entry<Integer, Integer>> runs = new HashMap<>();
|
||||
public static void delayRuns(TickEvent.PlayerTickEvent event) {
|
||||
if (event.side.isClient() && event.phase == TickEvent.Phase.END) {
|
||||
Map.copyOf(runs).forEach((runnable, countMap) -> {
|
||||
if(countMap.getValue() >= countMap.getKey()) {
|
||||
runnable.run();
|
||||
runs.remove(runnable);
|
||||
} else {
|
||||
countMap.setValue(countMap.getValue() + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.linearpast.sccore.animation.event.create;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* You can listen this event to register an animation layer <br>
|
||||
* Generally, the static function is better.
|
||||
* @see com.linearpast.sccore.animation.AnimationUtils#registerAnimationLayer
|
||||
*/
|
||||
public class AnimationLayerRegisterEvent extends Event implements IModBusEvent {
|
||||
private final Map<ResourceLocation, Integer> layers = new HashMap<>();
|
||||
|
||||
public AnimationLayerRegisterEvent() {
|
||||
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, Integer> getLayers() {
|
||||
return layers;
|
||||
}
|
||||
|
||||
public void putLayer(ResourceLocation key, Integer value) {
|
||||
layers.put(key, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.linearpast.sccore.animation.mixin;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public interface IMixinKeyframeAnimationPlayer {
|
||||
void sccore$setCurrentTick(int tick);
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.linearpast.sccore.animation.network.toclient;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationPlayer;
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.capability.data.player.SimplePlayerCapabilitySync;
|
||||
import com.linearpast.sccore.capability.network.SimpleCapabilityPacket;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class AnimationCapabilityPacket extends SimpleCapabilityPacket<Player> {
|
||||
public AnimationCapabilityPacket(CompoundTag data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public AnimationCapabilityPacket(FriendlyByteBuf buf) {
|
||||
super(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handler(NetworkEvent.Context context) {
|
||||
context.setPacketHandled(true);
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
ClientLevel level = instance.level;
|
||||
if (level == null) return;
|
||||
CompoundTag nbt = getData();
|
||||
Player player = level.getPlayerByUUID(nbt.getUUID(SimplePlayerCapabilitySync.OwnerUUID));
|
||||
try {
|
||||
IAnimationCapability data = getCapability(player);
|
||||
testPlayAnimations((AbstractClientPlayer) player, nbt, data);
|
||||
syncData(nbt, data);
|
||||
}catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IAnimationCapability getCapability(Player player) {
|
||||
return AnimationDataCapability.getCapability(player).orElse(null);
|
||||
}
|
||||
|
||||
private void testPlayAnimations(AbstractClientPlayer player, CompoundTag tag, IAnimationCapability data) {
|
||||
Set<ResourceLocation> oldLayerSet = new HashSet<>();
|
||||
if(data != null) oldLayerSet.addAll(data.getAnimations().keySet());
|
||||
CompoundTag animMap = tag.getCompound(AnimationDataCapability.AnimMap);
|
||||
for (String newLayerString : animMap.getAllKeys()) {
|
||||
ResourceLocation newLayerLocation = new ResourceLocation(newLayerString);
|
||||
String newAnimString = animMap.getString(newLayerString);
|
||||
ResourceLocation newAnimLocation = newAnimString.isEmpty() ? null : new ResourceLocation(newAnimString);
|
||||
ResourceLocation oldAnimLocation = data == null ? null : data.getAnimation(newLayerLocation);
|
||||
if (!Objects.equals(newAnimLocation, oldAnimLocation)) {
|
||||
AnimationPlayer.playAnimation(player, newLayerLocation, newAnimLocation);
|
||||
}
|
||||
oldLayerSet.remove(newLayerLocation);
|
||||
}
|
||||
for (ResourceLocation oldLayerLocation : oldLayerSet) {
|
||||
AnimationPlayer.playAnimation(player, oldLayerLocation, null);
|
||||
}
|
||||
|
||||
if(!tag.contains(AnimationDataCapability.RideAnimLayer)) {
|
||||
if(data != null && data.getRideAnimLayer() != null) {
|
||||
AnimationUtils.playAnimation(null, data.getRideAnimLayer(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.linearpast.sccore.animation.network.toclient;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationPlayer;
|
||||
import com.linearpast.sccore.animation.event.client.ClientPlayerTick;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SyncAnimationPacket {
|
||||
private final UUID playerUUID;
|
||||
private final UUID targetUUID;
|
||||
private final ResourceLocation layer;
|
||||
public SyncAnimationPacket(UUID playerUUID, UUID targetUUID, ResourceLocation layer) {
|
||||
this.playerUUID = playerUUID;
|
||||
this.targetUUID = targetUUID;
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
public SyncAnimationPacket(FriendlyByteBuf buf) {
|
||||
this.playerUUID = buf.readUUID();
|
||||
this.targetUUID = buf.readUUID();
|
||||
this.layer = buf.readResourceLocation();
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(this.playerUUID);
|
||||
buf.writeUUID(this.targetUUID);
|
||||
buf.writeResourceLocation(this.layer);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
ClientLevel level = instance.level;
|
||||
if(level == null) return;
|
||||
AbstractClientPlayer player = (AbstractClientPlayer) level.getPlayerByUUID(playerUUID);
|
||||
AbstractClientPlayer target = (AbstractClientPlayer) level.getPlayerByUUID(targetUUID);
|
||||
ClientPlayerTick.runs.put(
|
||||
() -> AnimationPlayer.syncAnimation(player, target, layer),
|
||||
new AbstractMap.SimpleEntry<>(5, 0)
|
||||
);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.linearpast.sccore.animation.network.toserver;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationPlayer;
|
||||
import com.linearpast.sccore.animation.event.AnimationLayerRegistry;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PlayAnimationRequestPacket {
|
||||
private final ResourceLocation layer;
|
||||
private @Nullable ResourceLocation animation;
|
||||
public PlayAnimationRequestPacket(ResourceLocation layer, @Nullable ResourceLocation animation) {
|
||||
this.layer = layer;
|
||||
this.animation = animation;
|
||||
}
|
||||
public PlayAnimationRequestPacket(FriendlyByteBuf buf){
|
||||
this.layer = buf.readResourceLocation();
|
||||
try {
|
||||
this.animation = buf.readResourceLocation();
|
||||
} catch (Exception e) {
|
||||
this.animation = null;
|
||||
}
|
||||
}
|
||||
public void encode(FriendlyByteBuf buf){
|
||||
buf.writeResourceLocation(layer);
|
||||
if(animation != null){
|
||||
buf.writeResourceLocation(animation);
|
||||
}
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier){
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
if (AnimationLayerRegistry.getAnimLayers().containsKey(layer)) {
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null) return;
|
||||
AnimationPlayer.serverPlayAnimation(sender, layer, animation);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.linearpast.sccore.animation.network.toserver;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationPlayer;
|
||||
import com.linearpast.sccore.animation.event.AnimationLayerRegistry;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PlayAnimationRidePacket {
|
||||
private final ResourceLocation layer;
|
||||
private @Nullable ResourceLocation animation;
|
||||
private final boolean force;
|
||||
public PlayAnimationRidePacket(ResourceLocation layer, @Nullable ResourceLocation animation, boolean force) {
|
||||
this.layer = layer;
|
||||
this.animation = animation;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
public PlayAnimationRidePacket(FriendlyByteBuf buf) {
|
||||
this.layer = buf.readResourceLocation();
|
||||
this.force = buf.readBoolean();
|
||||
try {
|
||||
this.animation = buf.readResourceLocation();
|
||||
} catch (Exception e) {
|
||||
this.animation = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeResourceLocation(layer);
|
||||
buf.writeBoolean(force);
|
||||
if(animation != null) {
|
||||
buf.writeResourceLocation(animation);
|
||||
}
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
if (AnimationLayerRegistry.getAnimLayers().containsKey(layer)) {
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null) return;
|
||||
AnimationPlayer.playAnimationWithRide(sender, layer, animation, force);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.linearpast.sccore.animation.network.toserver;
|
||||
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class RefreshAnimationPacket {
|
||||
private final Set<ResourceLocation> needRemoves;
|
||||
public RefreshAnimationPacket(Set<ResourceLocation> needRemoves) {
|
||||
this.needRemoves = needRemoves;
|
||||
}
|
||||
public RefreshAnimationPacket(FriendlyByteBuf buf) {
|
||||
int i = buf.readInt();
|
||||
needRemoves = new HashSet<>();
|
||||
for (int j = 0; j < i; ++j) {
|
||||
needRemoves.add(buf.readResourceLocation());
|
||||
}
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeInt(needRemoves.size());
|
||||
for (ResourceLocation needRemove : needRemoves) {
|
||||
buf.writeResourceLocation(needRemove);
|
||||
}
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null) return;
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(sender).orElse(null);
|
||||
if(data == null) return;
|
||||
needRemoves.forEach(data::removeAnimation);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.linearpast.sccore.animation.registry;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.entity.AnimationRideEntity;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
|
||||
|
||||
|
||||
public class AnimationEntities {
|
||||
public static final DeferredRegister<EntityType<?>> REGISTER = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, SnowyCrescentCore.MODID);
|
||||
|
||||
public static final RegistryObject<EntityType<AnimationRideEntity>> RIDE = register(
|
||||
"animation_ride_entity", EntityType.Builder.<AnimationRideEntity>of((type, world) -> new AnimationRideEntity(world), MobCategory.MISC)
|
||||
.sized(0.0F, 0.0F)
|
||||
.setCustomClientFactory((spawnEntity, world) ->
|
||||
new AnimationRideEntity(world)
|
||||
)
|
||||
);
|
||||
|
||||
private static <T extends Entity> RegistryObject<EntityType<T>> register(String name, EntityType.Builder<T> builder) {
|
||||
return REGISTER.register(name, () -> builder.build(name));
|
||||
}
|
||||
|
||||
public static void register(IEventBus modBus){
|
||||
REGISTER.register(modBus);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.linearpast.sccore.animation.registry;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import com.linearpast.sccore.animation.network.toclient.AnimationCapabilityPacket;
|
||||
import com.linearpast.sccore.animation.network.toclient.SyncAnimationPacket;
|
||||
import com.linearpast.sccore.animation.network.toserver.PlayAnimationRequestPacket;
|
||||
import com.linearpast.sccore.animation.network.toserver.PlayAnimationRidePacket;
|
||||
import com.linearpast.sccore.animation.network.toserver.RefreshAnimationPacket;
|
||||
import com.linearpast.sccore.capability.CapabilityUtils;
|
||||
import com.linearpast.sccore.capability.data.player.PlayerCapabilityRegistry;
|
||||
import com.linearpast.sccore.capability.network.CapabilityChannel;
|
||||
import com.linearpast.sccore.core.ModChannel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.network.NetworkDirection;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AnimationRegistry {
|
||||
private static final Map<ResourceLocation, Animation> animations = new HashMap<>();
|
||||
|
||||
public static Map<ResourceLocation, Animation> getAnimations() {
|
||||
return animations;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Animation getAnimation(ResourceLocation location) {
|
||||
return animations.get(location);
|
||||
}
|
||||
|
||||
public static void registerAnimation(ResourceLocation location, Animation animation) {
|
||||
animations.put(location, animation);
|
||||
}
|
||||
|
||||
public static boolean isAnimationPresent(ResourceLocation location) {
|
||||
return animations.containsKey(location);
|
||||
}
|
||||
|
||||
public static void addAnimationListener(IEventBus forgeBus, IEventBus modBus) {
|
||||
AnimationUtils.ANIMATION_RUNNER.testLoadedAndAddListener(forgeBus, modBus);
|
||||
}
|
||||
|
||||
private static void registerAnimationCapability() {
|
||||
CapabilityChannel channel = CapabilityUtils.createChannel();
|
||||
CapabilityUtils.registerPlayerCapabilityWithNetwork(
|
||||
AnimationDataCapability.key,
|
||||
new PlayerCapabilityRegistry.CapabilityRecord<>(
|
||||
AnimationDataCapability.class,
|
||||
CapabilityManager.get(new CapabilityToken<>() {}),
|
||||
IAnimationCapability.class
|
||||
),
|
||||
channel,
|
||||
ModChannel.getAndAddCid(),
|
||||
AnimationCapabilityPacket.class,
|
||||
AnimationCapabilityPacket::new,
|
||||
AnimationCapabilityPacket::encode,
|
||||
AnimationCapabilityPacket::handle
|
||||
);
|
||||
}
|
||||
|
||||
private static void registerChannel() {
|
||||
ModChannel.INSTANCE.messageBuilder(SyncAnimationPacket.class, ModChannel.getAndAddCid(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.decoder(SyncAnimationPacket::new)
|
||||
.encoder(SyncAnimationPacket::encode)
|
||||
.consumerMainThread(SyncAnimationPacket::handle)
|
||||
.add();
|
||||
|
||||
ModChannel.INSTANCE.messageBuilder(PlayAnimationRequestPacket.class, ModChannel.getAndAddCid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(PlayAnimationRequestPacket::new)
|
||||
.encoder(PlayAnimationRequestPacket::encode)
|
||||
.consumerMainThread(PlayAnimationRequestPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(PlayAnimationRidePacket.class, ModChannel.getAndAddCid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(PlayAnimationRidePacket::new)
|
||||
.encoder(PlayAnimationRidePacket::encode)
|
||||
.consumerMainThread(PlayAnimationRidePacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(RefreshAnimationPacket.class, ModChannel.getAndAddCid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(RefreshAnimationPacket::new)
|
||||
.encoder(RefreshAnimationPacket::encode)
|
||||
.consumerMainThread(RefreshAnimationPacket::handle)
|
||||
.add();
|
||||
}
|
||||
|
||||
public static void register(){
|
||||
AnimationUtils.ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
registerAnimationCapability();
|
||||
registerChannel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import com.linearpast.sccore.capability.data.player.PlayerCapabilityHandler;
|
|||
import com.linearpast.sccore.capability.data.player.PlayerCapabilityRegistry;
|
||||
import com.linearpast.sccore.capability.network.CapabilityChannel;
|
||||
import com.linearpast.sccore.capability.network.ICapabilityPacket;
|
||||
import com.linearpast.sccore.network.Channel;
|
||||
import com.linearpast.sccore.core.ModChannel;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
|
@ -24,15 +24,16 @@ import java.util.function.Supplier;
|
|||
public class CapabilityUtils {
|
||||
|
||||
/**
|
||||
* 同时注册玩家capability和对应的网络包
|
||||
* @param key capability的唯一name
|
||||
* @param capabilityRecord capability的注册数据
|
||||
* @param channelRegister 应该提前创建好实例传入,参阅{@link CapabilityUtils#createChannel}
|
||||
* @param cid 网络频道索引
|
||||
* @param clazz 网络包的类
|
||||
* @param decoder 网络包的decode
|
||||
* @param encoder 网络包的encode
|
||||
* @param handler 网络包的handle
|
||||
* Simultaneously register player capability and corresponding network packets
|
||||
* @param key The unique name of capability
|
||||
* @param capabilityRecord Registration data for capability
|
||||
* @param channelRegister You should create an instance in advance to pass in, see: {@link CapabilityUtils#createChannel}
|
||||
* @param cid Network Channel Index
|
||||
* @param clazz Class of network packets
|
||||
* @param decoder Decoding of network packets
|
||||
* @param encoder Encoding of network packets
|
||||
* @param handler Handle of network packets
|
||||
* @param <T> extend {@code ICapabilityPacket<?>}
|
||||
*/
|
||||
public static <T extends ICapabilityPacket<?>> void registerPlayerCapabilityWithNetwork(
|
||||
ResourceLocation key, PlayerCapabilityRegistry.CapabilityRecord<? extends ICapabilitySync<Player>> capabilityRecord,
|
||||
|
|
@ -48,15 +49,16 @@ public class CapabilityUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 同时注册实体capability和对应的网络包
|
||||
* @param key capability的唯一name
|
||||
* @param capabilityRecord capability的注册数据
|
||||
* @param channelRegister 应该提前创建好实例传入,参阅{@link CapabilityUtils#createChannel}
|
||||
* @param cid 网络频道索引
|
||||
* @param clazz 网络包的类
|
||||
* @param decoder 网络包的decode
|
||||
* @param encoder 网络包的encode
|
||||
* @param handler 网络包的handle
|
||||
* Simultaneously register entity capability and corresponding network packets
|
||||
* @param key The unique name of capability
|
||||
* @param capabilityRecord Registration data for capability
|
||||
* @param channelRegister You should create an instance in advance to pass in, see: {@link CapabilityUtils#createChannel}
|
||||
* @param cid Network Channel Index
|
||||
* @param clazz Class of network packets
|
||||
* @param decoder Decoding of network packets
|
||||
* @param encoder Encoding of network packets
|
||||
* @param handler Handle of network packets
|
||||
* @param <T> {@code ICapabilityPacket<?>}
|
||||
*/
|
||||
public static <T extends ICapabilityPacket<?>> void registerEntityCapabilityWithNetwork(
|
||||
ResourceLocation key, EntityCapabilityRegistry.CapabilityRecord<? extends ICapabilitySync<? extends Entity>> capabilityRecord,
|
||||
|
|
@ -72,46 +74,47 @@ public class CapabilityUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 通过此方法注册玩家capability,仅当 {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}
|
||||
* 事件结束之前有效
|
||||
* @param key capability的唯一name
|
||||
* @param capabilityRecord 使用record存储了应该注册的capability的各项数据,参阅:{@link PlayerCapabilityRegistry.CapabilityRecord}
|
||||
* See {@link PlayerCapabilityRegistry#registerCapability(ResourceLocation, PlayerCapabilityRegistry.CapabilityRecord)}
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link PlayerCapabilityRegistry.CapabilityRecord}
|
||||
* @param <T> extends {@code ICapabilitySync<Player>}
|
||||
*/
|
||||
public static <T extends ICapabilitySync<Player>> void registerPlayerCapability(ResourceLocation key, PlayerCapabilityRegistry.CapabilityRecord<T> capabilityRecord){
|
||||
PlayerCapabilityRegistry.registerCapability(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过此方法注册实体capability,仅当 {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}
|
||||
* 事件结束之前有效
|
||||
* @param key capability的唯一name
|
||||
* @param capabilityRecord 使用record存储了应该注册的capability的各项数据,参阅:{@link PlayerCapabilityRegistry.CapabilityRecord}
|
||||
* See {@link EntityCapabilityRegistry#registerCapability(ResourceLocation, EntityCapabilityRegistry.CapabilityRecord)}
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link EntityCapabilityRegistry.CapabilityRecord}
|
||||
* @param <T> extends {@code ICapabilitySync<Entity>}
|
||||
*/
|
||||
public static <T extends ICapabilitySync<Entity>> void registerEntityCapability(ResourceLocation key, EntityCapabilityRegistry.CapabilityRecord<T> capabilityRecord){
|
||||
EntityCapabilityRegistry.registerCapability(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过这个方法返回一个新的PlayerCapabilityChannel实例,一般只有注册不同channel的网络包才会使用
|
||||
* @param channel 你自己模组的channel
|
||||
* @return 新的实例
|
||||
* Return a new PlayerCapabilityChannel instance through this method<br>
|
||||
* Generally, only network packets registered on different channels will be used
|
||||
* @param channel Your own mod channel
|
||||
* @return newInstances
|
||||
*/
|
||||
public static CapabilityChannel createChannel(SimpleChannel channel) {
|
||||
return new CapabilityChannel(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过这个方法返回本模组的Channel的PlayerCapabilityChannel实例
|
||||
* @return 新的实例
|
||||
* Return the PlayerCapabilityChannel instance of the Channel in SCCore through this method
|
||||
* @return newInstances
|
||||
*/
|
||||
public static CapabilityChannel createChannel() {
|
||||
return new CapabilityChannel(Channel.INSTANCE);
|
||||
return new CapabilityChannel(ModChannel.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过此方法监听capability事件,从而启用所有功能<br>
|
||||
* 重复调用不会发生任何事
|
||||
* @param forgeBus forge事件总线
|
||||
* By using this method to listen for capability events, all functions will be enabled<br>
|
||||
* Repeated calls will not cause anything
|
||||
* @param forgeBus Forge event bus
|
||||
*/
|
||||
public static void registerHandler(IEventBus forgeBus){
|
||||
PlayerCapabilityHandler.register(forgeBus);
|
||||
|
|
@ -119,11 +122,13 @@ public class CapabilityUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 请通过该方法获取capability
|
||||
* @param entity 目标实体,类型 {@code <E extends Entity>}
|
||||
* @param key capability的唯一名
|
||||
* @param clazz 应返回的capability类型,若为null则会返回 {@code ICapabilitySync<E>}
|
||||
* @return 返回对应的capability
|
||||
* Please obtain capability through this method
|
||||
* @param entity Target entity, type: {@code <E extends Entity>}
|
||||
* @param key The unique name of capability
|
||||
* @param clazz The capability type that should be returned. If it is null, will return: {@code ICapabilitySync<E>}
|
||||
* @param <E> extend {@code Entity}
|
||||
* @param <T> extend {@code ICapabilitySync<E>}
|
||||
* @return Return the corresponding capability
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
|
|
@ -142,11 +147,13 @@ public class CapabilityUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 请通过该方法获取capability
|
||||
* @param entity 目标实体,类型 {@code <E extends Entity>}
|
||||
* @param key capability的唯一名
|
||||
* @param clazz 应返回的capability类型,若为null则会返回 {@code ICapabilitySync<E>}
|
||||
* @return 返回对应的capability
|
||||
* Please obtain capability through this method
|
||||
* @param entity Target entity, type: {@code <E extends Player>}
|
||||
* @param key The unique name of capability
|
||||
* @param clazz The capability type that should be returned. If it is null, will return: {@code ICapabilitySync<E>}
|
||||
* @param <E> extend {@code Player}
|
||||
* @param <T> extend {@code ICapabilitySync<E>}
|
||||
* @return Return the corresponding capability
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
|
|
@ -165,10 +172,10 @@ public class CapabilityUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取一个未转换类型的Cap
|
||||
* @param entity 目标
|
||||
* @param key cap的唯一名
|
||||
* @return 未转换类型的cap
|
||||
* Get a capability of an unconverted type
|
||||
* @param entity Target
|
||||
* @param key The unique name of capability
|
||||
* @return An unconverted capability
|
||||
*/
|
||||
@Nullable
|
||||
public static ICapabilitySync<?> getCapability(Entity entity, ResourceLocation key) {
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
package com.linearpast.sccore.capability.data;
|
||||
|
||||
import com.linearpast.sccore.capability.network.SimpleCapabilityPacket;
|
||||
import com.linearpast.sccore.network.Channel;
|
||||
import com.linearpast.sccore.core.ModChannel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
|
||||
public interface ICapabilitySync<T extends Entity> extends INBTSerializable<CompoundTag> {
|
||||
/**
|
||||
* You should call it to set it to true in the setter of each property to trigger automatic synchronization
|
||||
* @param dirty dirty
|
||||
*/
|
||||
void setDirty(boolean dirty);
|
||||
|
||||
boolean isDirty();
|
||||
|
||||
CompoundTag toTag(CompoundTag tag);
|
||||
void fromTag(CompoundTag tag);
|
||||
|
||||
/**
|
||||
* 该方法重写时应该在最后调用super方法
|
||||
* @param oldData 旧数据
|
||||
* @param listenDone 最后是否执行完成方法 {@link ICapabilitySync#onCopyDone()}
|
||||
* When this method is overridden, the super method should be called at the end
|
||||
* @param oldData old data
|
||||
* @param listenDone Whether to execute the completion method at the end: {@link ICapabilitySync#onCopyDone()}
|
||||
*/
|
||||
default void copyFrom(ICapabilitySync<?> oldData, boolean listenDone) {
|
||||
this.setDirty(oldData.isDirty());
|
||||
|
|
@ -25,40 +27,40 @@ public interface ICapabilitySync<T extends Entity> extends INBTSerializable<Comp
|
|||
}
|
||||
|
||||
/**
|
||||
* 当copy结束之后,如果某些值需要被重定义,你应该重写这个方法 <br>
|
||||
* 多用于玩家 跨越维度/死亡 时重置数据
|
||||
* After the copy is completed, if certain values need to be redefined, you should override this method <br>
|
||||
* Commonly used for resetting data when players cross dimensions or die
|
||||
*/
|
||||
default void onCopyDone(){}
|
||||
|
||||
/**
|
||||
* 一般情况下建议重写,否则会以sccore的Channel实例发送<br>
|
||||
* 服务端给全体玩家发送客户端同步数据
|
||||
* In general, it is recommended to rewrite it, otherwise it will be sent as a Channel instance of SCCore<br>
|
||||
* The server sends client synchronized data to all players
|
||||
*/
|
||||
default void sendToClient(){
|
||||
Channel.sendAllPlayer(getDefaultPacket());
|
||||
ModChannel.sendAllPlayer(getDefaultPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
* 一般情况下建议重写,否则会以sccore的Channel实例发送<br>
|
||||
* 服务端给单个玩家发送客户端同步数据
|
||||
* @param player 发送给的目标玩家
|
||||
* In general, it is recommended to rewrite it, otherwise it will be sent as a Channel instance of SCCore<br>
|
||||
* The server sends client synchronized data to a single player
|
||||
* @param player Target player
|
||||
*/
|
||||
default void sendToClient(ServerPlayer player){
|
||||
Channel.sendToPlayer(getDefaultPacket(), player);
|
||||
ModChannel.sendToPlayer(getDefaultPacket(), player);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写该方法为你的Capability设定一个网络包类,目前仅有客户端 <br>
|
||||
* 当调用sendToClient方法时会从这里获取网络包直接发送 <br>
|
||||
* 一般情况下,你应该extends SimpleCapabilityPacket然后重写该方法返回你的子类
|
||||
* @return 网络包类SimpleCapabilityPacket
|
||||
* Rewrite this method to set a network packet class for your Capability <br>
|
||||
* When calling the sendToClient method, network packets will be obtained from here and sent directly <br>
|
||||
* In general, you should extend SimpleCapacityPackage and then override the method to return your subclass
|
||||
* @return SimpleCapacityPacket, a network packet class
|
||||
*/
|
||||
SimpleCapabilityPacket<T> getDefaultPacket();
|
||||
|
||||
/**
|
||||
* 当玩家登录 / 实体加入世界时的cao初始化时会调用 <br>
|
||||
* 必须实现,但可为空方法
|
||||
* @param entity 目标
|
||||
* When players log in or entity join the world, the capability initialization will be called <br>
|
||||
* Must be implemented, but can be an empty method
|
||||
* @param entity Target
|
||||
*/
|
||||
void attachInit(T entity);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ public class EntityCapabilityHandler {
|
|||
private static boolean isRegistered = false;
|
||||
|
||||
/**
|
||||
* 应在Forge主线中调用以监听capability注册 <br>
|
||||
* 建议在Mod构造方法里调用
|
||||
* It should be called in the Forge mainline to listen to the capability registration <br>
|
||||
* Suggest calling it in the Mod constructor method <br>
|
||||
* Normally SCCore will call it, so you should not call it
|
||||
* @param forgeBus forge event bus
|
||||
*/
|
||||
public static void register(IEventBus forgeBus) {
|
||||
if (isRegistered) return;
|
||||
|
|
@ -30,7 +32,10 @@ public class EntityCapabilityHandler {
|
|||
isRegistered = true;
|
||||
}
|
||||
|
||||
//注册 capability
|
||||
/**
|
||||
* Register capability
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void registerCapability(RegisterCapabilitiesEvent event) {
|
||||
EntityCapabilityRegistry.getCapabilityMap().values().forEach(record ->
|
||||
|
|
@ -38,20 +43,24 @@ public class EntityCapabilityHandler {
|
|||
);
|
||||
}
|
||||
|
||||
//附加 capability
|
||||
/**
|
||||
* Attach capability to entity
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void attachCapability(AttachCapabilitiesEvent<?> event) {
|
||||
if(event.getObject() instanceof Entity entity) {
|
||||
EntityCapabilityRegistry.getCapabilityMap().forEach((key, record) -> {
|
||||
if(record.target().isInstance(entity)) {
|
||||
try {
|
||||
ICapabilitySync<?> capabilitySync = (ICapabilitySync<?>) record.aClass().getDeclaredConstructor().newInstance();
|
||||
event.addCapability(key, new EntityCapabilityProvider<>(key, capabilitySync));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to instantiate capability sync class {}. Your capability register is wrong.", record.aClass(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
EntityCapabilityRegistry.getCapabilityMap().forEach((key, record) ->
|
||||
record.targets().forEach(target -> {
|
||||
if(target.isInstance(entity)) {
|
||||
try {
|
||||
ICapabilitySync<?> capabilitySync = (ICapabilitySync<?>) record.aClass().getDeclaredConstructor().newInstance();
|
||||
event.addCapability(key, new EntityCapabilityProvider<>(key, capabilitySync));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to instantiate capability sync class {}. Your capability register is wrong.", record.aClass(), e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,19 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* cap的最终 序列化、反序列化、获取方法
|
||||
* @param <C> 继承 {@link ICapabilitySync}
|
||||
* The final serialization, deserialization, and retrieval methods of capability
|
||||
* @param <C> extends {@link ICapabilitySync}
|
||||
*/
|
||||
@AutoRegisterCapability
|
||||
public class EntityCapabilityProvider<C extends ICapabilitySync<? extends Entity>> implements ICapabilitySerializable<CompoundTag> {
|
||||
private final C instance;
|
||||
private final ResourceLocation resourceLocation;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param resourceLocation key
|
||||
* @param instance instance
|
||||
*/
|
||||
public EntityCapabilityProvider(ResourceLocation resourceLocation, C instance) {
|
||||
this.resourceLocation = resourceLocation;
|
||||
this.instance = instance;
|
||||
|
|
|
|||
|
|
@ -7,46 +7,47 @@ import net.minecraftforge.common.capabilities.Capability;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class EntityCapabilityRegistry {
|
||||
public static final EntityCapabilityRegistry CAPABILITIES = new EntityCapabilityRegistry();
|
||||
private final Map<ResourceLocation, CapabilityRecord<?>> capabilityRecordMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 通过此方法注册capability,仅当 {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}
|
||||
* 事件结束之前有效
|
||||
* @param key capability的唯一name
|
||||
* @param capabilityRecord 使用record存储了应该注册的capability的各项数据,参阅:{@link CapabilityRecord}
|
||||
* Registering entity capabilities through this method only applies to {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}<br>
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link EntityCapabilityRegistry.CapabilityRecord}
|
||||
*/
|
||||
public static void registerCapability(ResourceLocation key, CapabilityRecord<?> capabilityRecord) {
|
||||
CAPABILITIES.capabilityRecordMap.put(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过此方法获取对应的capability数据
|
||||
* @param key 根据key获取
|
||||
* @return capability 数据
|
||||
* Obtain corresponding capability data through this method
|
||||
* @param key Obtain based on key
|
||||
* @return capability
|
||||
*/
|
||||
public static CapabilityRecord<?> getCapabilityRecord(ResourceLocation key){
|
||||
return CAPABILITIES.capabilityRecordMap.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有key对应的cap数据集
|
||||
* @return map
|
||||
*/
|
||||
public static Map<ResourceLocation, CapabilityRecord<?>> getCapabilityMap(){
|
||||
return CAPABILITIES.capabilityRecordMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录capability的注册数据
|
||||
* @param aClass 最终会附加给实体的实例的类,应该是实现了clazz的类
|
||||
* @param capability 注册时一般默认{@code CapabilityManager.get(new CapabilityToken<>(){})}即可
|
||||
* @param interfaceClass instance类对应的实例对应的接口类,比如ICapabilitySync.class
|
||||
* @param target capability附加的目标类型
|
||||
* Record the registration data of capability
|
||||
* @param aClass The instance that will ultimately be attached to the entity. Should be an instance of ICapabilitySync
|
||||
* @param capability In general, it is not necessary to initialize it, default: <span>{@code CapabilityManager.get(new CapabilityToken<>(){})}</span>
|
||||
* @param interfaceClass The interface class corresponding to the instance, such as: ICapabilitySync.class.
|
||||
* @param targets Targets types attached to capability
|
||||
*/
|
||||
public record CapabilityRecord<T extends ICapabilitySync<? extends Entity>>(Class<?> aClass, Capability<T> capability, Class<T> interfaceClass, Class<? extends Entity> target) {
|
||||
public record CapabilityRecord<T extends ICapabilitySync<? extends Entity>>(
|
||||
Class<?> aClass,
|
||||
Capability<T> capability,
|
||||
Class<T> interfaceClass,
|
||||
Set<Class<? extends Entity>> targets
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import net.minecraftforge.event.entity.player.PlayerEvent;
|
|||
|
||||
public class EntityCapabilityRemainder {
|
||||
/**
|
||||
* 玩家追踪实体事件<br>
|
||||
* 当有其他实体被加载时,客户端需要对方的capability,该事件可以主动发送<br>
|
||||
* 会调用{@link ICapabilitySync#sendToClient(ServerPlayer)}
|
||||
* @param event 追踪事件实例
|
||||
* Player start tracking an entity event<br>
|
||||
* When other entities are loaded, the client requires the capabilities of the other party, and this event can be actively sent<br>
|
||||
* Will call{@link ICapabilitySync#sendToClient(ServerPlayer)}
|
||||
* @param event event
|
||||
*/
|
||||
public static void onEntityBeTracked(PlayerEvent.StartTracking event) {
|
||||
if (event.getEntity() instanceof ServerPlayer attacker) {
|
||||
|
|
@ -28,10 +28,10 @@ public class EntityCapabilityRemainder {
|
|||
}
|
||||
|
||||
/**
|
||||
* 实体Tick事件<br>
|
||||
* 如果capability是dirty的,就会调用{@link ICapabilitySync#sendToClient()} <br>
|
||||
* 为了性能,每秒才触发一次同步
|
||||
* @param event 事件实例
|
||||
* Entity Tick Event<br>
|
||||
* If the capability is dirty, it will call {@link ICapabilitySync#sendToClient()} <br>
|
||||
* For performance reasons, synchronization is only triggered once per second
|
||||
* @param event event
|
||||
*/
|
||||
public static void capabilitySync(LivingEvent.LivingTickEvent event) {
|
||||
LivingEntity entity = event.getEntity();
|
||||
|
|
@ -50,8 +50,8 @@ public class EntityCapabilityRemainder {
|
|||
}
|
||||
|
||||
/**
|
||||
* 实体加入level的事件,初始化
|
||||
* @param event 实体加入事件
|
||||
* Event of entity joining level, initialization
|
||||
* @param event event
|
||||
*/
|
||||
public static void onEntityJoin(EntityJoinLevelEvent event) {
|
||||
Entity entity = event.getEntity();
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import net.minecraft.nbt.CompoundTag;
|
|||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
/**
|
||||
* 实现时建议手动添加:<br>
|
||||
* key —— 作为cap的唯一标识 <br>
|
||||
* getCapability(Entity entity) —— 获取cap的简化方法<br>
|
||||
* It is recommended to manually add it during implementation: <br>
|
||||
* {@code key} ---- As the unique identifier of capability. <br>
|
||||
* {@code getCapability(Entity entity)} ---- Simplified method for obtaining capability<br>
|
||||
* 例:
|
||||
* <pre>
|
||||
* {@code
|
||||
|
|
@ -23,6 +23,9 @@ import net.minecraft.world.entity.Entity;
|
|||
*
|
||||
*/
|
||||
public abstract class SimpleEntityCapabilitySync<T extends Entity> implements ICapabilitySync<T> {
|
||||
/**
|
||||
* Id
|
||||
*/
|
||||
public static final String Id = "Id";
|
||||
|
||||
private boolean dirty;
|
||||
|
|
@ -34,28 +37,36 @@ public abstract class SimpleEntityCapabilitySync<T extends Entity> implements IC
|
|||
}
|
||||
|
||||
/**
|
||||
* 你应该在每个属性的setter里调用它设置为true,以触发自动同步
|
||||
* @param dirty 是否应该同步(是否为脏)
|
||||
* You should call it to set it to true in the setter of each property to trigger automatic synchronization
|
||||
* @param dirty dirty
|
||||
*/
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* get id
|
||||
* @return Id
|
||||
*/
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* set id
|
||||
* @param id id
|
||||
*/
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从参数实例中复制数据到当前实例 <br>
|
||||
* 你不应该重写它,你应该实现 {@link SimpleEntityCapabilitySync#copyFrom(ICapabilitySync)}
|
||||
* @param oldData 旧数据
|
||||
* @param listenDone 最后是否执行完成方法 {@link ICapabilitySync#onCopyDone()}
|
||||
* Copy data from parameter instance to current instance <br>
|
||||
*You shouldn't rewrite it, you should implement: {@link SimpleEntityCapabilitySync#copyFrom(ICapabilitySync)}
|
||||
* @param oldData old data
|
||||
* @param listenDone Whether to execute the completion method at the end: {@link ICapabilitySync#onCopyDone()}
|
||||
*/
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData, boolean listenDone) {
|
||||
|
|
@ -66,14 +77,14 @@ public abstract class SimpleEntityCapabilitySync<T extends Entity> implements IC
|
|||
}
|
||||
|
||||
/**
|
||||
* 触发数据复制时会执行的方法
|
||||
* @param oldData 从这个数据中复制到当前实例
|
||||
* The method that will be executed when triggering data replication
|
||||
* @param oldData Copy from this data to the current instance
|
||||
*/
|
||||
public abstract void copyFrom(ICapabilitySync<?> oldData);
|
||||
|
||||
/**
|
||||
* 序列化为tag <br>
|
||||
* 你不应该重写它,你应该实现{@link ICapabilitySync#toTag(CompoundTag)}
|
||||
* Serialize to tag <br>
|
||||
* You shouldn't rewrite it, you should implement: {@link SimpleEntityCapabilitySync#toTag(CompoundTag)}
|
||||
* @return tag
|
||||
*/
|
||||
@Override
|
||||
|
|
@ -85,8 +96,8 @@ public abstract class SimpleEntityCapabilitySync<T extends Entity> implements IC
|
|||
}
|
||||
|
||||
/**
|
||||
* 反序列化为实例对象 <br>
|
||||
* 你应该不需要重写它,你应该实现{@link ICapabilitySync#fromTag(CompoundTag)}
|
||||
* Deserialize to instance object <br>
|
||||
* You don't need to rewrite it, you should implement: {@link SimpleEntityCapabilitySync#fromTag(CompoundTag)}
|
||||
* @param nbt nbt
|
||||
*/
|
||||
@Override
|
||||
|
|
@ -95,4 +106,19 @@ public abstract class SimpleEntityCapabilitySync<T extends Entity> implements IC
|
|||
if(nbt.contains(Id)) this.id = nbt.getInt(Id);
|
||||
fromTag(nbt);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the serializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to serializeNBT()
|
||||
* @param tag data tag
|
||||
* @return tag
|
||||
*/
|
||||
public abstract CompoundTag toTag(CompoundTag tag);
|
||||
|
||||
/**
|
||||
* In the deserializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to deserializeNBT()
|
||||
* @param tag data tag
|
||||
*/
|
||||
public abstract void fromTag(CompoundTag tag);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ public class PlayerCapabilityHandler {
|
|||
private static boolean isRegistered = false;
|
||||
|
||||
/**
|
||||
* 应在Forge主线中调用以监听capability注册 <br>
|
||||
* 建议在Mod构造方法里调用
|
||||
* It should be called in the Forge mainline to listen to the capability registration <br>
|
||||
* Suggest calling it in the Mod constructor method <br>
|
||||
* Normally SCCore will call it, so you should not call it
|
||||
* @param forgeBus forge event bus
|
||||
*/
|
||||
public static void register(IEventBus forgeBus) {
|
||||
if (isRegistered) return;
|
||||
|
|
@ -32,7 +34,10 @@ public class PlayerCapabilityHandler {
|
|||
isRegistered = true;
|
||||
}
|
||||
|
||||
//注册 capability
|
||||
/**
|
||||
* Register capability
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void registerCapability(RegisterCapabilitiesEvent event) {
|
||||
PlayerCapabilityRegistry.getCapabilityMap().values().forEach(record ->
|
||||
|
|
@ -40,7 +45,10 @@ public class PlayerCapabilityHandler {
|
|||
);
|
||||
}
|
||||
|
||||
//附加 capability
|
||||
/**
|
||||
* Attach capability to entity
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void attachCapability(AttachCapabilitiesEvent<?> event) {
|
||||
if(event.getObject() instanceof Player) {
|
||||
|
|
|
|||
|
|
@ -11,10 +11,20 @@ import net.minecraftforge.common.util.LazyOptional;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The final serialization, deserialization, and retrieval methods of capability
|
||||
* @param <C> extends {@link ICapabilitySync}
|
||||
*/
|
||||
@AutoRegisterCapability
|
||||
public class PlayerCapabilityProvider<C extends ICapabilitySync<?>> implements ICapabilitySerializable<CompoundTag> {
|
||||
private final C instance;
|
||||
private final ResourceLocation resourceLocation;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param resourceLocation key
|
||||
* @param instance instance
|
||||
*/
|
||||
public PlayerCapabilityProvider(ResourceLocation resourceLocation, C instance) {
|
||||
this.resourceLocation = resourceLocation;
|
||||
this.instance = instance;
|
||||
|
|
|
|||
|
|
@ -13,19 +13,18 @@ public class PlayerCapabilityRegistry {
|
|||
private final Map<ResourceLocation, CapabilityRecord<?>> capabilityRecordMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 通过此方法注册capability,仅当 {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}
|
||||
* 事件结束之前有效
|
||||
* @param key capability的唯一name
|
||||
* @param capabilityRecord 使用record存储了应该注册的capability的各项数据,参阅:{@link CapabilityRecord}
|
||||
* Registering player capabilities through this method only applies to {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}<br>
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link PlayerCapabilityRegistry.CapabilityRecord}
|
||||
*/
|
||||
public static void registerCapability(ResourceLocation key, CapabilityRecord<?> capabilityRecord) {
|
||||
CAPABILITIES.capabilityRecordMap.put(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过此方法获取对应的capability数据
|
||||
* @param key 根据key获取
|
||||
* @return capability 数据
|
||||
* Obtain corresponding capability data through this method
|
||||
* @param key Obtain based on key
|
||||
* @return capability
|
||||
*/
|
||||
public static CapabilityRecord<?> getCapabilityRecord(ResourceLocation key){
|
||||
return CAPABILITIES.capabilityRecordMap.get(key);
|
||||
|
|
@ -36,10 +35,10 @@ public class PlayerCapabilityRegistry {
|
|||
}
|
||||
|
||||
/**
|
||||
* 记录capability的注册数据
|
||||
* @param aClass 最终会附加给玩家的实例,应该是ICapabilitySync的实例
|
||||
* @param capability 一般情况下不需要初始化它,默认:CapabilityManager.get(new CapabilityToken<>(){})
|
||||
* @param interfaceClass instance实例对应的接口类,比如ICapabilitySync.class
|
||||
* Record the registration data of capability
|
||||
* @param aClass The instance that will ultimately be attached to the player. Should be an instance of ICapabilitySync
|
||||
* @param capability In general, it is not necessary to initialize it, default: <span>{@code CapabilityManager.get(new CapabilityToken<>(){})}</span>
|
||||
* @param interfaceClass The interface class corresponding to the instance, such as: ICapabilitySync.class.
|
||||
*/
|
||||
public record CapabilityRecord<T extends ICapabilitySync<? extends Player>>(Class<?> aClass, Capability<T> capability, Class<T> interfaceClass) { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,22 +2,15 @@ package com.linearpast.sccore.capability.data.player;
|
|||
|
||||
import com.linearpast.sccore.capability.CapabilityUtils;
|
||||
import com.linearpast.sccore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用于维护数据同步
|
||||
*/
|
||||
public class PlayerCapabilityRemainder {
|
||||
/**
|
||||
* 玩家跨越维度/死亡时应该转移数据到新身体上
|
||||
* @param event Clone事件
|
||||
* Players should transfer data to a new body when crossing dimensions or dying
|
||||
* @param event event
|
||||
*/
|
||||
public static void onPlayerClone(PlayerEvent.Clone event) {
|
||||
Player entity = event.getEntity();
|
||||
|
|
@ -37,8 +30,8 @@ public class PlayerCapabilityRemainder {
|
|||
}
|
||||
|
||||
/**
|
||||
* 玩家重生时应该更新自己的capability
|
||||
* @param event 重生事件实例
|
||||
* Players should update their capabilities when they are reborn
|
||||
* @param event event
|
||||
*/
|
||||
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
|
||||
if(event.getEntity() instanceof ServerPlayer newPlayer){
|
||||
|
|
@ -51,10 +44,10 @@ public class PlayerCapabilityRemainder {
|
|||
}
|
||||
|
||||
/**
|
||||
* 玩家追踪实体事件<br>
|
||||
* 当有其他玩家被加载时,客户端需要对方的capability,该事件可以主动发送<br>
|
||||
* 会调用{@link ICapabilitySync#sendToClient(ServerPlayer)}
|
||||
* @param event 追踪事件实例
|
||||
* Player start tracking an player event<br>
|
||||
* When other entities are loaded, the client requires the capabilities of the other party, and this event can be actively sent<br>
|
||||
* Will call{@link ICapabilitySync#sendToClient(ServerPlayer)}
|
||||
* @param event event
|
||||
*/
|
||||
public static void onEntityBeTracked(PlayerEvent.StartTracking event) {
|
||||
if (event.getTarget() instanceof Player target && event.getEntity() instanceof ServerPlayer attacker) {
|
||||
|
|
@ -67,9 +60,9 @@ public class PlayerCapabilityRemainder {
|
|||
}
|
||||
|
||||
/**
|
||||
* 玩家Tick事件<br>
|
||||
* 如果capability是dirty的,就会调用{@link ICapabilitySync#sendToClient()}
|
||||
* @param event 事件实例
|
||||
* Player Tick Event<br>
|
||||
* If the capability is dirty, it will call {@link ICapabilitySync#sendToClient()} <br>
|
||||
* @param event event
|
||||
*/
|
||||
public static void capabilitySync(TickEvent.PlayerTickEvent event) {
|
||||
if(!event.player.level().isClientSide){
|
||||
|
|
@ -85,10 +78,9 @@ public class PlayerCapabilityRemainder {
|
|||
}
|
||||
|
||||
/**
|
||||
* 玩家登录事件 <br>
|
||||
* 重初始化登录玩家的cap <br>
|
||||
* 将服务端所有玩家的cap发送给该玩家以初始化该玩家的客户端侧的RemotePlayer数据<br>
|
||||
* 上一行的这个行为可能会导致卡顿,它的必要性还未知,可以发pr或issue提议删除它
|
||||
* Player login event <br>
|
||||
* Reinitialize the login player's capability <br>
|
||||
* @param event event
|
||||
*/
|
||||
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
Player player = event.getEntity();
|
||||
|
|
@ -103,16 +95,5 @@ public class PlayerCapabilityRemainder {
|
|||
data.setDirty(false);
|
||||
data.sendToClient();
|
||||
});
|
||||
Optional.ofNullable(serverPlayer.getServer()).map(MinecraftServer::getPlayerList).map(PlayerList::getPlayers).ifPresent(
|
||||
serverPlayers -> serverPlayers.forEach(p -> {
|
||||
if(!p.getUUID().equals(serverPlayer.getUUID())) {
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<?> data = CapabilityUtils.getCapability(player, key);
|
||||
if(data == null) return;
|
||||
data.sendToClient(serverPlayer);
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
package com.linearpast.sccore.capability.data.player;
|
||||
|
||||
import com.linearpast.sccore.capability.data.ICapabilitySync;
|
||||
import com.linearpast.sccore.capability.data.entity.SimpleEntityCapabilitySync;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 实现时建议手动添加:<br>
|
||||
* key —— 作为cap的唯一标识 <br>
|
||||
* getCapability(Player player) —— 获取cap的简化方法<br>
|
||||
* It is recommended to manually add it during implementation: <br>
|
||||
* {@code key} ---- As the unique identifier of capability. <br>
|
||||
* {@code getCapability(Player player)} ---- Simplified method for obtaining capability<br>
|
||||
* 例:
|
||||
* <pre>
|
||||
* {@code
|
||||
|
|
@ -50,10 +51,10 @@ public abstract class SimplePlayerCapabilitySync implements ICapabilitySync<Play
|
|||
}
|
||||
|
||||
/**
|
||||
* 从参数实例中复制数据到当前实例 <br>
|
||||
* 你不应该重写它,你应该实现 {@link SimplePlayerCapabilitySync#copyFrom(ICapabilitySync)}
|
||||
* @param oldData 旧数据
|
||||
* @param listenDone 最后是否执行完成方法 {@link ICapabilitySync#onCopyDone()}
|
||||
* Copy data from parameter instance to current instance <br>
|
||||
*You shouldn't rewrite it, you should implement: {@link SimpleEntityCapabilitySync#copyFrom(ICapabilitySync)}
|
||||
* @param oldData old data
|
||||
* @param listenDone Whether to execute the completion method at the end: {@link ICapabilitySync#onCopyDone()}
|
||||
*/
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData, boolean listenDone) {
|
||||
|
|
@ -64,14 +65,14 @@ public abstract class SimplePlayerCapabilitySync implements ICapabilitySync<Play
|
|||
}
|
||||
|
||||
/**
|
||||
* 触发数据复制时会执行的方法
|
||||
* @param oldData 从这个数据中复制到当前实例
|
||||
* The method that will be executed when triggering data replication
|
||||
* @param oldData Copy from this data to the current instance
|
||||
*/
|
||||
public abstract void copyFrom(ICapabilitySync<?> oldData);
|
||||
|
||||
/**
|
||||
* 序列化为tag <br>
|
||||
* 你不应该重写它,你应该实现{@link ICapabilitySync#toTag(CompoundTag)}
|
||||
* Serialize to tag <br>
|
||||
* You shouldn't rewrite it, you should implement: {@link SimplePlayerCapabilitySync#toTag(CompoundTag)}
|
||||
* @return tag
|
||||
*/
|
||||
@Override
|
||||
|
|
@ -83,8 +84,8 @@ public abstract class SimplePlayerCapabilitySync implements ICapabilitySync<Play
|
|||
}
|
||||
|
||||
/**
|
||||
* 反序列化为实例对象 <br>
|
||||
* 你不应该重写它,你应该实现{@link ICapabilitySync#fromTag(CompoundTag)} )}
|
||||
* Deserialize to instance object <br>
|
||||
* You don't need to rewrite it, you should implement: {@link SimplePlayerCapabilitySync#fromTag(CompoundTag)}
|
||||
* @param nbt nbt
|
||||
*/
|
||||
@Override
|
||||
|
|
@ -93,4 +94,19 @@ public abstract class SimplePlayerCapabilitySync implements ICapabilitySync<Play
|
|||
if(nbt.contains(OwnerUUID)) this.ownerUUID = nbt.getUUID(OwnerUUID);
|
||||
fromTag(nbt);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the serializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to serializeNBT()
|
||||
* @param tag data tag
|
||||
* @return tag
|
||||
*/
|
||||
public abstract CompoundTag toTag(CompoundTag tag);
|
||||
|
||||
/**
|
||||
* In the deserializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to deserializeNBT()
|
||||
* @param tag data tag
|
||||
*/
|
||||
public abstract void fromTag(CompoundTag tag);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,31 +10,32 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 在Mod主类构造方法逻辑中调用createChannel,有两种:<br>
|
||||
* Call createChannel in the Mod main class construction method logic. There are two ways:<br>
|
||||
* <pre>
|
||||
* 1. {@link com.linearpast.sccore.capability.CapabilityUtils#createChannel(SimpleChannel)}
|
||||
* 若如此做,则必须重写Cap实体类中的所有sendToPlayer方法,并在重写中调用使用你的Channel
|
||||
* If you do this, you must override all the sendToPlayer methods in the Capability class, and call your Channel in the override
|
||||
* </pre>
|
||||
* <pre>
|
||||
* 2. {@link com.linearpast.sccore.capability.CapabilityUtils#createChannel()}
|
||||
* 若如此做,则网络包会以SnowyCrescentCore的Channel注册
|
||||
* If this is done, the network package will be registered with SCCore's Channel
|
||||
* </pre>
|
||||
* 所添加的网络包必须实现ICapabilityPacket接口
|
||||
* The added network packet must implement the ICapabilityPacket interface
|
||||
*/
|
||||
public class CapabilityChannel {
|
||||
private final SimpleChannel channel;
|
||||
|
||||
public CapabilityChannel(SimpleChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过此方法预设添加一个网络包,等待实例register
|
||||
* @param clazz 网络包类
|
||||
* @param cid 索引
|
||||
* @param decoder 解码器
|
||||
* @param encoder 编码器
|
||||
* @param handler 句柄
|
||||
* @param <T> 网络包接口
|
||||
* Add a network packet through this method and register
|
||||
* @param clazz Network packet class
|
||||
* @param cid index
|
||||
* @param decoder decoder
|
||||
* @param encoder encoder
|
||||
* @param handler handler
|
||||
* @param <T> extend {@code ICapabilityPacket<?>}
|
||||
*/
|
||||
public <T extends ICapabilityPacket<?>> void register(
|
||||
Class<T> clazz,
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ import java.util.function.Supplier;
|
|||
|
||||
public interface ICapabilityPacket<T extends Entity> {
|
||||
/**
|
||||
* 解码网络包
|
||||
* Decoding network packets
|
||||
* @param buf FriendlyByteBuf
|
||||
*/
|
||||
void encode(FriendlyByteBuf buf);
|
||||
|
||||
/**
|
||||
* 网络包处理事件,一般情况下不需要重写它,默认的行为足够使用
|
||||
* Network packet processing events generally do not need to be rewritten, and the default behavior is sufficient for use
|
||||
* @param supplier supplier
|
||||
*/
|
||||
default void handle(Supplier<NetworkEvent.Context> supplier){
|
||||
|
|
@ -26,30 +26,30 @@ public interface ICapabilityPacket<T extends Entity> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 网络包处理事件应该在这重写
|
||||
* Network packet processing events should be rewritten here
|
||||
* @param context NetworkEvent.Context
|
||||
*/
|
||||
void handler(NetworkEvent.Context context);
|
||||
|
||||
/**
|
||||
* 在网络包中获取对应的capability,一般在 {@link ICapabilityPacket#syncData}后执行
|
||||
* @param entity 目标实体
|
||||
* @return 返回Capability
|
||||
* Retrieve the corresponding capability from the network packet, usually executed after {@link ICapabilityPacket#syncData}
|
||||
* @param entity Target
|
||||
* @return capability
|
||||
*/
|
||||
@Nullable ICapabilitySync getCapability(T entity);
|
||||
@Nullable ICapabilitySync<?> getCapability(T entity);
|
||||
|
||||
/**
|
||||
* 获取Tag
|
||||
* @return Tag
|
||||
* Get tag
|
||||
* @return tag
|
||||
*/
|
||||
CompoundTag getData();
|
||||
|
||||
/**
|
||||
* 网络包中将tag转换为capability data,默认直接反序列化
|
||||
* Convert tags to capability data in network packets and deserialize them directly by default
|
||||
* @param dataTag tag
|
||||
* @param data 应被写入数据的data
|
||||
* @param data The data that should be written into the data
|
||||
*/
|
||||
default void syncData(CompoundTag dataTag, ICapabilitySync data){
|
||||
default void syncData(CompoundTag dataTag, ICapabilitySync<?> data){
|
||||
if(data == null) return;
|
||||
data.deserializeNBT(dataTag);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,19 +13,35 @@ import net.minecraftforge.network.NetworkEvent;
|
|||
public abstract class SimpleCapabilityPacket<T extends Entity> implements ICapabilityPacket<T> {
|
||||
private final CompoundTag data;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param data data tag
|
||||
*/
|
||||
public SimpleCapabilityPacket(CompoundTag data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* decoder
|
||||
* @param buf buf
|
||||
*/
|
||||
public SimpleCapabilityPacket(FriendlyByteBuf buf) {
|
||||
this.data = buf.readNbt();
|
||||
}
|
||||
|
||||
/**
|
||||
* encoder
|
||||
* @param buf buf
|
||||
*/
|
||||
@Override
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeNbt(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default network packet handle, generally sufficient for use
|
||||
* @param context NetworkEvent.Context
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void handler(NetworkEvent.Context context) {
|
||||
|
|
@ -43,7 +59,7 @@ public abstract class SimpleCapabilityPacket<T extends Entity> implements ICapab
|
|||
}
|
||||
if(entity == null) return;
|
||||
try {
|
||||
ICapabilitySync data = getCapability((T) entity);
|
||||
ICapabilitySync<?> data = getCapability((T) entity);
|
||||
syncData(nbt, data);
|
||||
}catch (Exception ignored) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.linearpast.sccore.network;
|
||||
package com.linearpast.sccore.core;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
|
@ -8,8 +8,8 @@ import net.minecraftforge.network.NetworkRegistry;
|
|||
import net.minecraftforge.network.PacketDistributor;
|
||||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
|
||||
public class Channel {
|
||||
public static int cid = 0;
|
||||
public class ModChannel {
|
||||
private static int cid = 0;
|
||||
private static final String PROTOCOL_VERSION = ModList.get()
|
||||
.getModContainerById(SnowyCrescentCore.MODID)
|
||||
.map(c -> c.getModInfo().getVersion().toString())
|
||||
|
|
@ -25,12 +25,8 @@ public class Channel {
|
|||
|
||||
}
|
||||
|
||||
public static int getCid() {
|
||||
return cid;
|
||||
}
|
||||
|
||||
public static void setCid(int cid) {
|
||||
Channel.cid = cid;
|
||||
public static int getAndAddCid() {
|
||||
return cid++;
|
||||
}
|
||||
|
||||
public static <MSG> void sendAllPlayer(MSG message){
|
||||
64
src/main/java/com/linearpast/sccore/core/ModCommands.java
Normal file
64
src/main/java/com/linearpast/sccore/core/ModCommands.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package com.linearpast.sccore.core;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.command.AnimationCommands;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandBuildContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraftforge.client.event.RegisterClientCommandsEvent;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class ModCommands {
|
||||
static final Set<String> animationCommand = new HashSet<>(Set.of(SnowyCrescentCore.MODID, "sc", "scc"));
|
||||
public static void addCommandAlias(String alias) {
|
||||
animationCommand.add(alias);
|
||||
}
|
||||
public static Set<String> getAnimationCommand() {
|
||||
return animationCommand;
|
||||
}
|
||||
|
||||
public static void registerCommands(IEventBus forgeBus, IEventBus modBus) {
|
||||
forgeBus.addListener(ModCommands::commonCommandRegister);
|
||||
forgeBus.addListener(ModCommands::clientCommandRegister);
|
||||
Arguments.register(modBus);
|
||||
}
|
||||
|
||||
|
||||
public static void commonCommandRegister(RegisterCommandsEvent event) {
|
||||
animationCommand.forEach(string -> {
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal(string);
|
||||
CommandBuildContext buildContext = event.getBuildContext();
|
||||
AnimationCommands.commonCommandRegister(builder);
|
||||
|
||||
event.getDispatcher().register(builder);
|
||||
});
|
||||
}
|
||||
|
||||
public static void clientCommandRegister(RegisterClientCommandsEvent event) {
|
||||
animationCommand.forEach(string -> {
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal(string);
|
||||
AnimationCommands.clientCommandRegister(builder);
|
||||
|
||||
event.getDispatcher().register(builder);
|
||||
});
|
||||
}
|
||||
|
||||
public static class Arguments {
|
||||
public static final DeferredRegister<ArgumentTypeInfo<?, ?>> REGISTRY = DeferredRegister.create(
|
||||
ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, SnowyCrescentCore.MODID
|
||||
);
|
||||
public static void register(IEventBus eventBus) {
|
||||
AnimationCommands.registerArguments(REGISTRY);
|
||||
REGISTRY.register(eventBus);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/main/java/com/linearpast/sccore/core/ModConfigs.java
Normal file
20
src/main/java/com/linearpast/sccore/core/ModConfigs.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package com.linearpast.sccore.core;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
|
||||
public class ModConfigs {
|
||||
public static class Common {
|
||||
public static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
public static final ForgeConfigSpec SPEC;
|
||||
|
||||
public static final ForgeConfigSpec.ConfigValue<Boolean> enableExample;
|
||||
|
||||
static {
|
||||
BUILDER.push("Development");
|
||||
enableExample = BUILDER.comment("Enable some example for lib.")
|
||||
.define("enableExample", false);
|
||||
BUILDER.pop();
|
||||
SPEC = BUILDER.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/main/java/com/linearpast/sccore/core/ModLazyRun.java
Normal file
52
src/main/java/com/linearpast/sccore/core/ModLazyRun.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package com.linearpast.sccore.core;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public abstract class ModLazyRun {
|
||||
private final String modId;
|
||||
public ModLazyRun(String modId) {
|
||||
this.modId = modId;
|
||||
}
|
||||
|
||||
public boolean testLoadedAndRun(Runnable runnable){
|
||||
if(isModLoaded()) runnable.run();
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public <T> T testLoadedAndCall(Callable<T> callable) {
|
||||
try {
|
||||
if(isModLoaded()) return callable.call();
|
||||
} catch (Exception ignored) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T testLoadedAndCall(Callable<T> callable, Callable<T> elseCall) {
|
||||
try {
|
||||
if(isModLoaded()) return callable.call();
|
||||
else return elseCall.call();
|
||||
}catch(Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addCommonListener(IEventBus forgeBus, IEventBus modBus){}
|
||||
public void addClientListener(IEventBus forgeBus, IEventBus modBus){}
|
||||
public void testLoadedAndAddListener(IEventBus forgeBus, IEventBus modBus) {
|
||||
if(isModLoaded()){
|
||||
addCommonListener(forgeBus, modBus);
|
||||
if(FMLLoader.getDist() == Dist.CLIENT){
|
||||
addClientListener(forgeBus, modBus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isModLoaded() {
|
||||
return ModList.get().isLoaded(modId);
|
||||
};
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package com.linearpast.sccore.example;
|
||||
|
||||
import com.linearpast.sccore.capability.CapabilityUtils;
|
||||
import com.linearpast.sccore.capability.data.entity.EntityCapabilityRegistry;
|
||||
import com.linearpast.sccore.capability.network.CapabilityChannel;
|
||||
import com.linearpast.sccore.example.cap.ISheepData;
|
||||
import com.linearpast.sccore.example.cap.SheepDataCapability;
|
||||
import com.linearpast.sccore.example.event.PlayerAttackEvent;
|
||||
import com.linearpast.sccore.network.Channel;
|
||||
import net.minecraft.world.entity.animal.Sheep;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
|
||||
public class ModCaps {
|
||||
/**
|
||||
* 注册实体capability的示例<br>
|
||||
* 请参阅 {@link CapabilityUtils}
|
||||
*/
|
||||
public static void register(){
|
||||
//如果你想将网络包注册到你自己的mod中,createChannel(INSTANCE)
|
||||
//然后别忘记在capability类里面重写所有的sendToClient方法
|
||||
CapabilityChannel channel = CapabilityUtils.createChannel();
|
||||
//不可与其他网络包重复的任意整数
|
||||
int cid = Channel.getCid();
|
||||
//注册实体cap和它的网络包
|
||||
//若注册玩家的请用registerPlayerCapabilityWithNetwork
|
||||
CapabilityUtils.registerEntityCapabilityWithNetwork(
|
||||
//一个resourceLocation,任意命名,不重复即可
|
||||
SheepDataCapability.key,
|
||||
//需要注册cap的数据
|
||||
new EntityCapabilityRegistry.CapabilityRecord<>(
|
||||
//registry将会 new 一个此类的实例,你可以在该类中重写无参构造以让它初始化
|
||||
SheepDataCapability.class,
|
||||
//固定写法,一般情况你无需修改它
|
||||
CapabilityManager.get(new CapabilityToken<>() {}),
|
||||
//第一个参数类的接口,可以为抽象类或不用接口
|
||||
//你可以用它自己: SheepDataCapability.class
|
||||
ISheepData.class,
|
||||
//注册的cap应附加在什么实体上
|
||||
Sheep.class
|
||||
),
|
||||
channel,
|
||||
//索引使用后+1,防止后续网络频道冲突
|
||||
cid++,
|
||||
//网络包的class
|
||||
SheepDataCapability.SheepCapabilityPacket.class,
|
||||
//网络包的decode方法
|
||||
SheepDataCapability.SheepCapabilityPacket::new,
|
||||
//网络包的encode方法
|
||||
SheepDataCapability.SheepCapabilityPacket::encode,
|
||||
//网络包的handle方法
|
||||
SheepDataCapability.SheepCapabilityPacket::handle
|
||||
);
|
||||
//这是为了还给Channel一个增加后的cid,以防止后续网络包索引重复
|
||||
Channel.setCid(cid);
|
||||
}
|
||||
|
||||
//测试cap是否成功添加的监听事件
|
||||
public static void addListenerToEvent(IEventBus forgeBus){
|
||||
forgeBus.addListener(PlayerAttackEvent::onPlayerAttack);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.linearpast.sccore.example.animation;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import com.linearpast.sccore.animation.data.Ride;
|
||||
import com.linearpast.sccore.animation.event.create.AnimationLayerRegisterEvent;
|
||||
import com.linearpast.sccore.example.animation.event.ExampleCommandEvent;
|
||||
import com.linearpast.sccore.example.animation.event.ExamplePlayerAttackEvent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
|
||||
/**
|
||||
* @see AnimationUtils
|
||||
*/
|
||||
public class ModAnimation {
|
||||
/**
|
||||
* This is an animation layer
|
||||
*/
|
||||
public static final ResourceLocation normalLayers = new ResourceLocation(SnowyCrescentCore.MODID, "normal_layers");
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* They are animations
|
||||
* {@code new ResourceLocation(modid, name)}
|
||||
* Resource from "assets/{modid}/player_animation/{name}.json"</pre>
|
||||
*/
|
||||
public static final ResourceLocation AmLyingToRightLying = new ResourceLocation(SnowyCrescentCore.MODID, "am_lying_to_right_lying");
|
||||
public static final ResourceLocation AmStandToLying = new ResourceLocation(SnowyCrescentCore.MODID, "am_stand_to_lying");
|
||||
public static final ResourceLocation WaltzGentleman = new ResourceLocation(SnowyCrescentCore.MODID, "waltz_gentleman");
|
||||
public static final ResourceLocation WaltzLady = new ResourceLocation(SnowyCrescentCore.MODID, "waltz_lady");
|
||||
|
||||
public static void register(IEventBus forgeBus, IEventBus modBus) {
|
||||
//You must define corresponding Animation to register
|
||||
Animation amLTRL = new Animation(AmLyingToRightLying)
|
||||
.withLyingType(Animation.LyingType.RIGHT)
|
||||
.withRide(Ride.create().addComponentAnimation(AmStandToLying));
|
||||
Animation amSTL = new Animation(AmStandToLying)
|
||||
.withLyingType(Animation.LyingType.FRONT);
|
||||
|
||||
Animation waltzGentleman = new Animation(WaltzGentleman)
|
||||
.withRide(Ride.create().addComponentAnimation(WaltzLady));
|
||||
Animation waltzLady = new Animation(WaltzLady)
|
||||
.withCamYaw(180);
|
||||
|
||||
//You can use it to register an Animation
|
||||
AnimationUtils.registerAnimation(AmLyingToRightLying, amLTRL);
|
||||
AnimationUtils.registerAnimation(AmStandToLying, amSTL);
|
||||
AnimationUtils.registerAnimation(WaltzGentleman, waltzGentleman);
|
||||
AnimationUtils.registerAnimation(WaltzLady, waltzLady);
|
||||
|
||||
|
||||
//Register by event
|
||||
//Or use AnimationUtils.registerAnimationLayer(ResourceLocation layer, int priority);
|
||||
modBus.addListener(ModAnimation::onLayerRegister);
|
||||
|
||||
//Try to play animation
|
||||
forgeBus.addListener(ExamplePlayerAttackEvent::onPlayerAttack);
|
||||
forgeBus.addListener(ExampleCommandEvent::inviteDance);
|
||||
if(FMLEnvironment.dist == Dist.CLIENT){
|
||||
forgeBus.addListener(ExamplePlayerAttackEvent::onInputEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onLayerRegister(AnimationLayerRegisterEvent event) {
|
||||
event.putLayer(normalLayers, 42);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package com.linearpast.sccore.example.animation.event;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.command.argument.AnimationArgument;
|
||||
import com.linearpast.sccore.animation.command.argument.AnimationLayerArgument;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class ExampleCommandEvent {
|
||||
record InviteRecord(long time, ResourceLocation layer, ResourceLocation animation, boolean isForce){}
|
||||
private static final Map<UUID, Map<UUID, InviteRecord>> invites = new HashMap<>();
|
||||
public static void inviteDance(RegisterCommandsEvent event) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dance").then(literal("invite")
|
||||
.then(argument("player", EntityArgument.player())
|
||||
.then(argument("layer", AnimationLayerArgument.layer())
|
||||
.then(argument("anim", AnimationArgument.animation())
|
||||
.executes(ExampleCommandEvent::inviteDance)
|
||||
.then(argument("force", BoolArgumentType.bool())
|
||||
.executes(ExampleCommandEvent::inviteDance)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(literal("accept")
|
||||
.then(argument("player", EntityArgument.player())
|
||||
.executes(ExampleCommandEvent::acceptInvite)
|
||||
)
|
||||
)
|
||||
);
|
||||
event.getDispatcher().register(builder);
|
||||
}
|
||||
|
||||
private static int inviteDance(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
//get info
|
||||
boolean force = false;
|
||||
try {
|
||||
force = BoolArgumentType.getBool(context, "force");
|
||||
} catch (Exception ignored) {}
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
ServerPlayer target = EntityArgument.getPlayer(context, "player");
|
||||
String layerString = AnimationLayerArgument.getLayer(context, "layer");
|
||||
String animString = AnimationArgument.getAnimation(context, "anim");
|
||||
ResourceLocation layer = new ResourceLocation(layerString);
|
||||
ResourceLocation anim = new ResourceLocation(animString);
|
||||
boolean finalForce = force;
|
||||
|
||||
//test info present
|
||||
boolean animationPresent = AnimationUtils.isAnimationPresent(anim);
|
||||
boolean animationLayerPresent = AnimationUtils.isAnimationLayerPresent(layer);
|
||||
if(!animationLayerPresent || !animationPresent) throw new Exception();
|
||||
|
||||
//update static cache
|
||||
Map<UUID, InviteRecord> inviteRecordMap = invites.getOrDefault(player.getUUID(), new HashMap<>());
|
||||
inviteRecordMap.put(target.getUUID(), new InviteRecord(System.currentTimeMillis(), layer, anim, finalForce));
|
||||
invites.put(player.getUUID(), inviteRecordMap);
|
||||
|
||||
//send message
|
||||
Component name = player.getName();
|
||||
Style pStyle = Style.EMPTY.withBold(true).withColor(ChatFormatting.GREEN).withClickEvent(
|
||||
new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/dance invite accept " + player.getName().getString())
|
||||
);
|
||||
target.sendSystemMessage(name.copy().append("邀请你跳一支舞。").append(Component.literal("单击此处同意.").setStyle(pStyle)));
|
||||
source.sendSuccess(() -> Component.literal("命令执行成功. 已发送邀请").withStyle(ChatFormatting.GREEN), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.literal("Command run fail.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int acceptInvite(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer target = source.getPlayerOrException();
|
||||
ServerPlayer player = EntityArgument.getPlayer(context, "player");
|
||||
|
||||
Map<UUID, InviteRecord> inviteRecordMap = invites.getOrDefault(player.getUUID(), null);
|
||||
if(inviteRecordMap == null) throw new Exception();
|
||||
InviteRecord inviteRecord = inviteRecordMap.getOrDefault(target.getUUID(), null);
|
||||
if(inviteRecord == null) throw new Exception();
|
||||
long now = System.currentTimeMillis();
|
||||
if(now - inviteRecord.time > 120000) {
|
||||
source.sendFailure(Component.literal("邀请已超时(2分钟).").withStyle(ChatFormatting.RED));
|
||||
player.sendSystemMessage(target.getName().copy().append("接受了你的舞蹈邀请. 但是邀请超时了(2分钟).").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
if(player.position().distanceToSqr(target.position()) > 64) {
|
||||
source.sendFailure(Component.literal("你们距离太远了(8格).").withStyle(ChatFormatting.RED));
|
||||
player.sendSystemMessage(target.getName().copy().append("接受了你的舞蹈邀请. 但你们距离太远了(8格).").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
inviteRecordMap.remove(target.getUUID());
|
||||
invites.put(player.getUUID(), inviteRecordMap);
|
||||
AnimationUtils.startAnimationTogether(player, inviteRecord.layer, inviteRecord.animation, inviteRecord.isForce, target);
|
||||
source.sendSuccess(() -> Component.literal("已接受邀请.").withStyle(ChatFormatting.GREEN), true);
|
||||
player.sendSystemMessage(target.getName().copy().append("已接受你的舞蹈邀请.").withStyle(ChatFormatting.GREEN));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.literal("Command run fail.").withStyle(ChatFormatting.RED));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.linearpast.sccore.example.animation.event;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.example.animation.ModAnimation;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.animal.Sheep;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.client.event.InputEvent;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
|
||||
public class ExamplePlayerAttackEvent {
|
||||
/**
|
||||
* when attack sheep, will play stand to lying animation <br>
|
||||
* when attack other player, will play animation together
|
||||
* @param event event
|
||||
*/
|
||||
public static void onPlayerAttack(AttackEntityEvent event) {
|
||||
Entity target = event.getTarget();
|
||||
Player entity = event.getEntity();
|
||||
if(entity instanceof ServerPlayer player) {
|
||||
if(target instanceof Sheep){
|
||||
ResourceLocation playing = AnimationUtils.getAnimationPlaying(player, ModAnimation.normalLayers);
|
||||
if(playing == null) {
|
||||
AnimationUtils.playAnimation(player, ModAnimation.normalLayers, ModAnimation.AmStandToLying);
|
||||
} else {
|
||||
AnimationUtils.playAnimation(player, ModAnimation.normalLayers, null);
|
||||
}
|
||||
}
|
||||
if(target instanceof ServerPlayer serverPlayer) {
|
||||
AnimationUtils.startAnimationTogether(
|
||||
serverPlayer,
|
||||
ModAnimation.normalLayers,
|
||||
ModAnimation.AmLyingToRightLying,
|
||||
true,
|
||||
player
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* when press "/", this will run
|
||||
* @param event event
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void onInputEvent(InputEvent.Key event) {
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
LocalPlayer player = instance.player;
|
||||
if (player == null) return;
|
||||
if(instance.options.keyCommand.isDown()) {
|
||||
ResourceLocation playing = AnimationUtils.getAnimationPlaying(player, ModAnimation.normalLayers);
|
||||
if(playing == null) {
|
||||
AnimationUtils.playAnimationWithRide(null, ModAnimation.normalLayers, ModAnimation.AmLyingToRightLying, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.linearpast.sccore.example.cap;
|
||||
|
||||
import com.linearpast.sccore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.world.entity.animal.Sheep;
|
||||
|
||||
/**
|
||||
* 接口继承ICapabilitySync是必需的,但是接口是非必需的(你可以在注册时直接使用cap类本身) <br>
|
||||
* 用于共享一些可能会用到的cap的公共方法
|
||||
*/
|
||||
public interface ISheepData extends ICapabilitySync<Sheep> {
|
||||
Integer getValue();
|
||||
void setValue(Integer value);
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.linearpast.sccore.example.capability;
|
||||
|
||||
import com.linearpast.sccore.capability.CapabilityUtils;
|
||||
import com.linearpast.sccore.capability.data.entity.EntityCapabilityRegistry;
|
||||
import com.linearpast.sccore.capability.network.CapabilityChannel;
|
||||
import com.linearpast.sccore.core.ModChannel;
|
||||
import com.linearpast.sccore.example.capability.data.ISheepData;
|
||||
import com.linearpast.sccore.example.capability.data.SheepDataCapability;
|
||||
import com.linearpast.sccore.example.capability.event.PlayerAttackEvent;
|
||||
import net.minecraft.world.entity.animal.Sheep;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ModCapability {
|
||||
/**
|
||||
* Example of Registered Entity Capability<br>
|
||||
* @see CapabilityUtils#registerEntityCapability
|
||||
* @see CapabilityUtils#registerPlayerCapability
|
||||
* @see CapabilityUtils#registerEntityCapabilityWithNetwork
|
||||
* @see CapabilityUtils#registerPlayerCapabilityWithNetwork
|
||||
*/
|
||||
public static void register(){
|
||||
//If you want to register network packets in your own mod, to use : createChannel(INSTANCE)
|
||||
//And don't forget to rewrite all the sendToClient methods in the capability class
|
||||
CapabilityChannel channel = CapabilityUtils.createChannel();
|
||||
//Register the entity capability and its network packet
|
||||
//If you want register about player, please use CapabilityUtils.registerPlayerCapabilityWithNetwork()
|
||||
CapabilityUtils.registerEntityCapabilityWithNetwork(
|
||||
//A resourceLocation, named arbitrarily without repetition
|
||||
SheepDataCapability.key,
|
||||
//Data that needs to be registered for capability
|
||||
new EntityCapabilityRegistry.CapabilityRecord<>(
|
||||
//Registry will create a new instance of this class
|
||||
//And you can override the parameterless construct in this class to initialize it
|
||||
SheepDataCapability.class,
|
||||
//Fixed writing style, generally you don't need to modify it
|
||||
CapabilityManager.get(new CapabilityToken<>() {}),
|
||||
//The interface of the first parameter class can be an abstract class or not require an interface
|
||||
//You can use it yourself: SheepDataCapability.class
|
||||
ISheepData.class,
|
||||
//What entities should the registered capability be attached to
|
||||
Set.of(Sheep.class)
|
||||
),
|
||||
channel,
|
||||
//Index+1 after use to prevent subsequent network channel conflicts
|
||||
ModChannel.getAndAddCid(),
|
||||
//Class of network packet
|
||||
SheepDataCapability.SheepCapabilityPacket.class,
|
||||
//Decoder method for network packet
|
||||
SheepDataCapability.SheepCapabilityPacket::new,
|
||||
//Encoder method for network packet
|
||||
SheepDataCapability.SheepCapabilityPacket::encode,
|
||||
//Handler method for network packet
|
||||
SheepDataCapability.SheepCapabilityPacket::handle
|
||||
);
|
||||
}
|
||||
|
||||
public static void addListenerToEvent(IEventBus forgeBus){
|
||||
forgeBus.addListener(PlayerAttackEvent::onPlayerAttack);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.linearpast.sccore.example.capability.data;
|
||||
|
||||
import com.linearpast.sccore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.world.entity.animal.Sheep;
|
||||
|
||||
/**
|
||||
* The interface inheritance ICapabilitySync is required, but the interface is not necessary (you can directly use the cap class itself during registration) <br>
|
||||
* Common methods for sharing caps that may be used.
|
||||
*/
|
||||
public interface ISheepData extends ICapabilitySync<Sheep> {
|
||||
Integer getValue();
|
||||
void setValue(Integer value);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.linearpast.sccore.example.cap;
|
||||
package com.linearpast.sccore.example.capability.data;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.capability.CapabilityUtils;
|
||||
|
|
@ -8,56 +8,61 @@ import com.linearpast.sccore.capability.network.SimpleCapabilityPacket;
|
|||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.animal.Sheep;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* cap的实体类 <br>
|
||||
* 继承SimpleEntityCapabilitySync意味着自动托管一个id的同步 <br>
|
||||
* 实现的IsheepData仅含有属性value的getter和setter <br>
|
||||
* The entity class of cap <br>
|
||||
* Inheriting SimpleElementCapability Sync means automatically hosting synchronization of an ID <br>
|
||||
* The IsheepData implemented only contains the property 'value' as a getter and setter <br>
|
||||
* @see SimpleEntityCapabilitySync
|
||||
*/
|
||||
public class SheepDataCapability extends SimpleEntityCapabilitySync<Sheep> implements ISheepData {
|
||||
//代表cap的key,注册、获取时都需要它
|
||||
public static final ResourceLocation key = new ResourceLocation(SnowyCrescentCore.MODID, "sheep_data");
|
||||
|
||||
//只是为了统一管理(反)序列化时的keyName
|
||||
public static final String Value = "Value";
|
||||
|
||||
//最后附加到实体实例变量
|
||||
private Integer value;
|
||||
|
||||
//getter
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
//setter
|
||||
@Override
|
||||
public void setValue(Integer value) {
|
||||
this.value = value;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
//在SimpleEntityCapabilitySync的serializeNBT方法中会调用
|
||||
//实际上相当于serializeNBT
|
||||
/**
|
||||
* @param tag data tag
|
||||
* @return tag
|
||||
* @see SimpleEntityCapabilitySync#toTag(CompoundTag)
|
||||
*/
|
||||
@Override
|
||||
public CompoundTag toTag(CompoundTag tag) {
|
||||
if(value != null) tag.putInt(Value, value);
|
||||
return tag;
|
||||
}
|
||||
|
||||
//在SimpleEntityCapabilitySync的deserializeNBT方法中会调用
|
||||
//实际上相当于deserializeNBT
|
||||
/**
|
||||
* @see SimpleEntityCapabilitySync#fromTag(CompoundTag)
|
||||
* @param tag data tag
|
||||
*/
|
||||
@Override
|
||||
public void fromTag(CompoundTag tag) {
|
||||
this.value = null;
|
||||
if(tag.contains(Value)) this.value = tag.getInt(Value);
|
||||
}
|
||||
|
||||
//从旧实例中复制数据到新实例的方法
|
||||
/**
|
||||
* @see SimpleEntityCapabilitySync#copyFrom(ICapabilitySync)
|
||||
* @param oldData Copy from this data to the current instance
|
||||
*/
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData) {
|
||||
SheepDataCapability data = (SheepDataCapability) oldData;
|
||||
|
|
@ -65,43 +70,54 @@ public class SheepDataCapability extends SimpleEntityCapabilitySync<Sheep> imple
|
|||
}
|
||||
|
||||
/**
|
||||
* 网络包,你可以在里面重写任意方法,关于方法的作用请参阅<br>
|
||||
* Network packet, you can rewrite any method inside. For the function of methods, please refer to<br>
|
||||
* {@link com.linearpast.sccore.capability.network.ICapabilityPacket} <br>
|
||||
* 可以不写在内部类中,作者是觉得它内容太少,写里面显得更紧凑美观
|
||||
* It is not necessary to include it in the internal class. I feel that the content is too limited and writing it inside makes it more compact and beautiful
|
||||
* @see SimpleCapabilityPacket
|
||||
*/
|
||||
public static class SheepCapabilityPacket extends SimpleCapabilityPacket<Sheep> {
|
||||
//网络包构造方法
|
||||
public SheepCapabilityPacket(CompoundTag data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
//这实际上是decoder
|
||||
public SheepCapabilityPacket(FriendlyByteBuf buf) {
|
||||
super(buf);
|
||||
}
|
||||
|
||||
//仅用在网络包内部的getCap
|
||||
@Override
|
||||
public @Nullable SheepDataCapability getCapability(Sheep entity) {
|
||||
return SheepDataCapability.getCapability(entity).orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
//获取默认网络包,会在sendToClient的时候调用以发送
|
||||
/**
|
||||
* Get the default network packet, which will be called when sendToClient sends it
|
||||
* @return network packet
|
||||
* @see ICapabilitySync#getDefaultPacket()
|
||||
*/
|
||||
@Override
|
||||
public SimpleCapabilityPacket<Sheep> getDefaultPacket() {
|
||||
return new SheepCapabilityPacket(serializeNBT());
|
||||
}
|
||||
|
||||
//该方法会在cap初始化时调用,比如玩家登录
|
||||
//该例中,当羊加入level时会调用该方法以初始化cap
|
||||
/**
|
||||
* This method will be called during cap initialization, such as player login <br>
|
||||
* In this example, when the sheep joins the level, this method will be called to initialize the capability
|
||||
* @param entity Target
|
||||
* @see ICapabilitySync#attachInit(Entity)
|
||||
*/
|
||||
@Override
|
||||
public void attachInit(Sheep entity) {
|
||||
|
||||
}
|
||||
|
||||
//在其他地方需要用到cap的时候调用这个
|
||||
//目的是为了简化cap utils的方法
|
||||
/**
|
||||
* It is not necessary. <br>
|
||||
* Call this when capability is needed in other places <br>
|
||||
* The purpose is to simplify the method of capability get
|
||||
* @param sheep Target
|
||||
* @return Optional capability
|
||||
*/
|
||||
public static Optional<SheepDataCapability> getCapability(Sheep sheep){
|
||||
return Optional.ofNullable(CapabilityUtils.getEntityCapability(
|
||||
sheep, SheepDataCapability.key, SheepDataCapability.class
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.linearpast.sccore.example.event;
|
||||
package com.linearpast.sccore.example.capability.event;
|
||||
|
||||
import com.linearpast.sccore.example.cap.SheepDataCapability;
|
||||
import com.linearpast.sccore.example.capability.data.SheepDataCapability;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
|
@ -9,7 +9,6 @@ import net.minecraft.world.entity.player.Player;
|
|||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
|
||||
public class PlayerAttackEvent {
|
||||
//简单的测试一下cap是否生效
|
||||
public static void onPlayerAttack(AttackEntityEvent event) {
|
||||
Entity target = event.getTarget();
|
||||
Player entity = event.getEntity();
|
||||
|
|
@ -22,7 +21,7 @@ public class PlayerAttackEvent {
|
|||
data.setValue(value);
|
||||
Integer id = data.getId();
|
||||
player.sendSystemMessage(Component.literal(
|
||||
"第" + value + "攻击了id为\"" + id + "\"的羊"
|
||||
value + "th attack on sheep with ID " + id
|
||||
));
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.linearpast.sccore.mixin;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SCCoreMixinPlugin implements IMixinConfigPlugin {
|
||||
@Override
|
||||
public void onLoad(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRefMapperConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||
if (targetClassName.startsWith("runData\\.")) {
|
||||
return "runData".equals(System.getProperty("gradle.task"));
|
||||
}
|
||||
if (mixinClassName.startsWith("com\\.linearpast\\." + SnowyCrescentCore.MODID + "\\.mixin\\.animation")) {
|
||||
return ModList.get().isLoaded(AnimationUtils.AnimModId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTargets(Set<String> set, Set<String> set1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.linearpast.sccore.mixin.animation;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.inter.IAnimationCapability;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class MixinEntity {
|
||||
@Shadow
|
||||
private float eyeHeight;
|
||||
|
||||
@Shadow public abstract float getEyeHeight(Pose pPose);
|
||||
|
||||
@Shadow private AABB bb;
|
||||
|
||||
@Shadow public abstract void setPose(Pose pPose);
|
||||
|
||||
@Inject(
|
||||
method = "getEyeHeight()F",
|
||||
at = @At(value = "HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void redefinedEyeHeight(CallbackInfoReturnable<Float> cir){
|
||||
Entity self = Entity.class.cast(this);
|
||||
if(self instanceof Player player){
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return;
|
||||
float camYModifier = 0.0f;
|
||||
for (ResourceLocation value : data.getAnimations().values()) {
|
||||
Animation animation = AnimationUtils.getAnimation(value);
|
||||
if(animation == null) continue;
|
||||
float animationCamY = animation.getCamY();
|
||||
camYModifier = Math.min(camYModifier, animationCamY);
|
||||
}
|
||||
this.eyeHeight = this.getEyeHeight(Pose.STANDING) + camYModifier;
|
||||
cir.setReturnValue(this.eyeHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "getBoundingBox",
|
||||
at = @At(value = "RETURN"),
|
||||
cancellable = true
|
||||
)
|
||||
private void redefinedBoundingBox(CallbackInfoReturnable<AABB> cir){
|
||||
Entity self = Entity.class.cast(this);
|
||||
if(self instanceof Player player){
|
||||
float heightModifier = AnimationUtils.getHeightModifier(player);
|
||||
if(heightModifier == 1.0f) return;
|
||||
double modifyHeight = 1.8f * heightModifier;
|
||||
cir.setReturnValue(this.bb.setMaxY(modifyHeight + this.bb.minY));
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "getBbHeight",
|
||||
at = @At(value = "RETURN")
|
||||
)
|
||||
private float redefinedBbHeight(float original){
|
||||
Entity self = Entity.class.cast(this);
|
||||
if(self instanceof Player player){
|
||||
float heightModifier = AnimationUtils.getHeightModifier(player);
|
||||
if(heightModifier == 1.0f) return original;
|
||||
return original * heightModifier;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Inject(
|
||||
method = "getPose",
|
||||
at = @At(value = "HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void redefinedPose(CallbackInfoReturnable<Pose> cir){
|
||||
Entity self = Entity.class.cast(this);
|
||||
if(self instanceof Player player){
|
||||
float heightModifier = AnimationUtils.getHeightModifier(player);
|
||||
if(heightModifier == 1.0f) return;
|
||||
setPose(Pose.STANDING);
|
||||
cir.setReturnValue(Pose.STANDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.linearpast.sccore.mixin.animation.client;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class MixinEntity {
|
||||
@Shadow public abstract void setXRot(float pXRot);
|
||||
|
||||
@Shadow private float xRot;
|
||||
|
||||
@Shadow public abstract void setYRot(float pYRot);
|
||||
|
||||
@Shadow private float yRot;
|
||||
|
||||
@Shadow public abstract float getXRot();
|
||||
|
||||
@Shadow public float xRotO;
|
||||
|
||||
@Shadow public float yRotO;
|
||||
|
||||
@Inject(
|
||||
method = "turn",
|
||||
at = {@At(value = "HEAD")},
|
||||
cancellable = true
|
||||
)
|
||||
private void turnPosePlayer(double pYRot, double pXRot, CallbackInfo ci) {
|
||||
Entity self = Entity.class.cast(this);
|
||||
if(self instanceof Player player){
|
||||
Animation.LyingType lyingType = AnimationUtils.getSideView(player);
|
||||
if(lyingType != null && Minecraft.getInstance().options.getCameraType().isFirstPerson()) {
|
||||
float f = (float)pXRot * 0.15F;
|
||||
float f1 = (float)pYRot * 0.15F;
|
||||
switch (lyingType) {
|
||||
case LEFT -> {
|
||||
this.setXRot(this.xRot + f1 * -1.0f);
|
||||
this.setYRot(this.yRot + f);
|
||||
}
|
||||
case RIGHT -> {
|
||||
this.setXRot(this.xRot + f1);
|
||||
this.setYRot(this.yRot + f * -1.0f);
|
||||
}
|
||||
}
|
||||
this.setXRot(Mth.clamp(this.getXRot(), 0.0f, 90.0f));
|
||||
this.xRotO = this.xRot;
|
||||
this.yRotO = this.yRot;
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.linearpast.sccore.mixin.animation.client;
|
||||
|
||||
import com.linearpast.sccore.animation.AnimationUtils;
|
||||
import com.linearpast.sccore.animation.data.Animation;
|
||||
import net.minecraft.client.model.AgeableListModel;
|
||||
import net.minecraft.client.model.ArmedModel;
|
||||
import net.minecraft.client.model.HeadedModel;
|
||||
import net.minecraft.client.model.HumanoidModel;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(HumanoidModel.class)
|
||||
public abstract class MixinHumanoidModel<T extends LivingEntity> extends AgeableListModel<T> implements ArmedModel, HeadedModel {
|
||||
@Shadow @Final public ModelPart head;
|
||||
|
||||
@Inject(
|
||||
method = "setupAnim(Lnet/minecraft/world/entity/LivingEntity;FFFFF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V")
|
||||
)
|
||||
private void modifyHeadRot(T pEntity, float pLimbSwing, float pLimbSwingAmount, float pAgeInTicks, float pNetHeadYaw, float pHeadPitch, CallbackInfo ci){
|
||||
if(pEntity instanceof Player player){
|
||||
Animation.LyingType lyingType = AnimationUtils.getSideView(player);
|
||||
if(lyingType != null) {
|
||||
float pitch = pHeadPitch - 90.0f;
|
||||
float yaw = pNetHeadYaw * -1.0f;
|
||||
switch (lyingType) {
|
||||
case LEFT: {
|
||||
pitch *= -1.0f;
|
||||
yaw *= -1.0f;
|
||||
}
|
||||
case RIGHT: {
|
||||
this.head.yRot = pitch * 0.017453292F;
|
||||
this.head.xRot = yaw * 0.017453292F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.linearpast.sccore.mixin.animation.client;
|
||||
|
||||
import com.linearpast.sccore.animation.mixin.IMixinKeyframeAnimationPlayer;
|
||||
import dev.kosmx.playerAnim.api.layered.KeyframeAnimationPlayer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(KeyframeAnimationPlayer.class)
|
||||
public class MixinKeyframeAnimationPlayer implements IMixinKeyframeAnimationPlayer {
|
||||
|
||||
@Shadow(remap = false)
|
||||
private int currentTick;
|
||||
|
||||
@Override
|
||||
@Unique
|
||||
public void sccore$setCurrentTick(int tick) {
|
||||
this.currentTick = tick;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,4 +25,11 @@ modId = "minecraft"
|
|||
mandatory = true
|
||||
versionRange = "${minecraft_version_range}"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
||||
|
||||
[[dependencies."${mod_id}"]]
|
||||
modId = "playeranimator"
|
||||
mandatory = false
|
||||
versionRange = "[1.0.1,)"
|
||||
ordering = "AFTER"
|
||||
side = "BOTH"
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"name": "am_lying_to_right_lying",
|
||||
"author": "LostInLinearPast",
|
||||
"description": "fix in 1.20.1 from CreatorGalaxy",
|
||||
"emote":{
|
||||
"isLoop": "false",
|
||||
"returnTick": 2,
|
||||
"beginTick":0,
|
||||
"endTick":6,
|
||||
"stopTick":2147483647,
|
||||
"degrees":false,
|
||||
"moves":[
|
||||
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"yaw":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"yaw":-0.08066412806510925
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"roll":-0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"roll":-1.568853497505188
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"pitch":1.5707963705062866
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"pitch":1.5704461336135864
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"y":-0.623153030872345
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"y":-0.4366978108882904
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"x":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"x":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"z":-0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"z":-0.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "am_stand_to_lying",
|
||||
"author": "LostInLinearPast",
|
||||
"description": "fix in 1.20.1 from CreatorGalaxy",
|
||||
"emote":{
|
||||
"isLoop": "false",
|
||||
"returnTick": 2,
|
||||
"beginTick":0,
|
||||
"endTick":5,
|
||||
"stopTick":2147483647,
|
||||
"degrees":false,
|
||||
"moves":[
|
||||
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"yaw":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"roll":-0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"pitch":1.5707963705062866
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"y":-0.623153030872345
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"x":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"z":-0.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1552
src/main/resources/assets/sccore/player_animation/waltz_lady.json
Normal file
1552
src/main/resources/assets/sccore/player_animation/waltz_lady.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/main/resources/logo.png
Normal file
BIN
src/main/resources/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
|
|
@ -4,9 +4,14 @@
|
|||
"package": "com.linearpast.sccore.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"refmap": "sccore.refmap.json",
|
||||
"plugin": "com.linearpast.sccore.mixin.SCCoreMixinPlugin",
|
||||
"mixins": [
|
||||
"animation.MixinEntity"
|
||||
],
|
||||
"client": [
|
||||
"animation.client.MixinEntity",
|
||||
"animation.client.MixinHumanoidModel",
|
||||
"animation.client.MixinKeyframeAnimationPlayer"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user