oldLayers = new HashSet<>(AnimationRegistry.getLayers().keySet());
- for (ResourceLocation layer : Set.copyOf(oldLayers)) {
- if (AnimationUtils.isClientAnimationStop(clientPlayer, layer)) {
- removeAnimation(clientPlayer, layer);
- }
- }
- });
- }
-
- /**
- * Test if layer exist and has been invite.
- * @param layer Target layer
- * @return If layer exist and has been invited
- */
- default boolean isAnimationLayerPresent(ResourceLocation layer) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> getLayers().contains(layer));
- }
-
- /**
- * Get animation which is playing now on player.
- * 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
- ResourceLocation getAnimationPlaying(Player player, @Nullable ResourceLocation layer);
-
- /**
- * Remove animation.
- * @param player Target player
- * @param layer Target layer
- * @return If success
- */
- @OnlyIn(Dist.CLIENT)
- default ApiBack removeAnimation(@Nullable AbstractClientPlayer player, ResourceLocation layer) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- AnimationUtils.removeAnimation(player, layer);
- LocalPlayer self = Minecraft.getInstance().player;
- if(self != null && player != null && Objects.equals(self.getUUID(), player.getUUID())) {
- ModChannel.INSTANCE.sendToServer(new StopAnimationPacket(layer));
- }
- return ApiBack.SUCCESS;
- });
- }
- ApiBack removeAnimation(@NotNull ServerPlayer serverPlayer, ResourceLocation layer);
-
- /**
- *
- * Play animation with ride. Player will ride an entity, then play animation.
- * When player unride, animation will be remove.
- * If player is riding and the "force" is false, it will return false
- *
- * @param player Target player
- * @param layer Target layer
- * @param animation Animation
- * @param force If force to ride and play animation
- * @return If success
- */
- @OnlyIn(Dist.CLIENT)
- default ApiBack playAnimationWithRide(@Nullable AbstractClientPlayer player, ResourceLocation layer, ResourceLocation animation, boolean force) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- if(isAnimationLayerPresent(layer) && isAnimationPresent(animation))
- return ApiBack.RESOURCE_NOT_FOUND;
- UUID uuid = player == null ? null : player.getUUID();
- AnimationData anim = getAnimation(animation);
- if(anim == null || anim.getRide() == null) return ApiBack.RESOURCE_NOT_FOUND;
- ModChannel.sendToServer(new PlayAnimationRidePacket(anim, layer, animation, uuid, force));
- return ApiBack.SUCCESS;
- });
- }
- default ApiBack playAnimationWithRide(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation, boolean force, Vec3 pos) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- ResourceLocation key = animation.getKey();
- if(!isAnimationLayerPresent(layer) || !isAnimationPresent(key))
- return ApiBack.RESOURCE_NOT_FOUND;
- if(animation.getRide() == null)
- return ApiBack.RESOURCE_NOT_FOUND;
- if(player instanceof FakePlayer)
- return ApiBack.UNSUPPORTED;
- boolean flag = player.getVehicle() != null;
- if(flag && force) player.stopRiding();
- else if(flag) return ApiBack.UNSUPPORTED;
- boolean result = AnimationRideEntity.create(player, layer, animation, force, pos) != null;
- return result ? ApiBack.SUCCESS : ApiBack.FAIL;
- });
- }
- default ApiBack playAnimationWithRide(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation, boolean force) {
- return playAnimationWithRide(player, layer, animation, force, player.position());
- }
-
- /**
- *
- * Play animation.
- * If run in Dist.CLIENT, player can be null, it will play animation only client.
- * If animation be null, it will remove animation on layer.
- *
- * @param player Target player
- * @param layer Target layer
- * @param animation Animation
- * @return If success
- */
- @OnlyIn(Dist.CLIENT)
- default ApiBack playAnimation(@Nullable AbstractClientPlayer player, ResourceLocation layer, ResourceLocation animation) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- if(isAnimationLayerPresent(layer) && isAnimationPresent(animation))
- return ApiBack.RESOURCE_NOT_FOUND;
- UUID uuid = player == null ? null : player.getUUID();
- AnimationData anim = getAnimation(animation);
- if(anim == null) return ApiBack.RESOURCE_NOT_FOUND;
-
- AnimationEvent.Play playEvent = new AnimationEvent.Play(
- LogicalSide.CLIENT,
- player,
- layer,
- anim
- );
- boolean post = MinecraftForge.EVENT_BUS.post(playEvent);
- Event.Result eventResult = playEvent.getResult();
- if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
- ModChannel.sendToServer(new PlayAnimationPacket(anim, layer, animation, uuid));
- return ApiBack.SUCCESS;
- });
- }
-
- /**
- * Trigger event and let implementation class handle
- * @param player player
- * @param layer target layer
- * @param animation animation
- * @return Api back
- */
- default ApiBack playAnimation(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation){
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- AnimationEvent.Play playEvent = new AnimationEvent.Play(LogicalSide.SERVER, player, layer, animation);
- boolean post = MinecraftForge.EVENT_BUS.post(playEvent);
- Event.Result eventResult = playEvent.getResult();
- if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
- return playAnimationServer(player, layer, animation);
- });
- }
- ApiBack playAnimationServer(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation);
-
- /**
- * Request animation
- */
- default ApiBack request(ServerPlayer player, ServerPlayer target, ResourceLocation layer, AnimationData animation, boolean isRide) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation.getKey()))
- return ApiBack.RESOURCE_NOT_FOUND;
- int tickCount = player.server.getTickCount();
- UUID playerUUID = player.getUUID();
- UUID targetUUID = target.getUUID();
-
- int origin = ModConfigs.Server.requestValidTime.get() * 20;
- int cooldown = ModConfigs.Server.requestCooldown.get() * 20;
- AnimationEvent.Send sendEvent = new AnimationEvent.Send(LogicalSide.SERVER, origin, cooldown, AnimationEvent.Type.APPLY);
- boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
- if(post) return ApiBack.BE_CANCELLED;
- Event.Result eventResult = sendEvent.getResult();
- switch (eventResult) {
- case DENY : return ApiBack.BE_CANCELLED;
- case DEFAULT : {
- //Test if is not in cooldown
- int lastTick = lastRequestTickMap.getOrDefault(playerUUID, 0);
- if(Math.max(tickCount - sendEvent.getCooldownTick(), 0) >= lastTick) {
- lastRequestTickMap.put(playerUUID, tickCount);
- } else return ApiBack.COOLDOWN;
- }
- case ALLOW : {
- //Add to cache, done
- int expireTick = sendEvent.getValidTick() + tickCount;
- requestMap.put(playerUUID, new RequestAnimationRecord(layer, animation, expireTick, targetUUID, isRide));
- return ApiBack.SUCCESS;
- }
- default: return ApiBack.UNSUPPORTED;
- }
- });
- }
-
- /**
- * Client request
- * @param target target
- * @param layer layer
- * @param animation animation
- * @return Api back
- */
- default ApiBack request(AbstractClientPlayer target, ResourceLocation layer, ResourceLocation animation) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation))
- return ApiBack.RESOURCE_NOT_FOUND;
- KeyframeAnimation keyframeAnimation = PlayerAnimationRegistry.getAnimation(animation);
- if(keyframeAnimation == null) return ApiBack.RESOURCE_NOT_FOUND;
- D data = getAnimation(animation);
- if(data == null) return ApiBack.RESOURCE_NOT_FOUND;
- AnimationEvent.Send sendEvent = new AnimationEvent.Send(
- LogicalSide.CLIENT,
- 0,
- 0,
- AnimationEvent.Type.REQUEST
- );
- boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
- Event.Result eventResult = sendEvent.getResult();
- if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
- ModChannel.sendToServer(new RequestAnimationPacket(data, layer, target.getUUID()));
- return ApiBack.SUCCESS;
- });
- }
-
- /**
- * Accept Request
- */
- default ApiBack acceptRequest(ServerPlayer player, ServerPlayer requestor) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- UUID requestorUUID = requestor.getUUID();
- RequestAnimationRecord record = requestMap.getOrDefault(requestorUUID, null);
- if (record == null) return ApiBack.UNSUPPORTED;
- UUID uuid = player.getUUID();
- if (!record.target().equals(uuid)) return ApiBack.UNSUPPORTED;
-
- AnimationEvent.Accept acceptEvent = new AnimationEvent.Accept(AnimationEvent.Type.REQUEST, 0);
- boolean post = MinecraftForge.EVENT_BUS.post(acceptEvent);
- if(post) return ApiBack.BE_CANCELLED;
- Event.Result eventResult = acceptEvent.getResult();
- switch (eventResult) {
- case DENY : return ApiBack.BE_CANCELLED;
- case DEFAULT : {
- //Test if is in valid time
- int tickCount = requestor.server.getTickCount();
- if (tickCount >= record.expireTick()) {
- requestMap.remove(requestorUUID);
- return ApiBack.OPERATION_EXPIRE;
- }
- }
- case ALLOW : {
- //done
- ApiBack back;
- if(record.isRide()) back = playAnimationWithRide(player, record.layer(), record.animation(), false);
- else back = playAnimation(player, record.layer(), record.animation());
- if(back == ApiBack.SUCCESS) requestMap.remove(requestorUUID);
- return back;
- }
- default: return ApiBack.UNSUPPORTED;
- }
- });
- }
-
- /**
- * Send apply to join animation on server side
- */
- default ApiBack apply(ServerPlayer player, ServerPlayer target) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- int tickCount = player.server.getTickCount();
-
- int origin = ModConfigs.Server.applyValidTime.get() * 20;
- int cooldown = ModConfigs.Server.applyCooldown.get() * 20;
- AnimationEvent.Send sendEvent = new AnimationEvent.Send(
- LogicalSide.SERVER,
- origin,
- cooldown,
- AnimationEvent.Type.APPLY
- );
- boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
- if(post) return ApiBack.BE_CANCELLED;
- Event.Result eventResult = sendEvent.getResult();
- switch (eventResult) {
- case DENY : return ApiBack.BE_CANCELLED;
- case DEFAULT : {
- //Test if is not in cooldown
- int lastTick = lastApplyTickMap.getOrDefault(player.getUUID(), 0);
- if(Math.max(tickCount - sendEvent.getCooldownTick(), 0) >= lastTick) {
- lastApplyTickMap.put(player.getUUID(), tickCount);
- } else return ApiBack.COOLDOWN;
- }
- case ALLOW : {
- //Add to cache, done
- int expireTick = sendEvent.getValidTick() + tickCount;
- applyMap.put(player.getUUID(), new ApplyAnimationRecord(target.getUUID(), expireTick));
- return ApiBack.SUCCESS;
- }
- default: return ApiBack.UNSUPPORTED;
- }
- });
- }
-
- /**
- * Send apply to join animation on client side.
- * It will send network packet and work on server side.
- */
- @OnlyIn(Dist.CLIENT)
- default ApiBack apply(AbstractClientPlayer target) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- AnimationEvent.Send sendEvent = new AnimationEvent.Send(
- LogicalSide.CLIENT,
- 0,
- 0,
- AnimationEvent.Type.APPLY
- );
- boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
- Event.Result eventResult = sendEvent.getResult();
- if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
- ModChannel.sendToServer(new ApplyAnimationPacket(target.getUUID()));
- return ApiBack.SUCCESS;
- });
- }
-
- /**
- * Player accept join apply.
- * @param player Acceptor
- * @param applier Applier
- * @return If accept and riding success
- */
- default ApiBack acceptApply(ServerPlayer player, ServerPlayer applier) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- ApplyAnimationRecord record = applyMap.getOrDefault(applier.getUUID(), null);
- if (record == null) return ApiBack.UNSUPPORTED;
- UUID uuid = player.getUUID();
- if (!record.target().equals(uuid)) return ApiBack.UNSUPPORTED;
-
- int maxDistance = ModConfigs.Server.applyValidDistance.get();
- AnimationEvent.Accept acceptEvent = new AnimationEvent.Accept(AnimationEvent.Type.APPLY, maxDistance);
- boolean post = MinecraftForge.EVENT_BUS.post(acceptEvent);
- if(post) return ApiBack.BE_CANCELLED;
- Event.Result eventResult = acceptEvent.getResult();
- switch (eventResult) {
- case DENY : return ApiBack.BE_CANCELLED;
- case DEFAULT : {
- //Test if is in valid distance
- int validDistance = acceptEvent.getValidDistance();
- if(player.distanceToSqr(applier) > validDistance * validDistance) {
- return ApiBack.OUT_RANGE;
- }
- int tickCount = applier.server.getTickCount();
- if (tickCount > record.expireTick()) {
- applyMap.remove(applier.getUUID());
- return ApiBack.OPERATION_EXPIRE;
- }
- }
- case ALLOW : {
- //done
- ApiBack back = joinAnimation(applier, player, false);
- if(back == ApiBack.SUCCESS) applyMap.remove(applier.getUUID());
- return back;
- }
- default: return ApiBack.UNSUPPORTED;
- }
- });
- }
-
- /**
- * Send invite on server side.
- * @param player Sender
- * @param animation Raw animation info
- * @param layer Target layer
- * @param targets Be invited players
- */
- default ApiBack invite(ServerPlayer player, ResourceLocation layer, AnimationData animation, Collection targets) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation.getKey()))
- return ApiBack.RESOURCE_NOT_FOUND;
- int tickCount = player.server.getTickCount();
-
- int origin = ModConfigs.Server.inviteValidTime.get() * 20;
- int cooldown = ModConfigs.Server.inviteCooldown.get() * 20;
- AnimationEvent.Send sendEvent = new AnimationEvent.Send(
- LogicalSide.SERVER,
- origin,
- cooldown,
- AnimationEvent.Type.INVITE
- );
- boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
- if(post) return ApiBack.BE_CANCELLED;
- Event.Result eventResult = sendEvent.getResult();
- switch (eventResult) {
- case DENY : return ApiBack.BE_CANCELLED;
- case DEFAULT : {
- //Test if is not in cooldown
- int lastTick = lastInviteTickMap.getOrDefault(player.getUUID(), 0);
- if(Math.max(tickCount - sendEvent.getCooldownTick(), 0) >= lastTick) {
- lastInviteTickMap.put(player.getUUID(), tickCount);
- } else return ApiBack.COOLDOWN;
- }
- case ALLOW : {
- //Add to cache, done
- int expireTick = sendEvent.getValidTick() + tickCount;
- inviteMap.put(player.getUUID(), new InviteAnimationRecord(layer, animation, expireTick, new ArrayList<>(targets)));
- return ApiBack.SUCCESS;
- }
- default: return ApiBack.UNSUPPORTED;
- }
- });
- }
-
- /**
- * Send invite on client side.
- * It will send network packet and work on server side.
- * @param animation Raw animation info
- * @param layer Target layer
- * @param targets Be invited players
- * @return If send to server successfully.
- */
- @OnlyIn(Dist.CLIENT)
- default ApiBack invite(ResourceLocation layer, ResourceLocation animation, AbstractClientPlayer ... targets){
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation))
- return ApiBack.RESOURCE_NOT_FOUND;
- KeyframeAnimation keyframeAnimation = PlayerAnimationRegistry.getAnimation(animation);
- if(keyframeAnimation == null) return ApiBack.RESOURCE_NOT_FOUND;
- Set list = Arrays.stream(targets).map(AbstractClientPlayer::getUUID).collect(Collectors.toSet());
- D data = getAnimation(animation);
- if(data == null) return ApiBack.RESOURCE_NOT_FOUND;
- AnimationEvent.Send sendEvent = new AnimationEvent.Send(
- LogicalSide.CLIENT,
- 0,
- 0,
- AnimationEvent.Type.INVITE
- );
- boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
- Event.Result eventResult = sendEvent.getResult();
- if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
- ModChannel.sendToServer(new InviteAnimationPacket(data, layer, list));
- return ApiBack.SUCCESS;
- });
- }
-
- /**
- * Player accept invite
- * @param player Acceptor
- * @param inviter Inviter
- * @return If accept and riding success.
- */
- default ApiBack acceptInvite(ServerPlayer player, ServerPlayer inviter) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- InviteAnimationRecord record = inviteMap.getOrDefault(inviter.getUUID(), null);
- if(record == null) return ApiBack.UNSUPPORTED;
- UUID uuid = player.getUUID();
- if (!record.targets().contains(uuid)) return ApiBack.UNSUPPORTED;
-
- int maxDistance = ModConfigs.Server.inviteValidDistance.get();
- AnimationEvent.Accept acceptEvent = new AnimationEvent.Accept(AnimationEvent.Type.INVITE, maxDistance);
- boolean post = MinecraftForge.EVENT_BUS.post(acceptEvent);
- if(post) return ApiBack.BE_CANCELLED;
- Event.Result eventResult = acceptEvent.getResult();
- switch (eventResult) {
- case DENY : return ApiBack.BE_CANCELLED;
- case DEFAULT : {
- //Test if is in valid distance
- int validDistance = acceptEvent.getValidDistance();
- if(player.distanceToSqr(inviter) > validDistance * validDistance) {
- return ApiBack.OUT_RANGE;
- }
- int tickCount = inviter.server.getTickCount();
- if(tickCount >= record.expireTick()) {
- inviteMap.remove(inviter.getUUID());
- return ApiBack.OPERATION_EXPIRE;
- }
- }
- case ALLOW : {
- //done
- ApiBack apiBack = playAnimationWithRide(inviter, record.layer(), record.animation(), false);
- if(apiBack == ApiBack.SUCCESS) {
- if(record.targets().isEmpty()) inviteMap.remove(inviter.getUUID());
- ApiBack back = joinAnimation(player, inviter, false);
- if(back == ApiBack.SUCCESS) record.targets().remove(uuid);
- return back;
- } else return apiBack;
- }
- default: return ApiBack.UNSUPPORTED;
- }
- });
- }
-}
diff --git a/src/main/java/top/leisuretimedock/animationcore/animation/service/RawAnimationService.java b/src/main/java/top/leisuretimedock/animationcore/animation/service/RawAnimationService.java
deleted file mode 100644
index d081176..0000000
--- a/src/main/java/top/leisuretimedock/animationcore/animation/service/RawAnimationService.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Anim Core mod
- * Copyright (C) 2026 LeisureTimeDock
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package top.leisuretimedock.animationcore.animation.service;
-
-import top.leisuretimedock.animationcore.animation.capability.RawAnimationDataCapability;
-import top.leisuretimedock.animationcore.animation.data.AnimationData;
-import top.leisuretimedock.animationcore.animation.data.RawAnimationData;
-import top.leisuretimedock.animationcore.animation.event.create.AnimationRegisterEvent;
-import top.leisuretimedock.animationcore.animation.register.RawAnimationRegistry;
-import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
-import top.leisuretimedock.animationcore.animation.utils.ApiBack;
-import net.minecraft.client.player.AbstractClientPlayer;
-import net.minecraft.nbt.CompoundTag;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.server.level.ServerPlayer;
-import net.minecraft.world.entity.player.Player;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.api.distmarker.OnlyIn;
-import net.minecraftforge.common.util.FakePlayer;
-import net.minecraftforge.fml.loading.FMLEnvironment;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-
-public class RawAnimationService implements IAnimationService {
- public static final RawAnimationService INSTANCE = new RawAnimationService();
-
- /**
- * Trigger raw animation registry.
- * It will clear all have been registered raw animation, then trigger register event.
- * If you need dynamic register, see {@link RawAnimationRegistry#register}, but it will reset when registry call register event.
- * If you need static register, you can add listener to {@link AnimationRegisterEvent.RawAnimation}
- */
- @OnlyIn(Dist.CLIENT)
- public void triggerRegistry() {
- ANIMATION_RUNNER.testLoadedAndRun(RawAnimationRegistry::triggerRegistry);
- }
-
- @Override
- public @Nullable RawAnimationData getAnimation(ResourceLocation location) {
- if(FMLEnvironment.dist == Dist.CLIENT) {
- return RawAnimationRegistry.getAnimations().getOrDefault(location, null);
- } else {
- return RawAnimationData.create(location);
- }
- }
-
- @Override
- public @Nullable RawAnimationData getAnimation(CompoundTag tag) {
- return new RawAnimationData(){{deserializeNBT(tag);}};
- }
-
- @Override
- public @Nullable RawAnimationDataCapability getCapability(Player player) {
- return RawAnimationDataCapability.getCapability(player).orElse(null);
- }
-
- @Override
- public void clearAnimations(ServerPlayer serverPlayer) {
- ANIMATION_RUNNER.testLoadedAndRun(() -> {
- Optional.ofNullable(getCapability(serverPlayer)).ifPresent(RawAnimationDataCapability::clearAnimations);
- detachAnimation(serverPlayer);
- });
- }
-
- @Override
- public boolean isAnimationPresent(ResourceLocation location) {
- return true;
- }
-
- @Override
- @OnlyIn(Dist.CLIENT)
- public void refreshAnimation(AbstractClientPlayer clientPlayer) {
- ANIMATION_RUNNER.testLoadedAndRun(() -> {
- RawAnimationDataCapability data = getCapability(clientPlayer);
- if(data == null) return;
- Set oldLayers = new HashSet<>(data.getAnimations().keySet());
- for (ResourceLocation layer : Set.copyOf(oldLayers)) {
- if (AnimationUtils.isClientAnimationStop(clientPlayer, layer)) {
- removeAnimation(clientPlayer, layer);
- }
- }
- });
- }
-
- @Override
- public @Nullable ResourceLocation getAnimationPlaying(Player player, @Nullable ResourceLocation layer) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- RawAnimationDataCapability data = getCapability(player);
- 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;
- });
- }
-
- @Override
- public ApiBack removeAnimation(@NotNull ServerPlayer serverPlayer, ResourceLocation layer) {
- boolean result = ANIMATION_RUNNER.testLoadedAndCall(() -> Optional.ofNullable(getCapability(serverPlayer))
- .map(data -> data.removeAnimation(layer)).orElse(false));
- return result ? ApiBack.SUCCESS : ApiBack.FAIL;
- }
-
- @Override
- public ApiBack playAnimationServer(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation) {
- return ANIMATION_RUNNER.testLoadedAndCall(() -> {
- ResourceLocation key = animation.getKey();
- if(!isAnimationLayerPresent(layer) || !isAnimationPresent(key))
- return ApiBack.RESOURCE_NOT_FOUND;
- if(player instanceof FakePlayer) return ApiBack.UNSUPPORTED;
- Boolean flag = Optional.ofNullable(getCapability(player)).map(data ->
- data.mergeAnimation(layer, key)).orElse(false);
- return flag ? ApiBack.SUCCESS : ApiBack.FAIL;
- });
- }
-}
diff --git a/src/main/java/top/leisuretimedock/animationcore/animation/utils/AnimationUtils.java b/src/main/java/top/leisuretimedock/animationcore/animation/utils/AnimationUtils.java
deleted file mode 100644
index d91b3d6..0000000
--- a/src/main/java/top/leisuretimedock/animationcore/animation/utils/AnimationUtils.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Anim Core mod
- * Copyright (C) 2026 LeisureTimeDock
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package top.leisuretimedock.animationcore.animation.utils;
-
-import top.leisuretimedock.animationcore.AnimationCore;
-import top.leisuretimedock.animationcore.animation.AnimationApi;
-import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
-import top.leisuretimedock.animationcore.animation.capability.RawAnimationDataCapability;
-import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
-import top.leisuretimedock.animationcore.animation.data.AnimationData;
-import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
-import top.leisuretimedock.animationcore.animation.mixin.IMixinKeyframeAnimationPlayer;
-import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
-import top.leisuretimedock.animationcore.animation.service.AnimationService;
-import top.leisuretimedock.animationcore.animation.service.IAnimationService;
-import top.leisuretimedock.animationcore.animation.service.RawAnimationService;
-import top.leisuretimedock.animationcore.core.datagen.ModLang;
-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.ChatFormatting;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.player.AbstractClientPlayer;
-import net.minecraft.client.player.LocalPlayer;
-import net.minecraft.network.chat.Component;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.entity.player.Player;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.api.distmarker.OnlyIn;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-import java.util.function.Predicate;
-
-public class AnimationUtils {
- /**
- * Test if layer exist animation which is not end.
- * Only in dist client
- * @param player Target player
- * @param layer Target layer
- * @return True when animation is loop, or currentTick not larger than endTick
- */
- @SuppressWarnings("unchecked")
- @OnlyIn(Dist.CLIENT)
- public static boolean isClientAnimationNotEnd(AbstractClientPlayer player, @Nullable ResourceLocation layer) {
- return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
- try {
- Set resourceLocations = new HashSet<>();
- if(layer == null) resourceLocations.addAll(AnimationRegistry.getLayers().keySet());
- else resourceLocations.add(layer);
- for (ResourceLocation location : resourceLocations) {
- ModifierLayer animationModifierLayer = (ModifierLayer) PlayerAnimationAccess
- .getPlayerAssociatedData(player).get(location);
- if(animationModifierLayer == null) continue;
- KeyframeAnimationPlayer animation = (KeyframeAnimationPlayer) animationModifierLayer.getAnimation();
- if(animation == null) return false;
- int currentTick = animation.getCurrentTick();
- boolean isLoop = animation.getData().isInfinite;
- int endTick = animation.getData().endTick;
- return isLoop || currentTick <= endTick;
- }
- } catch (Exception ignored) {}
- return false;
- });
- }
-
- /**
- * Test if layer exist animation which is not stop.
- * Only in dist client
- * @param player Target player
- * @param layer Target layer
- * @return True when the currentTick not larger than stopTick
- */
- @SuppressWarnings("unchecked")
- @OnlyIn(Dist.CLIENT)
- public static boolean isClientAnimationStop(AbstractClientPlayer player, @Nullable ResourceLocation layer) {
- return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
- try {
- Set resourceLocations = new HashSet<>();
- if(layer == null) resourceLocations.addAll(AnimationRegistry.getLayers().keySet());
- else resourceLocations.add(layer);
- boolean isNoneAnimation = true;
- for (ResourceLocation location : resourceLocations) {
- ModifierLayer animationModifierLayer = (ModifierLayer) PlayerAnimationAccess
- .getPlayerAssociatedData(player).get(location);
- if(animationModifierLayer == null) continue;
- KeyframeAnimationPlayer animation = (KeyframeAnimationPlayer) animationModifierLayer.getAnimation();
- if(animation == null) continue;
- int currentTick = animation.getCurrentTick();
- int stopTick = animation.getStopTick();
- if(currentTick < stopTick) isNoneAnimation = false;
- }
- return isNoneAnimation;
- } catch (Exception ignored) {}
- return true;
- });
- }
-
- /**
- * Client sync animation
- * @param clientPlayer player
- * @param target target
- */
- @SuppressWarnings("unchecked")
- @OnlyIn(Dist.CLIENT)
- public static void syncAnimation(AbstractClientPlayer clientPlayer, AbstractClientPlayer target) {
- IAnimationCapability clientPlayerData = AnimationDataCapability.getCapability(clientPlayer).orElse(null);
- IAnimationCapability targetData = AnimationDataCapability.getCapability(target).orElse(null);
- if(clientPlayerData == null) return;
- if(targetData == null) return;
- ResourceLocation clientPlayerLayer = clientPlayerData.getRiderAnimLayer();
- ResourceLocation targetLayer = targetData.getRiderAnimLayer();
- try {
- if(clientPlayerLayer == null || targetLayer == null) return;
- ModifierLayer modifierLayer = (ModifierLayer) PlayerAnimationAccess
- .getPlayerAssociatedData(clientPlayer).get(clientPlayerLayer);
- ModifierLayer targetModifierLayer = (ModifierLayer) PlayerAnimationAccess
- .getPlayerAssociatedData(target).get(targetLayer);
- 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.animcore$setCurrentTick(currentTick);
- } catch (Exception ignored) {}
- }
-
- /**
- * client remove animation
- * @param clientPlayer player
- * @param layer layer
- */
- @OnlyIn(Dist.CLIENT)
- public static void removeAnimation(@Nullable AbstractClientPlayer clientPlayer, ResourceLocation layer) {
- playAnimation(clientPlayer, layer, null);
- }
-
- /**
- * Client play animation
- * @param clientPlayer player
- * @param layer layer
- * @param animation animation
- */
- @SuppressWarnings("unchecked")
- @OnlyIn(Dist.CLIENT)
- public static void playAnimation(@Nullable AbstractClientPlayer clientPlayer, ResourceLocation layer, @Nullable ResourceLocation animation) {
- try {
- LocalPlayer localPlayer = Minecraft.getInstance().player;
- if(clientPlayer == null) clientPlayer = localPlayer;
- if(clientPlayer == null) return;
- ModifierLayer modifierLayer = (ModifierLayer) PlayerAnimationAccess
- .getPlayerAssociatedData(clientPlayer).get(layer);
- if(animation == null) {
- if(modifierLayer != null) {
- modifierLayer.replaceAnimationWithFade(
- AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
- null
- );
- }
- return;
- }
- if(modifierLayer == null) return;
- AnimationData anim = AnimationService.INSTANCE.getAnimation(animation);
- if(anim == null) {
- if((anim = RawAnimationService.INSTANCE.getAnimation(animation)) == null)
- return;
- }
- KeyframeAnimation keyframeAnimation = anim.getAnimation();
- if(keyframeAnimation == null) {
- if(localPlayer == null) return;
- localPlayer.sendSystemMessage(Component.translatable(
- ModLang.TranslatableMessage.ANIMATION_RESOURCE_NOT_FOUND.getKey(),
- animation.toString()
- ).withStyle(ChatFormatting.RED));
- modifierLayer.replaceAnimationWithFade(
- AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
- null
- );
- IAnimationService, ?> service = AnimationApi.getServiceGetterHelper(anim.getKey()).getService();
- if(service != null) service.removeAnimation(clientPlayer, layer);
- return;
- }
- modifierLayer.replaceAnimationWithFade(
- AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
- new KeyframeAnimationPlayer(keyframeAnimation)
- );
- }catch (Exception e) {
- AnimationCore.log.error("Failed to play animation : {}", animation, e);
- }
- }
-
- /**
- * Get the LyingType when there are animations which playing on player.
- * And It will return the first which be found.
- * @param player Target player
- * @return The first LyingType it find.
- */
- @Nullable
- public static GenericAnimationData.LyingType getSideView(Player player) {
- return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
- IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
- RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
- if(data == null) return null;
- if(rawData == null) return null;
-
- Map.Entry animations = null;
- ArrayList resourceLocations = new ArrayList<>();
- resourceLocations.addAll(data.getAnimations().values());
- resourceLocations.addAll(rawData.getAnimations().values());
- for (ResourceLocation value : resourceLocations) {
- AnimationData animation = AnimationApi.getDataHelper().getAnimationData(value);
- if(animation == null) return null;
- AnimationData.LyingType type = animation.getLyingType();
- if(type == null) continue;
- switch (type) {
- case FRONT,BACK -> {}
- case LEFT,RIGHT -> {
- if(animations == null || animations.getKey() < animation.getCamComputePriority()) {
- animations = new AbstractMap.SimpleEntry<>(animation.getCamComputePriority(), type);
- }
- }
- }
- }
- return animations == null ? null : animations.getValue();
- });
- }
-
- @Nullable
- @OnlyIn(Dist.CLIENT)
- public static AnimationData getPredicateAnimationData(Predicate predicate) {
- return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
- LocalPlayer player = Minecraft.getInstance().player;
- if(player == null) return null;
- IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
- RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
- if(data == null) return null;
- if(rawData == null) return null;
-
- Map.Entry animations = null;
- ArrayList resourceLocations = new ArrayList<>();
- resourceLocations.addAll(data.getAnimations().values());
- resourceLocations.addAll(rawData.getAnimations().values());
- if(data.getRiderAnimation() != null) resourceLocations.add(data.getRiderAnimation());
- for (ResourceLocation value : resourceLocations) {
- AnimationData animation = AnimationApi.getDataHelper().getAnimationData(value);
- if(animation == null) continue;
- if(!predicate.test(animation)) continue;
- if(animations == null || animations.getKey() < animation.getCamComputePriority()) {
- animations = new AbstractMap.SimpleEntry<>(animation.getCamComputePriority(), animation);
- }
- }
- return animations == null ? null : animations.getValue();
- });
- }
-
- @Nullable
- public static AnimationData getEyeModifierAnimationData(Player player) {
- IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
- RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
- if(data == null) return null;
- if(rawData == null) return null;
- Map.Entry entry = null;
- List values = new ArrayList<>();
- if(data.getRiderAnimation() != null) values.add(data.getRiderAnimation());
- values.addAll(data.getAnimations().values());
- values.addAll(rawData.getAnimations().values());
- for (ResourceLocation value : values) {
- AnimationData animation = AnimationApi.getDataHelper().getAnimationData(value);
- if(animation == null) continue;
- float animationCamY = (float) animation.getCamPosOffset().y;
- int priority = animation.getCamComputePriority();
- if((entry == null && animationCamY != 0)
- || (entry != null && priority > entry.getValue())) {
- entry = new HashMap.SimpleEntry<>(animation, priority);
- }
- }
- return entry == null ? null : entry.getKey();
- }
-}
diff --git a/src/main/java/top/leisuretimedock/animationcore/animation/utils/ApiBack.java b/src/main/java/top/leisuretimedock/animationcore/animation/utils/ApiBack.java
deleted file mode 100644
index 56d1337..0000000
--- a/src/main/java/top/leisuretimedock/animationcore/animation/utils/ApiBack.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Anim Core mod
- * Copyright (C) 2026 LeisureTimeDock
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package top.leisuretimedock.animationcore.animation.utils;
-
-import org.jetbrains.annotations.Nullable;
-
-public enum ApiBack {
- FAIL(0),
- SUCCESS(1),
- COOLDOWN(2),
- RESOURCE_NOT_FOUND(3),
- OUT_RANGE(4),
- OPERATION_EXPIRE(5),
- UNSUPPORTED(6),
- BE_CANCELLED(7),
- ;
- public final int value;
- ApiBack(int value) {
- this.value = value;
- }
-
- public boolean isValueOf(int value) {
- return this.value == value;
- }
-
- @Nullable
- public static ApiBack valueOf(int value) {
- ApiBack[] values = ApiBack.values();
- for (ApiBack v : values) {
- if (v.isValueOf(value)) {
- return v;
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/top/leisuretimedock/animationcore/animation/utils/FileUtils.java b/src/main/java/top/leisuretimedock/animationcore/animation/utils/FileUtils.java
deleted file mode 100644
index 5fba7ae..0000000
--- a/src/main/java/top/leisuretimedock/animationcore/animation/utils/FileUtils.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Anim Core mod
- * Copyright (C) 2026 LeisureTimeDock
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package top.leisuretimedock.animationcore.animation.utils;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-public class FileUtils {
- public static Set getAllFile(Path directory, Predicate filter) {
- try (Stream walk = Files.walk(directory)) {
- return walk.filter(Files::isRegularFile)
- .filter(filter)
- .collect(Collectors.toSet());
- } catch (Exception ignored) {
- return Collections.emptySet();
- }
- }
-
- public static void safeUnzip(String zipFile, String destDir) {
- Path destPath = Paths.get(destDir).toAbsolutePath();
-
- try (ZipFile zip = new ZipFile(zipFile)) {
- Files.createDirectories(destPath);
- Enumeration extends ZipEntry> entries = zip.entries();
-
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- Path entryPath = destPath.resolve(entry.getName()).normalize();
-
- if (entry.isDirectory()) {
- Files.createDirectories(entryPath);
- } else {
- Files.createDirectories(entryPath.getParent());
- try (InputStream in = zip.getInputStream(entry);
- OutputStream out = Files.newOutputStream(entryPath, StandardOpenOption.CREATE)) {
- byte[] buffer = new byte[8192];
- int bytesRead;
- while ((bytesRead = in.read(buffer)) != -1) {
- out.write(buffer, 0, bytesRead);
- }
- }
- }
- }
- } catch (Exception ignored) {}
- }
-}
diff --git a/src/main/java/top/leisuretimedock/animationcore/capability/CapabilityUtils.java b/src/main/java/top/leisuretimedock/animationcore/capability/CapabilityUtils.java
deleted file mode 100644
index 2350db2..0000000
--- a/src/main/java/top/leisuretimedock/animationcore/capability/CapabilityUtils.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Anim Core mod
- * Copyright (C) 2026 LeisureTimeDock
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package top.leisuretimedock.animationcore.capability;
-
-import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
-import top.leisuretimedock.animationcore.capability.data.entity.EntityCapabilityHandler;
-import top.leisuretimedock.animationcore.capability.data.entity.EntityCapabilityRegistry;
-import top.leisuretimedock.animationcore.capability.data.player.PlayerCapabilityHandler;
-import top.leisuretimedock.animationcore.capability.data.player.PlayerCapabilityRegistry;
-import top.leisuretimedock.animationcore.capability.network.CapabilityChannel;
-import top.leisuretimedock.animationcore.capability.network.ICapabilityPacket;
-import top.leisuretimedock.animationcore.core.ModChannel;
-import net.minecraft.network.FriendlyByteBuf;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.entity.Entity;
-import net.minecraft.world.entity.player.Player;
-import net.minecraftforge.eventbus.api.IEventBus;
-import net.minecraftforge.network.NetworkEvent;
-import net.minecraftforge.network.simple.SimpleChannel;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-public class CapabilityUtils {
-
- /**
- * Simultaneously invite 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 extend {@code ICapabilityPacket>}
- */
- public static > void registerPlayerCapabilityWithNetwork(
- ResourceLocation key, PlayerCapabilityRegistry.CapabilityRecord extends ICapabilitySync> capabilityRecord,
- CapabilityChannel channelRegister,
- int cid,
- Class clazz,
- Function decoder,
- BiConsumer encoder,
- BiConsumer> handler
- ) {
- PlayerCapabilityRegistry.registerCapability(key, capabilityRecord);
- channelRegister.register(clazz, cid, decoder, encoder, handler);
- }
-
- /**
- * Simultaneously invite 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