From 8b89097c34d28a88dd79f6485f80e7feaa12fca8 Mon Sep 17 00:00:00 2001
From: AzureZhen <7415711+AzureDoom@users.noreply.github.com>
Date: Thu, 13 Feb 2025 09:26:12 -0500
Subject: [PATCH 01/40] Just have packets and testing left for 1.20.1
---
common/build.gradle | 2 +
.../azurelib/animatable/GeoBlockEntity.java | 1 +
.../azure/azurelib/animatable/GeoEntity.java | 1 +
.../azure/azurelib/animatable/GeoItem.java | 1 +
.../animatable/GeoReplacedEntity.java | 1 +
.../animatable/SingletonGeoAnimatable.java | 1 +
.../animatable/client/RenderProvider.java | 1 +
.../azure/azurelib/cache/AzureLibCache.java | 15 +-
.../azurelib/cache/object/BakedGeoModel.java | 1 +
.../azure/azurelib/cache/object/GeoBone.java | 1 +
.../azure/azurelib/cache/object/GeoCube.java | 1 +
.../azure/azurelib/cache/object/GeoQuad.java | 1 +
.../azurelib/cache/object/GeoVertex.java | 1 +
.../cache/texture/GeoAbstractTexture.java | 1 +
.../azure/azurelib/constant/DataTickets.java | 1 +
.../azurelib/constant/DefaultAnimations.java | 1 +
.../azure/azurelib/loading/FileLoader.java | 14 +
.../loading/object/BakedModelFactory.java | 1 +
...ntityMixin_AzBlockEntityAnimatorCache.java | 32 ++
.../EntityMixin_AzEntityAnimatorCache.java | 32 ++
.../mixins/FabricMixinHumanoidArmorLayer.java | 84 +++-
.../ItemStackMixin_AzItemAnimatorCache.java | 27 ++
...tackMixin_AzItemStackIdentityRegistry.java | 37 ++
.../mixins/NeoMixinHumanoidArmorLayer.java | 44 --
.../model/DefaultedBlockGeoModel.java | 1 +
.../model/DefaultedEntityGeoModel.java | 1 +
.../azurelib/model/DefaultedGeoModel.java | 1 +
.../azurelib/model/DefaultedItemGeoModel.java | 1 +
.../mod/azure/azurelib/model/GeoModel.java | 1 +
.../azurelib/model/data/EntityModelData.java | 1 +
.../network/SerializableDataTicket.java | 1 +
.../azure/azurelib/network/api/IPacket.java | 1 +
.../azurelib/network/api/IPacketDecoder.java | 1 +
.../azurelib/network/api/IPacketEncoder.java | 1 +
.../network/packet/AnimDataSyncPacket.java | 1 +
.../network/packet/AnimTriggerPacket.java | 1 +
.../AzBlockEntityDispatchCommandPacket.java | 74 +++
.../packet/AzEntityDispatchCommandPacket.java | 48 ++
.../AzItemStackDispatchCommandPacket.java | 43 ++
.../packet/BlockEntityAnimDataSyncPacket.java | 1 +
.../packet/BlockEntityAnimTriggerPacket.java | 1 +
.../packet/EntityAnimDataSyncPacket.java | 1 +
.../packet/EntityAnimTriggerPacket.java | 1 +
.../azurelib/network/packet/EntityPacket.java | 1 +
.../network/packet/EntityPacketOnClient.java | 1 +
.../platform/services/AzureLibNetwork.java | 26 +
.../renderer/DyeableGeoArmorRenderer.java | 1 +
.../renderer/DynamicGeoEntityRenderer.java | 1 +
.../azurelib/renderer/GeoArmorRenderer.java | 1 +
.../renderer/GeoArmorRendererConstants.java | 1 +
.../azurelib/renderer/GeoBlockRenderer.java | 1 +
.../azurelib/renderer/GeoEntityRenderer.java | 1 +
.../azurelib/renderer/GeoItemRenderer.java | 1 +
.../azurelib/renderer/GeoObjectRenderer.java | 1 +
.../azure/azurelib/renderer/GeoRenderer.java | 1 +
.../renderer/GeoReplacedEntityRenderer.java | 1 +
.../dynamic/DynamicGeoArmorRenderer.java | 1 +
.../dynamic/DynamicGeoBlockRenderer.java | 1 +
.../dynamic/DynamicGeoItemRenderer.java | 1 +
.../dynamic/DynamicGeoObjectRenderer.java | 1 +
.../DynamicGeoReplacedEntityRenderer.java | 1 +
.../renderer/layer/AutoGlowingGeoLayer.java | 1 +
.../renderer/layer/BlockAndItemGeoLayer.java | 1 +
.../renderer/layer/BoneFilterGeoLayer.java | 1 +
.../layer/FastBoneFilterGeoLayer.java | 1 +
.../renderer/layer/GeoRenderLayer.java | 1 +
.../layer/GeoRenderLayersContainer.java | 1 +
.../renderer/layer/ItemArmorGeoLayer.java | 2 +-
.../azurelib/rewrite/AzResourceCache.java | 59 +++
.../rewrite/animation/AzAnimationContext.java | 71 +++
.../rewrite/animation/AzAnimationTimer.java | 91 ++++
.../rewrite/animation/AzAnimator.java | 134 +++++
.../rewrite/animation/AzAnimatorAccessor.java | 37 ++
.../rewrite/animation/AzAnimatorConfig.java | 108 +++++
.../animation/AzBoneAnimationUpdateUtil.java | 107 ++++
.../animation/AzCachedBoneUpdateUtil.java | 160 ++++++
.../cache/AzBakedAnimationCache.java | 54 +++
.../rewrite/animation/cache/AzBoneCache.java | 87 ++++
.../AzIdentifiableItemStackAnimatorCache.java | 40 ++
.../animation/cache/AzIdentityRegistry.java | 39 ++
.../AzAbstractAnimationController.java | 33 ++
.../controller/AzAnimationController.java | 210 ++++++++
.../AzAnimationControllerBuilder.java | 64 +++
.../AzAnimationControllerContainer.java | 39 ++
.../AzAnimationControllerTimer.java | 48 ++
.../controller/AzAnimationQueue.java | 51 ++
.../controller/AzBoneAnimationQueueCache.java | 61 +++
.../controller/AzBoneSnapshotCache.java | 53 ++
.../keyframe/AzAbstractKeyframeExecutor.java | 79 +++
.../controller/keyframe/AzAnimationPoint.java | 27 ++
.../controller/keyframe/AzBoneAnimation.java | 20 +
.../keyframe/AzBoneAnimationQueue.java | 400 +++++++++++++++
.../controller/keyframe/AzKeyframe.java | 56 +++
.../keyframe/AzKeyframeCallbackHandler.java | 126 +++++
.../keyframe/AzKeyframeCallbacks.java | 105 ++++
.../keyframe/AzKeyframeExecutor.java | 126 +++++
.../keyframe/AzKeyframeLocation.java | 14 +
.../keyframe/AzKeyframeManager.java | 47 ++
.../controller/keyframe/AzKeyframeStack.java | 43 ++
.../keyframe/AzKeyframeTransitioner.java | 125 +++++
.../handler/AzCustomKeyframeHandler.java | 14 +
.../handler/AzParticleKeyframeHandler.java | 14 +
.../handler/AzSoundKeyframeHandler.java | 14 +
.../controller/state/AzAnimationState.java | 41 ++
.../state/impl/AzAnimationPauseState.java | 25 +
.../state/impl/AzAnimationPlayState.java | 88 ++++
.../state/impl/AzAnimationStopState.java | 25 +
.../impl/AzAnimationTransitionState.java | 74 +++
.../AzAnimationControllerStateMachine.java | 105 ++++
.../animation/dispatch/AzDispatchSide.java | 54 +++
.../animation/dispatch/command/AzCommand.java | 175 +++++++
.../dispatch/command/AzCommandBuilder.java | 19 +
.../command/AzRootCommandBuilder.java | 53 ++
.../dispatch/command/action/AzAction.java | 25 +
.../command/action/codec/AzActionCodec.java | 70 +++
.../action/impl/root/AzRootCancelAction.java | 56 +++
.../impl/root/AzRootCancelAllAction.java | 52 ++
.../AzRootPlayAnimationSequenceAction.java | 52 ++
.../root/AzRootSetAnimationSpeedAction.java | 47 ++
.../impl/root/AzRootSetEasingTypeAction.java | 51 ++
.../root/AzRootSetTransitionSpeedAction.java | 52 ++
.../action/registry/AzActionRegistry.java | 111 +++++
.../command/sequence/AzAnimationSequence.java | 24 +
.../sequence/AzAnimationSequenceBuilder.java | 35 ++
.../command/stage/AzAnimationStage.java | 24 +
.../animation/easing/AzEasingType.java | 47 ++
.../animation/easing/AzEasingTypeLoader.java | 33 ++
.../easing/AzEasingTypeRegistry.java | 56 +++
.../animation/easing/AzEasingTypes.java | 180 +++++++
.../animation/easing/AzEasingUtil.java | 249 ++++++++++
.../AzCustomInstructionKeyframeEvent.java | 29 ++
.../animation/event/AzKeyframeEvent.java | 65 +++
.../event/AzParticleKeyframeEvent.java | 34 ++
.../animation/event/AzSoundKeyframeEvent.java | 34 ++
.../animation/impl/AzBlockAnimator.java | 19 +
.../animation/impl/AzEntityAnimator.java | 69 +++
.../animation/impl/AzItemAnimator.java | 25 +
.../parse/AzBakedAnimationsAdapter.java | 364 ++++++++++++++
.../animation/parse/AzKeyframesAdapter.java | 141 ++++++
.../play_behavior/AzPlayBehavior.java | 20 +
.../play_behavior/AzPlayBehaviorRegistry.java | 31 ++
.../play_behavior/AzPlayBehaviors.java | 33 ++
.../animation/primitive/AzBakedAnimation.java | 19 +
.../primitive/AzBakedAnimations.java | 30 ++
.../animation/primitive/AzKeyframes.java | 23 +
.../animation/primitive/AzLoopType.java | 151 ++++++
.../primitive/AzQueuedAnimation.java | 27 ++
.../property/AzAnimationProperties.java | 91 ++++
.../property/AzAnimationStageProperties.java | 88 ++++
.../codec/AzAnimationPropertiesCodec.java | 54 +++
.../AzAnimationStagePropertiesCodec.java | 66 +++
.../azurelib/rewrite/model/AzBakedModel.java | 53 ++
.../azure/azurelib/rewrite/model/AzBone.java | 459 ++++++++++++++++++
.../rewrite/model/AzBoneMetadata.java | 23 +
.../rewrite/model/AzBoneSnapshot.java | 180 +++++++
.../model/cache/AzBakedModelCache.java | 49 ++
.../model/factory/AzBakedModelFactory.java | 147 ++++++
.../impl/AzBuiltinBakedModelFactory.java | 88 ++++
.../model/factory/primitive/VertexSet.java | 120 +++++
.../registry/AzBakedModelFactoryRegistry.java | 38 ++
.../rewrite/render/AzLayerRenderer.java | 52 ++
.../rewrite/render/AzModelRenderer.java | 165 +++++++
.../rewrite/render/AzPhasedRenderer.java | 29 ++
.../azurelib/rewrite/render/AzProvider.java | 78 +++
.../rewrite/render/AzRendererConfig.java | 186 +++++++
.../rewrite/render/AzRendererPipeline.java | 189 ++++++++
.../render/AzRendererPipelineContext.java | 204 ++++++++
.../rewrite/render/armor/AzArmorModel.java | 77 +++
.../render/armor/AzArmorModelRenderer.java | 79 +++
.../rewrite/render/armor/AzArmorRenderer.java | 80 +++
.../render/armor/AzArmorRendererConfig.java | 105 ++++
.../render/armor/AzArmorRendererPipeline.java | 126 +++++
.../armor/AzArmorRendererPipelineContext.java | 78 +++
.../render/armor/AzArmorRendererRegistry.java | 34 ++
.../render/armor/bone/AzArmorBoneContext.java | 197 ++++++++
.../armor/bone/AzArmorBoneProvider.java | 96 ++++
.../bone/AzDefaultArmorBoneProvider.java | 48 ++
.../block/AzBlockEntityModelRenderer.java | 155 ++++++
.../render/block/AzBlockEntityRenderer.java | 64 +++
.../block/AzBlockEntityRendererConfig.java | 92 ++++
.../block/AzBlockEntityRendererPipeline.java | 86 ++++
.../AzBlockEntityRendererPipelineContext.java | 34 ++
.../render/entity/AzEntityLayerRenderer.java | 34 ++
.../entity/AzEntityLeashRenderUtil.java | 159 ++++++
.../render/entity/AzEntityModelRenderer.java | 275 +++++++++++
.../render/entity/AzEntityNameRenderUtil.java | 60 +++
.../render/entity/AzEntityRenderer.java | 123 +++++
.../render/entity/AzEntityRendererConfig.java | 119 +++++
.../entity/AzEntityRendererPipeline.java | 113 +++++
.../AzEntityRendererPipelineContext.java | 53 ++
.../render/item/AzItemGuiRenderUtil.java | 56 +++
.../render/item/AzItemModelRenderer.java | 77 +++
.../rewrite/render/item/AzItemRenderer.java | 96 ++++
.../render/item/AzItemRendererConfig.java | 128 +++++
.../render/item/AzItemRendererPipeline.java | 90 ++++
.../item/AzItemRendererPipelineContext.java | 35 ++
.../render/item/AzItemRendererRegistry.java | 39 ++
.../rewrite/render/layer/AzArmorLayer.java | 414 ++++++++++++++++
.../render/layer/AzAutoGlowingLayer.java | 55 +++
.../render/layer/AzBlockAndItemLayer.java | 179 +++++++
.../rewrite/render/layer/AzRenderLayer.java | 41 ++
.../azurelib/rewrite/util/MoveAnalysis.java | 123 +++++
.../azurelib/rewrite/util/state/State.java | 21 +
.../rewrite/util/state/StateMachine.java | 40 ++
.../util/state/StateMachineContext.java | 14 +
.../mod/azure/azurelib/util/AzureLibUtil.java | 11 +
.../mod/azure/azurelib/util/RenderUtils.java | 23 +-
fabric/build.gradle | 1 +
.../resources/azurelib.fabric.mixins.json | 4 +
gradle.properties | 2 +-
neoforge/build.gradle | 4 +
.../mixins/NeoMixinHumanoidArmorLayer.java | 92 ++++
.../main/resources/azurelib.forge.mixins.json | 4 +
213 files changed, 12352 insertions(+), 70 deletions(-)
create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java
delete mode 100644 common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java
create mode 100644 common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java
create mode 100644 common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeExecutor.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeLocation.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeManager.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeStack.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeTransitioner.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzCustomKeyframeHandler.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzParticleKeyframeHandler.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/handler/AzSoundKeyframeHandler.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/AzAnimationState.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPauseState.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationPlayState.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationStopState.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/impl/AzAnimationTransitionState.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/state/machine/AzAnimationControllerStateMachine.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/AzDispatchSide.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommand.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzCommandBuilder.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/AzRootCommandBuilder.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/AzAction.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/codec/AzActionCodec.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAction.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootCancelAllAction.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootPlayAnimationSequenceAction.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetAnimationSpeedAction.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetEasingTypeAction.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/impl/root/AzRootSetTransitionSpeedAction.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/action/registry/AzActionRegistry.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequence.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/sequence/AzAnimationSequenceBuilder.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/dispatch/command/stage/AzAnimationStage.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingType.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeLoader.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypeRegistry.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingTypes.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/easing/AzEasingUtil.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzCustomInstructionKeyframeEvent.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzKeyframeEvent.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzParticleKeyframeEvent.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/event/AzSoundKeyframeEvent.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzBlockAnimator.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzEntityAnimator.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/impl/AzItemAnimator.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzBakedAnimationsAdapter.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/parse/AzKeyframesAdapter.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehavior.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviorRegistry.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/play_behavior/AzPlayBehaviors.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimation.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzBakedAnimations.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzKeyframes.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzLoopType.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/primitive/AzQueuedAnimation.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationProperties.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/AzAnimationStageProperties.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationPropertiesCodec.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/animation/property/codec/AzAnimationStagePropertiesCodec.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBakedModel.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBone.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneMetadata.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/AzBoneSnapshot.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/cache/AzBakedModelCache.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/AzBakedModelFactory.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/impl/AzBuiltinBakedModelFactory.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/primitive/VertexSet.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/model/factory/registry/AzBakedModelFactoryRegistry.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzLayerRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzModelRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzPhasedRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzProvider.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererConfig.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipeline.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/AzRendererPipelineContext.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModel.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorModelRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererConfig.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipeline.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererPipelineContext.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/AzArmorRendererRegistry.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneContext.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzArmorBoneProvider.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/armor/bone/AzDefaultArmorBoneProvider.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityModelRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererConfig.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipeline.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/block/AzBlockEntityRendererPipelineContext.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLayerRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityLeashRenderUtil.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityModelRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityNameRenderUtil.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererConfig.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipeline.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/entity/AzEntityRendererPipelineContext.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemGuiRenderUtil.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemModelRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRenderer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererConfig.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipeline.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererPipelineContext.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/item/AzItemRendererRegistry.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzArmorLayer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzAutoGlowingLayer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzBlockAndItemLayer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/render/layer/AzRenderLayer.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/MoveAnalysis.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/state/State.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachine.java
create mode 100644 common/src/main/java/mod/azure/azurelib/rewrite/util/state/StateMachineContext.java
create mode 100644 neoforge/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java
diff --git a/common/build.gradle b/common/build.gradle
index 8216986b1..25080ca95 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -17,6 +17,8 @@ minecraft {
dependencies {
compileOnly group:'org.spongepowered', name:'mixin', version:'0.8.5'
+ compileOnly group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5'
+ annotationProcessor group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5'
}
publishing {
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java
index 9257e1f1a..a40677591 100644
--- a/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java
+++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoBlockEntity.java
@@ -25,6 +25,7 @@
/**
* The {@link GeoAnimatable} interface specific to {@link BlockEntity BlockEntities}
*/
+@Deprecated(forRemoval = true)
public interface GeoBlockEntity extends GeoAnimatable {
/**
* Get server-synced animation data via its relevant {@link SerializableDataTicket}.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java
index b11ded72e..0f6415a20 100644
--- a/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java
+++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoEntity.java
@@ -22,6 +22,7 @@
* The {@link GeoAnimatable} interface specific to {@link net.minecraft.world.entity.Entity Entities}. This also applies to Projectiles and other Entity subclasses.
* NOTE: This cannot be used for entities using the {@link mod.azure.azurelib.renderer.GeoReplacedEntityRenderer} as you aren't extending {@code Entity}. Use {@link GeoReplacedEntity} instead.
*/
+@Deprecated(forRemoval = true)
public interface GeoEntity extends GeoAnimatable {
/**
* Get server-synced animation data via its relevant {@link SerializableDataTicket}.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java
index 48bb23e8d..fd498bee5 100644
--- a/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java
+++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoItem.java
@@ -34,6 +34,7 @@
/**
* The {@link mod.azure.azurelib.core.animatable.GeoAnimatable GeoAnimatable} interface specific to {@link net.minecraft.world.item.Item Items}. This also applies to armor, as they are just items too.
*/
+@Deprecated(forRemoval = true)
public interface GeoItem extends SingletonGeoAnimatable {
String ID_NBT_KEY = "AzureLibID";
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java b/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java
index 0295db2ef..cf53ce87f 100644
--- a/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java
+++ b/common/src/main/java/mod/azure/azurelib/animatable/GeoReplacedEntity.java
@@ -24,6 +24,7 @@
/**
* The {@link GeoAnimatable} interface specific to {@link Entity Entities}. This interface is specifically for entities replacing the rendering of other, existing entities.
*/
+@Deprecated(forRemoval = true)
public interface GeoReplacedEntity extends SingletonGeoAnimatable {
/**
* Returns the {@link EntityType} this entity is intending to replace.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java b/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java
index d5453b5be..e0dd918e0 100644
--- a/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java
+++ b/common/src/main/java/mod/azure/azurelib/animatable/SingletonGeoAnimatable.java
@@ -26,6 +26,7 @@
/**
* The {@link GeoAnimatable} interface specific to singleton objects. This primarily applies to armor and items
*/
+@Deprecated(forRemoval = true)
public interface SingletonGeoAnimatable extends GeoAnimatable {
/**
* Register this as a synched {@code GeoAnimatable} instance with AzureLib's networking functions.
diff --git a/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java b/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java
index fa96652fd..e82f1769c 100644
--- a/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java
+++ b/common/src/main/java/mod/azure/azurelib/animatable/client/RenderProvider.java
@@ -15,6 +15,7 @@
* Internal interface for safely providing a custom renderer instances at runtime.
* This can be safely instantiated as a new anonymous class inside your {@link Item} class
*/
+@Deprecated(forRemoval = true)
public interface RenderProvider {
RenderProvider DEFAULT = new RenderProvider() {};
diff --git a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java
index 600307242..6d45e7adc 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java
@@ -12,6 +12,8 @@
import mod.azure.azurelib.loading.object.BakedAnimations;
import mod.azure.azurelib.loading.object.BakedModelFactory;
import mod.azure.azurelib.loading.object.GeometryTree;
+import mod.azure.azurelib.rewrite.animation.cache.AzBakedAnimationCache;
+import mod.azure.azurelib.rewrite.model.cache.AzBakedModelCache;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.PreparableReloadListener.PreparationBarrier;
@@ -72,9 +74,16 @@ public static CompletableFuture reload(PreparationBarrier stage, ResourceM
Map models = new Object2ObjectOpenHashMap<>();
return CompletableFuture
- .allOf(loadAnimations(backgroundExecutor, resourceManager, animations::put),
- loadModels(backgroundExecutor, resourceManager, models::put))
- .thenCompose(stage::wait).thenAcceptAsync(empty -> {
+ .allOf(
+ // TODO: Remove these.
+ loadAnimations(backgroundExecutor, resourceManager, animations::put),
+ loadModels(backgroundExecutor, resourceManager, models::put),
+ // Forward-support for new cache components
+ AzBakedAnimationCache.getInstance().loadAnimations(backgroundExecutor, resourceManager),
+ AzBakedModelCache.getInstance().loadModels(backgroundExecutor, resourceManager)
+ )
+ .thenCompose(stage::wait)
+ .thenAcceptAsync(empty -> {
AzureLibCache.ANIMATIONS = animations;
AzureLibCache.MODELS = models;
}, gameExecutor);
diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java b/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java
index c5dfce06d..e800c2021 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/object/BakedGeoModel.java
@@ -17,6 +17,7 @@
/**
* Baked model object for AzureLib models.
*/
+@Deprecated(forRemoval = true)
public record BakedGeoModel(List topLevelBones, ModelProperties properties) implements CoreBakedGeoModel {
/**
* Gets the list of top-level bones for this model.
diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java
index 245ae593b..c8b4c5f8e 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoBone.java
@@ -24,6 +24,7 @@
* Mutable bone object representing a set of cubes, as well as child bones.
* This is the object that is directly modified by animations to handle movement
*/
+@Deprecated(forRemoval = true)
public class GeoBone implements CoreGeoBone {
private final GeoBone parent;
private final String name;
diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java
index c2f36f8ad..11d2139bc 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoCube.java
@@ -12,4 +12,5 @@
/**
* Baked cuboid for a {@link GeoBone}
*/
+@Deprecated(forRemoval = true)
public record GeoCube(GeoQuad[] quads, Vec3 pivot, Vec3 rotation, Vec3 size, double inflate, boolean mirror) {}
diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java
index a35821092..4758c7483 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoQuad.java
@@ -15,6 +15,7 @@
/**
* Quad data holder
*/
+@Deprecated(forRemoval = true)
public record GeoQuad(
GeoVertex[] vertices,
Vector3f normal,
diff --git a/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java b/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java
index aad4c474d..0c4ba1732 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/object/GeoVertex.java
@@ -15,6 +15,7 @@
* @param texU The texture U coordinate
* @param texV The texture V coordinate
*/
+@Deprecated(forRemoval = true)
public record GeoVertex(Vector3f position, float texU, float texV) {
public GeoVertex(double x, double y, double z) {
this(new Vector3f((float)x, (float)y, (float)z), 0, 0);
diff --git a/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java b/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java
index 28605b174..c7a43845f 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/texture/GeoAbstractTexture.java
@@ -30,6 +30,7 @@
* Abstract texture wrapper for AzureLib textures.
* Mostly just handles boilerplate
*/
+@Deprecated(forRemoval = true)
public abstract class GeoAbstractTexture extends AbstractTexture {
/**
* Generates the texture instance for the given path with the given appendix if it hasn't already been generated
diff --git a/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java b/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java
index 85b098c10..f2d472c19 100644
--- a/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java
+++ b/common/src/main/java/mod/azure/azurelib/constant/DataTickets.java
@@ -29,6 +29,7 @@
* Stores the default (builtin) {@link DataTicket DataTickets} used in AzureLib.
* Additionally handles registration of {@link mod.azure.azurelib.network.SerializableDataTicket SerializableDataTickets}
*/
+@Deprecated(forRemoval = true)
public final class DataTickets {
private static final Map> SERIALIZABLE_TICKETS = new ConcurrentHashMap<>();
diff --git a/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java b/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java
index ea56e7697..d78684faf 100644
--- a/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java
+++ b/common/src/main/java/mod/azure/azurelib/constant/DefaultAnimations.java
@@ -24,6 +24,7 @@
* Using these won't affect much, but it may help keep some consistency in animation namings.
* Additionally, it encourages use of cached {@link mod.azure.azurelib.core.animation.RawAnimation RawAnimations}, to reduce overheads.
*/
+@Deprecated(forRemoval = true)
public final class DefaultAnimations {
public static final RawAnimation ITEM_ON_USE = RawAnimation.begin().thenPlay("item.use");
diff --git a/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java b/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java
index 73671f50a..e4b4936ea 100644
--- a/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java
+++ b/common/src/main/java/mod/azure/azurelib/loading/FileLoader.java
@@ -13,6 +13,8 @@
import mod.azure.azurelib.cache.object.BakedGeoModel;
import mod.azure.azurelib.loading.json.raw.Model;
import mod.azure.azurelib.loading.object.BakedAnimations;
+import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimation;
+import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimations;
import mod.azure.azurelib.util.JsonUtil;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
@@ -32,16 +34,28 @@ public final class FileLoader {
* @param location The resource path of the animations file
* @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access
*/
+ @Deprecated(forRemoval = true)
public static BakedAnimations loadAnimationsFile(ResourceLocation location, ResourceManager manager) {
return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), BakedAnimations.class);
}
+ /**
+ * Load up and deserialize an animation json file to its respective {@link AzBakedAnimation} components
+ *
+ * @param location The resource path of the animations file
+ * @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access
+ */
+ public static AzBakedAnimations loadAzAnimationsFile(ResourceLocation location, ResourceManager manager) {
+ return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), AzBakedAnimations.class);
+ }
+
/**
* Load up and deserialize a geo model json file to its respective {@link BakedGeoModel} format
*
* @param location The resource path of the model file
* @param manager The Minecraft {@code ResourceManager} responsible for maintaining in-memory resource access
*/
+ @Deprecated(forRemoval = true)
public static Model loadModelFile(ResourceLocation location, ResourceManager manager) {
return JsonUtil.GEO_GSON.fromJson(loadFile(location, manager), Model.class);
}
diff --git a/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java b/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java
index 5e4a2fce2..d34d717f8 100644
--- a/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java
+++ b/common/src/main/java/mod/azure/azurelib/loading/object/BakedModelFactory.java
@@ -33,6 +33,7 @@
* Base interface for a factory of {@link BakedGeoModel} objects.
* Handled by default by AzureLib, but custom implementations may be added by other mods for special needs
*/
+@Deprecated(forRemoval = true)
public interface BakedModelFactory {
final Map FACTORIES = new Object2ObjectOpenHashMap<>(1);
final BakedModelFactory DEFAULT_FACTORY = new Builtin();
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java
new file mode 100644
index 000000000..3de316e31
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/mixins/BlockEntityMixin_AzBlockEntityAnimatorCache.java
@@ -0,0 +1,32 @@
+package mod.azure.azurelib.mixins;
+
+import mod.azure.azurelib.rewrite.animation.AzAnimator;
+import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+
+/**
+ * Mixin class that implements the {@code AzAnimatorAccessor} interface to enable managing and associating
+ * an {@link AzAnimator} instance with a {@link BlockEntity}. This allows for caching and retrieval of the animator
+ * associated with specific block entities. This mixin modifies the behavior of {@link BlockEntity} by adding an
+ * animator cache that can be used to store and retrieve {@link AzAnimator} instances for animation handling.
+ */
+@Mixin(BlockEntity.class)
+public abstract class BlockEntityMixin_AzBlockEntityAnimatorCache implements AzAnimatorAccessor {
+
+ @Unique
+ @Nullable
+ private AzAnimator animator;
+
+ @Override
+ public void setAnimator(@Nullable AzAnimator animator) {
+ this.animator = animator;
+ }
+
+ @Override
+ public @Nullable AzAnimator getAnimatorOrNull() {
+ return animator;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java
new file mode 100644
index 000000000..17a9c45b5
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/mixins/EntityMixin_AzEntityAnimatorCache.java
@@ -0,0 +1,32 @@
+package mod.azure.azurelib.mixins;
+
+import mod.azure.azurelib.rewrite.animation.AzAnimator;
+import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor;
+import net.minecraft.world.entity.Entity;
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+
+/**
+ * A Mixin class designed to integrate an animation cache mechanism into the {@link Entity} class through the use of the
+ * {@link AzAnimatorAccessor} interface. This allows entities to store an instance of {@link AzAnimator} for managing
+ * animations. Implements methods to set and retrieve the {@link AzAnimator} instance, enabling animation control and
+ * association to the entity.
+ */
+@Mixin(Entity.class)
+public abstract class EntityMixin_AzEntityAnimatorCache implements AzAnimatorAccessor {
+
+ @Unique
+ @Nullable
+ private AzAnimator animator;
+
+ @Override
+ public void setAnimator(@Nullable AzAnimator animator) {
+ this.animator = animator;
+ }
+
+ @Override
+ public @Nullable AzAnimator getAnimatorOrNull() {
+ return animator;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java b/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java
index fee1e43f4..abd9f2f6c 100644
--- a/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java
+++ b/common/src/main/java/mod/azure/azurelib/mixins/FabricMixinHumanoidArmorLayer.java
@@ -1,19 +1,23 @@
package mod.azure.azurelib.mixins;
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.sugar.Share;
+import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import com.mojang.blaze3d.vertex.PoseStack;
import mod.azure.azurelib.animatable.GeoItem;
import mod.azure.azurelib.animatable.client.RenderProvider;
+import mod.azure.azurelib.renderer.GeoArmorRenderer;
+import mod.azure.azurelib.rewrite.render.armor.AzArmorRendererRegistry;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer;
+import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
@@ -21,21 +25,67 @@
*/
@Mixin(value = HumanoidArmorLayer.class, priority = 700)
public abstract class FabricMixinHumanoidArmorLayer> {
- @Unique
- private LivingEntity gl_storedEntity;
- @Unique
- private EquipmentSlot gl_storedSlot;
- @Unique
- private ItemStack gl_storedItemStack;
-
- @Inject(method = "renderArmorPiece", at = @At(value = "HEAD"))
- public void armorModelHook(PoseStack poseStack, MultiBufferSource multiBufferSource, T livingEntity, EquipmentSlot equipmentSlot, int i, A humanoidModel, CallbackInfo ci) {
- this.gl_storedEntity = livingEntity;
- this.gl_storedSlot = equipmentSlot;
- this.gl_storedItemStack = livingEntity.getItemBySlot(equipmentSlot);
+ @ModifyExpressionValue(
+ method = "renderArmorPiece",
+ at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/world/entity/LivingEntity;getItemBySlot(Lnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/world/item/ItemStack;"
+ )
+ )
+ private ItemStack azurelib$captureItemBySlot(
+ ItemStack original,
+ @Share("item_by_slot") LocalRef itemBySlotRef
+ ) {
+ itemBySlotRef.set(original);
+ return original;
}
- @ModifyArg(method = "renderArmorPiece", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;renderModel(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/HumanoidModel;ZFFFLjava/lang/String;)V"), index = 4)
- public A injectArmor(A humanoidModel) {
- return this.gl_storedItemStack.getItem() instanceof GeoItem ? (A) RenderProvider.of(this.gl_storedItemStack).getGenericArmorModel(this.gl_storedEntity, this.gl_storedItemStack, this.gl_storedSlot, (HumanoidModel) humanoidModel) : humanoidModel; }
+ @Inject(
+ method = "renderArmorPiece", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;usesInnerModel(Lnet/minecraft/world/entity/EquipmentSlot;)Z"
+ ), cancellable = true
+ )
+ public void azurelib$renderAzurelibModel(
+ PoseStack poseStack,
+ MultiBufferSource bufferSource,
+ T entity,
+ EquipmentSlot equipmentSlot,
+ int packedLight,
+ A baseModel,
+ CallbackInfo ci,
+ @Share("item_by_slot") LocalRef itemBySlotRef
+ ) {
+ var stack = itemBySlotRef.get();
+ var renderProvider = RenderProvider.of(stack);
+ @SuppressWarnings("unchecked")
+ var humanoidModel = (HumanoidModel) baseModel;
+ var geckolibModel = renderProvider
+ .getGenericArmorModel(entity, stack, equipmentSlot, humanoidModel);
+
+ if (geckolibModel != null && stack.getItem() instanceof GeoItem) {
+ if (geckolibModel instanceof GeoArmorRenderer> geoArmorRenderer) {
+ geoArmorRenderer.prepForRender(entity, stack, equipmentSlot, baseModel);
+ }
+
+ baseModel.copyPropertiesTo((A) geckolibModel);
+
+ geckolibModel.renderToBuffer(poseStack, null, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1);
+ ci.cancel();
+ }
+
+ var renderer = AzArmorRendererRegistry.getOrNull(stack.getItem());
+
+ if (renderer != null) {
+ var rendererPipeline = renderer.rendererPipeline();
+ var armorModel = rendererPipeline.armorModel();
+ @SuppressWarnings("unchecked")
+ var typedHumanoidModel = (HumanoidModel) armorModel;
+
+ renderer.prepForRender(entity, stack, equipmentSlot, baseModel);
+ baseModel.copyPropertiesTo(typedHumanoidModel);
+ armorModel.renderToBuffer(poseStack, null, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1);
+ ci.cancel();
+ }
+ }
}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java
new file mode 100644
index 000000000..e61f5e3c3
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java
@@ -0,0 +1,27 @@
+package mod.azure.azurelib.mixins;
+
+import mod.azure.azurelib.rewrite.animation.AzAnimator;
+import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor;
+import mod.azure.azurelib.rewrite.animation.cache.AzIdentifiableItemStackAnimatorCache;
+import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator;
+import mod.azure.azurelib.util.AzureLibUtil;
+import net.minecraft.world.item.ItemStack;
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Mixin;
+
+@Mixin(ItemStack.class)
+public abstract class ItemStackMixin_AzItemAnimatorCache implements AzAnimatorAccessor {
+
+ @Override
+ public void setAnimator(@Nullable AzAnimator animator) {
+ var itemStack = AzureLibUtil.self(this);
+ AzIdentifiableItemStackAnimatorCache.getInstance().add(itemStack, (AzItemAnimator) animator);
+ }
+
+ @Override
+ public @Nullable AzAnimator getAnimatorOrNull() {
+ var self = AzureLibUtil.self(this);
+ var uuid = self.getOrCreateTag().getUUID("az_id");
+ return AzIdentifiableItemStackAnimatorCache.getInstance().getOrNull(uuid);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java
new file mode 100644
index 000000000..1295f25e8
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java
@@ -0,0 +1,37 @@
+package mod.azure.azurelib.mixins;
+
+import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry;
+import mod.azure.azurelib.util.AzureLibUtil;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.item.ItemStack;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.UUID;
+
+/**
+ * A mixin class for modifying the initialization behavior of the {@link ItemStack} class. This mixin specifically
+ * ensures that a unique identity component is added to an {@link ItemStack} upon creation, provided that the associated
+ * item has been registered in the {@link AzIdentityRegistry}. When an {@link ItemStack} is instantiated, the mixin
+ * checks if: - The item has an identity registered in {@link AzIdentityRegistry}. - The provided
+ * {@link PatchedDataComponentMap} does not already contain an `az_id` component. If both conditions are met, the mixin
+ * assigns a universally unique identifier (UUID) as the `az_id` component to the associated
+ * {@link PatchedDataComponentMap}. This mechanism enables unique identification and tracking of specific item stacks in
+ * the game.
+ */
+@Mixin(ItemStack.class)
+public class ItemStackMixin_AzItemStackIdentityRegistry {
+
+ @Inject(
+ method = "(Lnet/minecraft/nbt/CompoundTag;)V",
+ at = @At("TAIL")
+ )
+ public void az_addIdentityComponent(CompoundTag compoundTag, CallbackInfo ci) {
+ var self = AzureLibUtil.self(this);
+ if (AzIdentityRegistry.hasIdentity(self.getItem()) && !compoundTag.hasUUID("az_id")) {
+ compoundTag.putUUID("az_id", UUID.randomUUID());
+ }
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java b/common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java
deleted file mode 100644
index fb5ade0ed..000000000
--- a/common/src/main/java/mod/azure/azurelib/mixins/NeoMixinHumanoidArmorLayer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package mod.azure.azurelib.mixins;
-
-
-import com.mojang.blaze3d.vertex.PoseStack;
-import mod.azure.azurelib.animatable.GeoItem;
-import mod.azure.azurelib.animatable.client.RenderProvider;
-import net.minecraft.client.model.HumanoidModel;
-import net.minecraft.client.model.Model;
-import net.minecraft.client.renderer.MultiBufferSource;
-import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer;
-import net.minecraft.world.entity.EquipmentSlot;
-import net.minecraft.world.entity.LivingEntity;
-import net.minecraft.world.item.ItemStack;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Unique;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.ModifyArg;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-/**
- * Render hook for injecting AzureLib's armor rendering functionalities
- */
-@Mixin(value = HumanoidArmorLayer.class, priority = 700)
-public class NeoMixinHumanoidArmorLayer> {
-
- @Unique
- private LivingEntity gl_storedEntity;
- @Unique
- private EquipmentSlot gl_storedSlot;
- @Unique
- private ItemStack gl_storedItemStack;
-
- @Inject(method = "renderArmorPiece", at = @At(value = "HEAD"))
- public void armorModelHook(PoseStack poseStack, MultiBufferSource source, T livingEntity, EquipmentSlot equipmentSlot, int i, A model, CallbackInfo ci) {
- this.gl_storedEntity = livingEntity;
- this.gl_storedSlot = equipmentSlot;
- this.gl_storedItemStack = livingEntity.getItemBySlot(equipmentSlot);
- }
-
- @ModifyArg(method = "renderArmorPiece", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;renderModel(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/item/ArmorItem;Lnet/minecraft/client/model/Model;ZFFFLnet/minecraft/resources/ResourceLocation;)V", remap = false), index = 4)
- public Model injectArmor(Model humanoidModel) {
- return this.gl_storedItemStack.getItem() instanceof GeoItem ? (A) RenderProvider.of(this.gl_storedItemStack).getGenericArmorModel(this.gl_storedEntity, this.gl_storedItemStack, this.gl_storedSlot, (HumanoidModel) humanoidModel) : humanoidModel; }
-}
\ No newline at end of file
diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java
index a26ed806a..7f51b6d9f 100644
--- a/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedBlockGeoModel.java
@@ -14,6 +14,7 @@
* {@link DefaultedGeoModel} specific to {@link net.minecraft.world.level.block.Block Blocks}.
* Using this class pre-sorts provided asset paths into the "block" subdirectory
*/
+@Deprecated(forRemoval = true)
public class DefaultedBlockGeoModel extends DefaultedGeoModel {
/**
* Create a new instance of this model class.
diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java
index 0b544435c..e5b0e0445 100644
--- a/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedEntityGeoModel.java
@@ -20,6 +20,7 @@
* Using this class pre-sorts provided asset paths into the "entity" subdirectory
* Additionally it can automatically handle head-turning if the entity has a "head" bone
*/
+@Deprecated(forRemoval = true)
public class DefaultedEntityGeoModel extends DefaultedGeoModel {
private final boolean turnsHead;
diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java
index 6b49f3eed..4924b9cb6 100644
--- a/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedGeoModel.java
@@ -15,6 +15,7 @@
* This class allows for minimal boilerplate when implementing basic models, and saves on new classes.
* Additionally, it encourages consistency and sorting of asset paths.
*/
+@Deprecated(forRemoval = true)
public abstract class DefaultedGeoModel extends GeoModel {
private ResourceLocation modelPath;
private ResourceLocation texturePath;
diff --git a/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java b/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java
index 87b3069b7..5efc325fc 100644
--- a/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/model/DefaultedItemGeoModel.java
@@ -14,6 +14,7 @@
* {@link DefaultedGeoModel} specific to {@link net.minecraft.world.item.Item Items}.
* Using this class pre-sorts provided asset paths into the "item" subdirectory
*/
+@Deprecated(forRemoval = true)
public class DefaultedItemGeoModel extends DefaultedGeoModel {
/**
* Create a new instance of this model class.
diff --git a/common/src/main/java/mod/azure/azurelib/model/GeoModel.java b/common/src/main/java/mod/azure/azurelib/model/GeoModel.java
index 97ec86927..af7c2aa7b 100644
--- a/common/src/main/java/mod/azure/azurelib/model/GeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/model/GeoModel.java
@@ -39,6 +39,7 @@
* Base class for all code-based model objects.
* All models to registered to a {@link GeoRenderer} should be an instance of this or one of its subclasses.
*/
+@Deprecated(forRemoval = true)
public abstract class GeoModel implements CoreGeoModel {
private final AnimationProcessor processor = new AnimationProcessor<>(this);
diff --git a/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java b/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java
index a55600e3d..50e30294c 100644
--- a/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java
+++ b/common/src/main/java/mod/azure/azurelib/model/data/EntityModelData.java
@@ -10,4 +10,5 @@
/**
* Container class for various pieces of data relating to a model's current state.
*/
+@Deprecated(forRemoval = true)
public record EntityModelData(boolean isSitting, boolean isChild, float netHeadYaw, float headPitch) {}
diff --git a/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java b/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java
index 50e6465d0..c3b159fa7 100644
--- a/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/SerializableDataTicket.java
@@ -15,6 +15,7 @@
* Network-compatible {@link mod.azure.azurelib.core.object.DataTicket} implementation.
* Used for sending data from server -> client in an easy manner
*/
+@Deprecated(forRemoval = true)
public abstract class SerializableDataTicket extends DataTicket {
protected SerializableDataTicket(String id, Class extends D> objectType) {
super(id, objectType);
diff --git a/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java b/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java
index ced8116e5..05e04b940 100644
--- a/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/api/IPacket.java
@@ -14,6 +14,7 @@
import net.minecraft.resources.ResourceLocation;
+@Deprecated(forRemoval = true)
public interface IPacket {
ResourceLocation getPacketId();
diff --git a/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java b/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java
index cd89125d5..20a8ebe00 100644
--- a/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java
+++ b/common/src/main/java/mod/azure/azurelib/network/api/IPacketDecoder.java
@@ -15,6 +15,7 @@
import net.minecraft.network.FriendlyByteBuf;
@FunctionalInterface
+@Deprecated(forRemoval = true)
public interface IPacketDecoder {
T decode(FriendlyByteBuf buffer);
diff --git a/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java b/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java
index b2b0296fc..f87f1fccd 100644
--- a/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java
+++ b/common/src/main/java/mod/azure/azurelib/network/api/IPacketEncoder.java
@@ -15,6 +15,7 @@
import net.minecraft.network.FriendlyByteBuf;
@FunctionalInterface
+@Deprecated(forRemoval = true)
public interface IPacketEncoder {
void encode(T data, FriendlyByteBuf buffer);
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java
index 0f1070d88..11a1fbea2 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AnimDataSyncPacket.java
@@ -16,6 +16,7 @@
* Packet for syncing user-definable animation data for
* {@link SingletonGeoAnimatable} instances
*/
+@Deprecated(forRemoval = true)
public class AnimDataSyncPacket extends AbstractPacket {
private final String syncableId;
private final long instanceId;
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java
index 4c62c67f6..31eabc6fe 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AnimTriggerPacket.java
@@ -12,6 +12,7 @@
* Packet for syncing user-definable animations that can be triggered from the
* server
*/
+@Deprecated(forRemoval = true)
public class AnimTriggerPacket extends AbstractPacket {
private final String syncableId;
private final long instanceId;
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java
new file mode 100644
index 000000000..a06daf2fb
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java
@@ -0,0 +1,74 @@
+package mod.azure.azurelib.network.packet;
+
+import mod.azure.azurelib.network.AbstractPacket;
+import mod.azure.azurelib.platform.services.AzureLibNetwork;
+import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor;
+import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide;
+import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand;
+import mod.azure.azurelib.util.ClientUtils;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.resources.ResourceLocation;
+
+public class AzBlockEntityDispatchCommandPacket extends AbstractPacket {
+
+ private final BlockPos blockPos;
+ private final AzCommand dispatchCommand;
+
+ public static final StreamCodec CODEC = StreamCodec.composite(
+ BlockPos.STREAM_CODEC,
+ AzBlockEntityDispatchCommandPacket::blockPos,
+ AzCommand.CODEC,
+ AzBlockEntityDispatchCommandPacket::dispatchCommand,
+ AzBlockEntityDispatchCommandPacket::new
+ );
+
+ public AzBlockEntityDispatchCommandPacket(BlockPos blockPos, AzCommand dispatchCommand) {
+ this.blockPos = blockPos;
+ this.dispatchCommand = dispatchCommand;
+ }
+
+ public BlockPos blockPos() {
+ return blockPos;
+ }
+
+ public AzCommand dispatchCommand() {
+ return dispatchCommand;
+ }
+
+ @Override
+ public void encode(FriendlyByteBuf buf) {
+ buf.writeBlockPos(this.blockPos);
+ AzCommand.encode(buf, dispatchCommand());
+ }
+
+ public static AzBlockEntityDispatchCommandPacket decode(FriendlyByteBuf buf) {
+ var blockPos = buf.readBlockPos();
+
+ // Deserialize the AzCommand
+ AzCommand command = AzCommand.decode(buf);
+
+ return new AzBlockEntityDispatchCommandPacket(blockPos, dispatchCommand());
+ }
+
+
+ @Override
+ public void handle() {
+ var blockEntity = ClientUtils.getLevel().getBlockEntity(blockPos);
+
+ if (blockEntity == null) {
+ return;
+ }
+
+ var animator = AzAnimatorAccessor.getOrNull(blockEntity);
+
+ if (animator != null) {
+ dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator));
+ }
+ }
+
+ @Override
+ public ResourceLocation getPacketID() {
+ return AzureLibNetwork.AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java
new file mode 100644
index 000000000..1b0a54939
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java
@@ -0,0 +1,48 @@
+package mod.azure.azurelib.network.packet;
+
+import mod.azure.azurelib.network.AbstractPacket;
+import mod.azure.azurelib.platform.services.AzureLibNetwork;
+import mod.azure.azurelib.rewrite.animation.AzAnimatorAccessor;
+import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide;
+import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand;
+import mod.azure.azurelib.util.ClientUtils;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.resources.ResourceLocation;
+
+public class AzEntityDispatchCommandPacket extends AbstractPacket {
+
+ private final int entityId;
+ private final AzCommand dispatchCommand;
+
+ public AzEntityDispatchCommandPacket(int entityId, AzCommand dispatchCommand) {
+ this.entityId = entityId;
+ this.dispatchCommand = dispatchCommand;
+ }
+
+ @Override
+ public void encode(FriendlyByteBuf buf) {
+ buf.writeInt(this.entityId);
+ // TODO: Fix this, as I don't believe this is correct.
+ //buf.writeByte(dispatchCommand.actions().size());
+ }
+
+ @Override
+ public void handle() {
+ var entity = ClientUtils.getLevel().getEntity(this.entityId);
+
+ if (entity == null) {
+ return;
+ }
+
+ var animator = AzAnimatorAccessor.getOrNull(entity);
+
+ if (animator != null) {
+ dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator));
+ }
+ }
+
+ @Override
+ public ResourceLocation getPacketID() {
+ return AzureLibNetwork.AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java
new file mode 100644
index 000000000..06694054a
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java
@@ -0,0 +1,43 @@
+package mod.azure.azurelib.network.packet;
+
+import mod.azure.azurelib.network.AbstractPacket;
+import mod.azure.azurelib.platform.services.AzureLibNetwork;
+import mod.azure.azurelib.rewrite.animation.cache.AzIdentifiableItemStackAnimatorCache;
+import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide;
+import mod.azure.azurelib.rewrite.animation.dispatch.command.AzCommand;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.resources.ResourceLocation;
+
+import java.util.UUID;
+
+public class AzItemStackDispatchCommandPacket extends AbstractPacket {
+
+ private final UUID itemStackId;
+ private final AzCommand dispatchCommand;
+
+ public AzItemStackDispatchCommandPacket(UUID itemStackId, AzCommand dispatchCommand) {
+ this.itemStackId = itemStackId;
+ this.dispatchCommand = dispatchCommand;
+ }
+
+ @Override
+ public void encode(FriendlyByteBuf buf) {
+ buf.writeUUID(this.itemStackId);
+ // TODO: Fix this, as I don't believe this is correct.
+ //buf.writeByte(dispatchCommand.actions().size());
+ }
+
+ @Override
+ public void handle() {
+ var animator = AzIdentifiableItemStackAnimatorCache.getInstance().getOrNull(itemStackId);
+
+ if (animator != null) {
+ dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator));
+ }
+ }
+
+ @Override
+ public ResourceLocation getPacketID() {
+ return AzureLibNetwork.AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java
index 4d4d69f5d..effa38cb0 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimDataSyncPacket.java
@@ -17,6 +17,7 @@
* Packet for syncing user-definable animation data for {@link BlockEntity
* BlockEntities}
*/
+@Deprecated(forRemoval = true)
public class BlockEntityAnimDataSyncPacket extends AbstractPacket {
private final BlockPos blockPos;
private final SerializableDataTicket dataTicket;
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java
index ad3992d7e..b6caa4fa8 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/BlockEntityAnimTriggerPacket.java
@@ -23,6 +23,7 @@
* server for {@link net.minecraft.world.level.block.entity.BlockEntity
* BlockEntities}
*/
+@Deprecated(forRemoval = true)
public class BlockEntityAnimTriggerPacket extends AbstractPacket {
private final BlockPos blockPos;
private final String controllerName;
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java
index f523bf6cc..dcbdba4ad 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimDataSyncPacket.java
@@ -14,6 +14,7 @@
* Packet for syncing user-definable animation data for
* {@link net.minecraft.world.entity.Entity Entities}
*/
+@Deprecated(forRemoval = true)
public class EntityAnimDataSyncPacket extends AbstractPacket {
private final int entityId;
private final SerializableDataTicket dataTicket;
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java
index 7c80cc8e4..38fdcf7ba 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityAnimTriggerPacket.java
@@ -23,6 +23,7 @@
* Packet for syncing user-definable animations that can be triggered from the
* server for {@link net.minecraft.world.entity.Entity Entities}
*/
+@Deprecated(forRemoval = true)
public class EntityAnimTriggerPacket extends AbstractPacket {
private final int entityId;
private final boolean isReplacedEntity;
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java
index add06ae69..133bbd26c 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacket.java
@@ -5,6 +5,7 @@
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.world.entity.Entity;
+@Deprecated(forRemoval = true)
public class EntityPacket {
public static Packet createPacket(Entity entity) {
return Services.NETWORK.createPacket(entity);
diff --git a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java
index f6d0aca0f..fedab15f2 100644
--- a/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/EntityPacketOnClient.java
@@ -9,6 +9,7 @@
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
+@Deprecated(forRemoval = true)
public class EntityPacketOnClient {
public static void onPacket(Minecraft context, FriendlyByteBuf byteBuf) {
EntityType> type = BuiltInRegistries.ENTITY_TYPE.byId(byteBuf.readVarInt());
diff --git a/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java b/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java
index 981da9281..725978cce 100644
--- a/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java
+++ b/common/src/main/java/mod/azure/azurelib/platform/services/AzureLibNetwork.java
@@ -20,23 +20,46 @@ class LockHolder { // Package private class
}
public interface AzureLibNetwork {
+ @Deprecated(forRemoval = true)
ResourceLocation ANIM_DATA_SYNC_PACKET_ID = AzureLib.modResource("anim_data_sync");
+ @Deprecated(forRemoval = true)
ResourceLocation ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("anim_trigger_sync");
+ @Deprecated(forRemoval = true)
ResourceLocation ENTITY_ANIM_DATA_SYNC_PACKET_ID = AzureLib.modResource("entity_anim_data_sync");
+ @Deprecated(forRemoval = true)
ResourceLocation ENTITY_ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("entity_anim_trigger_sync");
+ @Deprecated(forRemoval = true)
ResourceLocation BLOCK_ENTITY_ANIM_DATA_SYNC_PACKET_ID = AzureLib.modResource("block_entity_anim_data_sync");
+ @Deprecated(forRemoval = true)
ResourceLocation BLOCK_ENTITY_ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("block_entity_anim_trigger_sync");
+ @Deprecated(forRemoval = true)
ResourceLocation CUSTOM_ENTITY_ID = AzureLib.modResource("spawn_entity");
+ @Deprecated(forRemoval = true)
Map SYNCED_ANIMATABLES = new Object2ObjectOpenHashMap<>();
+ ResourceLocation AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID = AzureLib.modResource(
+ "az_blockentity_dispatch_command_sync"
+ );
+
+ ResourceLocation AZ_ENTITY_ANIM_TRIGGER_SYNC_PACKET_ID = AzureLib.modResource("az_entity_anim_trigger_sync");
+
+ ResourceLocation AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID = AzureLib.modResource(
+ "az_entity_dispatch_command_sync"
+ );
+
+ ResourceLocation AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID = AzureLib.modResource(
+ "az_item_stack_dispatch_command_sync"
+ );
+
/**
* Registers a synced {@link GeoAnimatable} object for networking support.
* It is recommended that you don't call this directly, instead implementing and calling {@link mod.azure.azurelib.animatable.SingletonGeoAnimatable#registerSyncedAnimatable}
*/
+ @Deprecated(forRemoval = true)
default void registerSyncedAnimatable(GeoAnimatable animatable) {
synchronized (this) {
GeoAnimatable existing = SYNCED_ANIMATABLES.put(animatable.getClass().toString(), animatable);
@@ -46,11 +69,13 @@ default void registerSyncedAnimatable(GeoAnimatable animatable) {
}
}
+ @Deprecated(forRemoval = true)
Packet createPacket(Entity entity);
/**
* Used to register packets that the server sends
**/
+ @Deprecated(forRemoval = true)
void registerClientReceiverPackets();
void sendToTrackingEntityAndSelf(AbstractPacket packet, Entity entityToTrack);
@@ -71,6 +96,7 @@ interface IPacketCallback {
* @param className the className
*/
@Nullable
+ @Deprecated(forRemoval = true)
static GeoAnimatable getSyncedAnimatable(String className) {
GeoAnimatable animatable = SYNCED_ANIMATABLES.get(className);
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java
index 7bf7e55c5..976a2ac43 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/DyeableGeoArmorRenderer.java
@@ -26,6 +26,7 @@
/**
* A dyeable armour renderer for AzureLib armor models.
*/
+@Deprecated(forRemoval = true)
public abstract class DyeableGeoArmorRenderer extends GeoArmorRenderer {
protected final Set dyeableBones = new ObjectArraySet<>();
protected BakedGeoModel lastModel = null;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java
index e401e5287..f4c26bc52 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/DynamicGeoEntityRenderer.java
@@ -38,6 +38,7 @@
* Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily,
* and consider whether the benefits are worth the cost for your needs.
*/
+@Deprecated(forRemoval = true)
public abstract class DynamicGeoEntityRenderer extends GeoEntityRenderer {
protected static final Map TEXTURE_DIMENSIONS_CACHE = new Object2ObjectOpenHashMap<>();
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java
index 228a0d1ca..4a011471c 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRenderer.java
@@ -48,6 +48,7 @@
* @see GeoItem
* @param
*/
+@Deprecated(forRemoval = true)
public class GeoArmorRenderer extends HumanoidModel implements GeoRenderer {
protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this);
protected final GeoModel model;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java
index 1b11a15f0..dff2461fb 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoArmorRendererConstants.java
@@ -6,6 +6,7 @@
/**
* @author Boston Vanseghi
*/
+@Deprecated(forRemoval = true)
public class GeoArmorRendererConstants {
public static final String BONE_ARMOR_BODY_NAME = "armorBody";
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java
index 639812ae7..e9889effa 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoBlockRenderer.java
@@ -43,6 +43,7 @@
* Base {@link GeoRenderer} class for rendering {@link BlockEntity Blocks} specifically.
* All blocks added to be rendered by AzureLib should use an instance of this class.
*/
+@Deprecated(forRemoval = true)
public class GeoBlockRenderer implements GeoRenderer, BlockEntityRenderer {
protected final GeoModel model;
protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this);
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java
index c5364d92d..6dc0e42cc 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoEntityRenderer.java
@@ -50,6 +50,7 @@
* All entities added to be rendered by AzureLib should use an instance of this class.
* This also includes {@link net.minecraft.world.entity.projectile.Projectile Projectiles}
*/
+@Deprecated(forRemoval = true)
public class GeoEntityRenderer extends EntityRenderer implements GeoRenderer {
protected final List> renderLayers = new ObjectArrayList<>();
protected final GeoModel model;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java
index fbf6d944f..46b367217 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoItemRenderer.java
@@ -46,6 +46,7 @@
* Base {@link GeoRenderer} class for rendering {@link Item Items} specifically.
* All items added to be rendered by AzureLib should use an instance of this class.
*/
+@Deprecated(forRemoval = true)
public class GeoItemRenderer extends BlockEntityWithoutLevelRenderer implements GeoRenderer {
protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this);
protected final GeoModel model;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java
index c3a63833e..7405c6558 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoObjectRenderer.java
@@ -38,6 +38,7 @@
*
* It is strongly recommended you override {@link GeoRenderer#getInstanceId} if using this renderer
*/
+@Deprecated(forRemoval = true)
public class GeoObjectRenderer implements GeoRenderer {
protected final GeoRenderLayersContainer renderLayers = new GeoRenderLayersContainer<>(this);
protected final GeoModel model;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java
index 93675ac24..e5396ea56 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoRenderer.java
@@ -33,6 +33,7 @@
/**
* Base interface for all AzureLib renderers.
*/
+@Deprecated(forRemoval = true)
public interface GeoRenderer {
/**
* Gets the model instance for this renderer
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java
index f1e964f0d..1b3022aa9 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/GeoReplacedEntityRenderer.java
@@ -49,6 +49,7 @@
/**
* An alternate to {@link GeoEntityRenderer}, used specifically for replacing existing non-AzureLib entities with AzureLib rendering dynamically, without the need for an additional entity class
*/
+@Deprecated(forRemoval = true)
public class GeoReplacedEntityRenderer extends EntityRenderer implements GeoRenderer {
protected final GeoModel model;
protected final List> renderLayers = new ObjectArrayList<>();
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java
index 033d8a48f..09f02c8fb 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoArmorRenderer.java
@@ -33,6 +33,7 @@
* Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider
* whether the benefits are worth the cost for your needs.
*/
+@Deprecated(forRemoval = true)
public abstract class DynamicGeoArmorRenderer extends GeoArmorRenderer {
protected static final Map TEXTURE_DIMENSIONS_CACHE =
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java
index 3caab70e3..ea7ba3eba 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoBlockRenderer.java
@@ -32,6 +32,7 @@
* Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider
* whether the benefits are worth the cost for your needs.
*/
+@Deprecated(forRemoval = true)
public abstract class DynamicGeoBlockRenderer extends GeoBlockRenderer {
protected static final Map TEXTURE_DIMENSIONS_CACHE =
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java
index c1536f279..71f9de7cf 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoItemRenderer.java
@@ -32,6 +32,7 @@
* Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider
* whether the benefits are worth the cost for your needs.
*/
+@Deprecated(forRemoval = true)
public abstract class DynamicGeoItemRenderer extends GeoItemRenderer {
protected static final Map TEXTURE_DIMENSIONS_CACHE =
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java
index 530fd8eb6..1db0ad195 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoObjectRenderer.java
@@ -31,6 +31,7 @@
* Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider
* whether the benefits are worth the cost for your needs.
*/
+@Deprecated(forRemoval = true)
public abstract class DynamicGeoObjectRenderer extends GeoObjectRenderer {
protected static final Map TEXTURE_DIMENSIONS_CACHE =
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java
index c46c2b2db..83de8e986 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/dynamic/DynamicGeoReplacedEntityRenderer.java
@@ -33,6 +33,7 @@
* Because of the extra performance cost of this renderer, it is advised to avoid using it unnecessarily, and consider
* whether the benefits are worth the cost for your needs.
*/
+@Deprecated(forRemoval = true)
public abstract class DynamicGeoReplacedEntityRenderer extends GeoReplacedEntityRenderer {
protected static final Map TEXTURE_DIMENSIONS_CACHE =
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java
index a2d7ca3ab..6303a0e04 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/AutoGlowingGeoLayer.java
@@ -22,6 +22,7 @@
/**
* {@link GeoRenderLayer} for rendering the auto-generated glowlayer functionality implemented by AzureLib using the _glowing appendixed texture files.
*/
+@Deprecated(forRemoval = true)
public class AutoGlowingGeoLayer extends GeoRenderLayer {
public AutoGlowingGeoLayer(GeoRenderer renderer) {
super(renderer);
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java
index 55ec94608..8b6bb0850 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/BlockAndItemGeoLayer.java
@@ -30,6 +30,7 @@
/**
* {@link GeoRenderLayer} for rendering {@link net.minecraft.world.level.block.state.BlockState BlockStates} or {@link net.minecraft.world.item.ItemStack ItemStacks} on a given {@link GeoAnimatable}
*/
+@Deprecated(forRemoval = true)
public class BlockAndItemGeoLayer extends GeoRenderLayer {
protected final BiFunction stackForBone;
protected final BiFunction blockForBone;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java
index e55bf3b64..f5f3b2e38 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/BoneFilterGeoLayer.java
@@ -24,6 +24,7 @@
*
* NOTE: Despite this layer existing, it is much more efficient to use {@link FastBoneFilterGeoLayer} instead
*/
+@Deprecated(forRemoval = true)
public class BoneFilterGeoLayer extends GeoRenderLayer {
protected final TriConsumer checkAndApply;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java
index b4adead5b..b01e37b23 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/FastBoneFilterGeoLayer.java
@@ -26,6 +26,7 @@
* This version requires you provide the list of bones to filter up-front,
* so that the bone hierarchy doesn't need to be traversed.
*/
+@Deprecated(forRemoval = true)
public class FastBoneFilterGeoLayer extends BoneFilterGeoLayer {
protected final Supplier> boneSupplier;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java
index 85f66b29a..042a2a816 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayer.java
@@ -23,6 +23,7 @@
* Render layer base class for rendering additional layers of effects or textures over an existing model at runtime.
* Contains the base boilerplate and helper code for various render layer features
*/
+@Deprecated(forRemoval = true)
public abstract class GeoRenderLayer {
protected final GeoRenderer renderer;
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java
index a4888c7ca..23c247507 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/GeoRenderLayersContainer.java
@@ -17,6 +17,7 @@
* Base interface for a container for {@link GeoRenderLayer GeoRenderLayers}
* Each renderer should contain an instance of this, for holding its layers and handling events.
*/
+@Deprecated(forRemoval = true)
public class GeoRenderLayersContainer {
private final GeoRenderer renderer;
private final List> layers = new ObjectArrayList<>();
diff --git a/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java b/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java
index d5517d844..213c09fcf 100644
--- a/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java
+++ b/common/src/main/java/mod/azure/azurelib/renderer/layer/ItemArmorGeoLayer.java
@@ -9,7 +9,6 @@
import java.util.Map;
-import mod.azure.azurelib.platform.Services;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -60,6 +59,7 @@
* Supports both {@link mod.azure.azurelib.animatable.GeoItem AzureLib} and {@link net.minecraft.world.item.ArmorItem Vanilla} armor models.
* Unlike a traditional armor renderer, this renderer renders per-bone, giving much more flexible armor rendering.
*/
+@Deprecated(forRemoval = true)
public class ItemArmorGeoLayer extends GeoRenderLayer {
protected static final Map ARMOR_PATH_CACHE = new Object2ObjectOpenHashMap<>();
protected static final HumanoidModel INNER_ARMOR_MODEL = new HumanoidModel<>(Minecraft.getInstance().getEntityModels().bakeLayer(ModelLayers.PLAYER_INNER_ARMOR));
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java
new file mode 100644
index 000000000..731f066cc
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/AzResourceCache.java
@@ -0,0 +1,59 @@
+package mod.azure.azurelib.rewrite;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.packs.resources.ResourceManager;
+
+import java.util.Locale;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+/**
+ * AzResourceCache is an abstract base class designed for managing and loading mod resources asynchronously. This class
+ * provides helper functions for loading and processing resource files of a specific type and storing them in a cache.
+ */
+public abstract class AzResourceCache {
+
+ private static final Set EXCLUDED_NAMESPACES = ObjectOpenHashSet.of(
+ "moreplayermodels",
+ "customnpcs",
+ "creeperoverhaul",
+ "geckolib",
+ "gunsrpg",
+ "born_in_chaos_v1",
+ "neoforge"
+ );
+
+ protected final CompletableFuture loadResources(
+ Executor executor,
+ ResourceManager resourceManager,
+ String type,
+ Function loader,
+ BiConsumer map
+ ) {
+ return CompletableFuture.supplyAsync(
+ () -> resourceManager.listResources(type, fileName -> fileName.toString().endsWith(".json")),
+ executor
+ )
+ .thenApplyAsync(resources -> {
+ var tasks = new Object2ObjectOpenHashMap>();
+
+ for (var resource : resources.keySet()) {
+ tasks.put(resource, CompletableFuture.supplyAsync(() -> loader.apply(resource), executor));
+ }
+
+ return tasks;
+ }, executor)
+ .thenAcceptAsync(tasks -> {
+ for (var entry : tasks.entrySet()) {
+ if (!EXCLUDED_NAMESPACES.contains(entry.getKey().getNamespace().toLowerCase(Locale.ROOT))) {
+ map.accept(entry.getKey(), entry.getValue().join());
+ }
+ }
+ }, executor);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java
new file mode 100644
index 000000000..1d22bacb5
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationContext.java
@@ -0,0 +1,71 @@
+package mod.azure.azurelib.rewrite.animation;
+
+import mod.azure.azurelib.rewrite.animation.cache.AzBoneCache;
+
+/**
+ * The {@code AzAnimationContext} class provides a context for managing animation-related state and behaviors for
+ * animatable objects of type {@code T}. It serves as the central point of interaction between the animation system,
+ * configuration settings, and the animated object itself.
+ *
+ * @param The type of the animatable object that this context operates on.
+ */
+public class AzAnimationContext {
+
+ private final AzBoneCache boneCache;
+
+ private final AzAnimatorConfig config;
+
+ private final AzAnimationTimer timer;
+
+ // Package-private for mutability purposes.
+ T animatable;
+
+ public AzAnimationContext(
+ AzBoneCache boneCache,
+ AzAnimatorConfig config,
+ AzAnimationTimer timer
+ ) {
+ this.boneCache = boneCache;
+ this.config = config;
+ this.timer = timer;
+ }
+
+ /**
+ * Returns the current animatable instance associated with this animation context.
+ *
+ * @return The animatable instance of type {@code T}.
+ */
+ public T animatable() {
+ return animatable;
+ }
+
+ /**
+ * Returns the bone cache associated with the animation context. The bone cache is responsible for storing and
+ * managing bone-related data and transformations used during animations.
+ *
+ * @return The {@link AzBoneCache} instance managing bone data and transformations for animations.
+ */
+ public AzBoneCache boneCache() {
+ return boneCache;
+ }
+
+ /**
+ * Returns the animation configuration associated with this animation context. The configuration defines behavior
+ * such as bone reset speed, handling of missing bones, and whether animations should play while the game is paused.
+ *
+ * @return The {@link AzAnimatorConfig} instance containing the animation settings for this context.
+ */
+ public AzAnimatorConfig config() {
+ return config;
+ }
+
+ /**
+ * Returns the animation timer associated with the animation context. The timer is used to track animation progress
+ * over time and manage timing adjustments based on game state, such as pausing and resuming.
+ *
+ * @return The {@link AzAnimationTimer} instance responsible for animation timing.
+ */
+ public AzAnimationTimer timer() {
+ return timer;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java
new file mode 100644
index 000000000..7a60686b7
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimationTimer.java
@@ -0,0 +1,91 @@
+package mod.azure.azurelib.rewrite.animation;
+
+import mod.azure.azurelib.util.RenderUtils;
+import net.minecraft.client.Minecraft;
+
+/**
+ * AzAnimationTimer is responsible for managing animation progression based on game events and time deltas. It keeps
+ * track of the current animation time and ensures smooth transitions during various game states, such as pausing and
+ * resuming.
+ * The class relies on the provided {@link AzAnimatorConfig} for configurable behaviors, such as whether animations
+ * continue during game pauses or specific error handling preferences.
+ */
+public class AzAnimationTimer {
+
+ private final AzAnimatorConfig config;
+
+ // Remnants from GeoModel.
+ private double animTime;
+
+ private double lastGameTickTime;
+
+ private boolean wasPausedLastFrame;
+
+ /**
+ * Constructs a new instance of AzAnimationTimer with the given configuration.
+ *
+ * @param config The configuration settings used to configure the animation timer. It includes parameters such as
+ * bone reset time, behavior during game pause, and whether to crash if a bone is missing.
+ */
+ public AzAnimationTimer(AzAnimatorConfig config) {
+ this.config = config;
+ }
+
+ /**
+ * Updates the animation timer by calculating the time delta since the last frame and applying it to the internal
+ * animation time. This method handles game pause states and adjusts the time calculations accordingly.
+ * Behavior: If the game is paused:
+ *
+ * - Sets an internal flag to indicate the paused state.
+ * - Returns immediately if animations should not play while paused.
+ *
+ * If transitioning from paused to unpaused:
+ *
+ * - Resets the frame delta to prevent large time skips in animations.
+ *
+ * Accumulates the computed time delta into the animation time tracker to control the progression of animations.
+ */
+ public void tick() {
+ var minecraft = Minecraft.getInstance();
+ var currentRenderTick = RenderUtils.getCurrentTick();
+
+ if (minecraft.isPaused()) {
+ if (!wasPausedLastFrame) {
+ // If this is the first frame of the game pause time, we need to set a flag.
+ this.wasPausedLastFrame = true;
+ }
+
+ if (!config.shouldPlayAnimationsWhileGamePaused()) {
+ // If we cannot play animations while the game is paused, then return early.
+ return;
+ }
+ }
+
+ // Compute the delta render tick for this frame. This calculation by default does not account for the game
+ // pause state, which means that the difference here could be massive by the time the player unpauses.
+ var deltaRenderTick = currentRenderTick - lastGameTickTime;
+
+ if (wasPausedLastFrame && !minecraft.isPaused()) {
+ // If this is the first frame of the game play time, we need to set a flag and adjust the deltaRenderTick.
+ this.wasPausedLastFrame = false;
+ // To account for the deltaRenderTick being massive on exiting the game pause state, we simply set
+ // it to 0. This will result in no difference being added to animTime, allowing animations to
+ // continue right where it left off.
+ deltaRenderTick = 0;
+ }
+
+ // Add the deltaRenderTick to animTime. animTime is what controls the progress of animations.
+ this.animTime += deltaRenderTick;
+ this.lastGameTickTime = currentRenderTick;
+ }
+
+ /**
+ * Retrieves the current animation time.
+ *
+ * @return The current animation time as a double value, representing the accumulated time used for the progression
+ * of animations.
+ */
+ public double getAnimTime() {
+ return animTime;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java
new file mode 100644
index 000000000..bc707291d
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java
@@ -0,0 +1,134 @@
+package mod.azure.azurelib.rewrite.animation;
+
+import mod.azure.azurelib.AzureLibException;
+import mod.azure.azurelib.core.molang.MolangParser;
+import mod.azure.azurelib.core.molang.MolangQueries;
+import mod.azure.azurelib.rewrite.animation.cache.AzBakedAnimationCache;
+import mod.azure.azurelib.rewrite.animation.cache.AzBoneCache;
+import mod.azure.azurelib.rewrite.animation.controller.AzAnimationControllerContainer;
+import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimation;
+import mod.azure.azurelib.rewrite.model.AzBakedModel;
+import net.minecraft.client.Minecraft;
+import net.minecraft.resources.ResourceLocation;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * The {@code AzAnimator} class is an abstract base class for managing animations for various types of objects such as
+ * entities, blocks, or items. It provides a reusable structure for animating objects, allowing the integration of a
+ * variety of animation controllers and custom animations.
+ *
+ * @param The type of object this animator will animate (e.g., an entity, block entity, or item stack).
+ */
+public abstract class AzAnimator {
+
+ private final AzAnimationContext reusableContext;
+
+ // Holds animation controllers.
+ private final AzAnimationControllerContainer animationControllerContainer;
+
+ public boolean reloadAnimations;
+
+ protected AzAnimator() {
+ this(AzAnimatorConfig.defaultConfig());
+ }
+
+ protected AzAnimator(AzAnimatorConfig config) {
+ this.animationControllerContainer = new AzAnimationControllerContainer<>();
+
+ var boneCache = new AzBoneCache();
+ var timer = new AzAnimationTimer(config);
+
+ this.reusableContext = new AzAnimationContext<>(boneCache, config, timer);
+ }
+
+ public abstract void registerControllers(AzAnimationControllerContainer animationControllerContainer);
+
+ public abstract @NotNull ResourceLocation getAnimationLocation(T animatable);
+
+ public void animate(T animatable, float partialTicks) {
+ reusableContext.animatable = animatable;
+
+ var boneCache = reusableContext.boneCache();
+ var timer = reusableContext.timer();
+
+ timer.tick();
+
+ preAnimationSetup(animatable, timer.getAnimTime());
+
+ if (!boneCache.isEmpty()) {
+
+ for (var controller : animationControllerContainer.getAll()) {
+ controller.update();
+ }
+
+ this.reloadAnimations = false;
+
+ boneCache.update(reusableContext);
+ }
+
+ setCustomAnimations(animatable, partialTicks);
+ }
+
+ /**
+ * Apply transformations and settings prior to acting on any animation-related functionality
+ */
+ protected void preAnimationSetup(T animatable, double animTime) {
+ applyMolangQueries(animatable, animTime);
+ }
+
+ protected void applyMolangQueries(T animatable, double animTime) {
+ var level = Objects.requireNonNull(Minecraft.getInstance().level);
+ var parser = MolangParser.INSTANCE;
+
+ parser.setMemoizedValue(MolangQueries.LIFE_TIME, () -> animTime / 20d);
+ parser.setMemoizedValue(MolangQueries.ACTOR_COUNT, level::getEntityCount);
+ parser.setMemoizedValue(MolangQueries.TIME_OF_DAY, () -> level.getDayTime() / 24000f);
+ parser.setMemoizedValue(MolangQueries.MOON_PHASE, level::getMoonPhase);
+ }
+
+ /**
+ * Sets custom animations for the given animatable object. This method is used to define and configure specific
+ * animations unique to the context of the animatable and the current render state.
+ *
+ * @param animatable The object for which custom animations are being set.
+ * @param partialTicks The partial tick time used for interpolating animations smoothly between frames.
+ */
+ public void setCustomAnimations(T animatable, float partialTicks) {}
+
+ public void setActiveModel(AzBakedModel model) {
+ var modelChanged = reusableContext.boneCache().setActiveModel(model);
+
+ if (modelChanged) {
+ // If the model changed, we need to clear the bone animation queue cache for every controller.
+ // TODO: We shouldn't have to remember to do this. If the baked model changes, then the bone cache
+ // should be re-instantiated. If the bone cache is re-instantiated, then so should the bone animation
+ // queue caches.
+ animationControllerContainer.getAll()
+ .forEach(controller -> controller.boneAnimationQueueCache().clear());
+ }
+ }
+
+ /**
+ * Get the baked animation object used for rendering from the given resource path
+ */
+ public AzBakedAnimation getAnimation(T animatable, String name) {
+ var location = getAnimationLocation(animatable);
+ var bakedAnimations = AzBakedAnimationCache.getInstance().getNullable(location);
+
+ if (bakedAnimations == null) {
+ throw new AzureLibException(location, "Unable to find animation.");
+ }
+
+ return bakedAnimations.getAnimation(name);
+ }
+
+ public AzAnimationContext context() {
+ return reusableContext;
+ }
+
+ public AzAnimationControllerContainer getAnimationControllerContainer() {
+ return animationControllerContainer;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java
new file mode 100644
index 000000000..afa249885
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorAccessor.java
@@ -0,0 +1,37 @@
+package mod.azure.azurelib.rewrite.animation;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Optional;
+
+/**
+ * The {@code AzAnimatorAccessor} interface provides a mechanism to associate and manage an {@link AzAnimator} instance
+ * with a target object. This enables retrieval and manipulation of animator instances that are specific to the target
+ * object.
+ *
+ * @param The type of the target object that the animator applies to.
+ */
+public interface AzAnimatorAccessor {
+
+ @Nullable
+ AzAnimator getAnimatorOrNull();
+
+ void setAnimator(AzAnimator animator);
+
+ default Optional> getAnimator() {
+ return Optional.ofNullable(getAnimatorOrNull());
+ }
+
+ @SuppressWarnings("unchecked")
+ static AzAnimatorAccessor cast(T target) {
+ return (AzAnimatorAccessor) target;
+ }
+
+ static AzAnimator getOrNull(T target) {
+ return cast(target).getAnimatorOrNull();
+ }
+
+ static Optional> get(T target) {
+ return Optional.ofNullable(getOrNull(target));
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java
new file mode 100644
index 000000000..1ab8fa9da
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimatorConfig.java
@@ -0,0 +1,108 @@
+package mod.azure.azurelib.rewrite.animation;
+
+/**
+ * The {@code AzAnimatorConfig} record encapsulates configuration settings related to the animation system of the
+ * AzureLib framework. It provides customizable options for controlling animation behavior and error handling. This
+ * configuration is used to determine runtime behaviors such as whether animations should continue while the game is
+ * paused, whether the system should throw an error if a bone in the animation structure is missing, and the duration it
+ * takes to reset bone transformations.
+ *
+ * @param boneResetTime The specified time duration (in ticks or seconds) for resetting bones to
+ * their default transformations when animations are interrupted.
+ * @param crashIfBoneMissing Specifies whether the system will throw an exception if an expected bone
+ * in the animation is not found during runtime.
+ * @param shouldPlayAnimationsWhileGamePaused Indicates whether animations should continue playing when the game is
+ * paused.
+ */
+public record AzAnimatorConfig(
+ double boneResetTime,
+ boolean crashIfBoneMissing,
+ boolean shouldPlayAnimationsWhileGamePaused
+) {
+
+ /**
+ * Creates a new instance of the {@link Builder} to configure and build an {@code AzAnimatorConfig}.
+ *
+ * @return A new {@code Builder} instance for constructing an {@code AzAnimatorConfig}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns a default {@link AzAnimatorConfig} instance with predefined settings. The default configuration typically
+ * includes settings such as:
+ *
+ * - A bone reset time of 1 tick/second.
+ * - Disabling the feature to crash if a bone is missing.
+ * - Disabling animations while the game is paused.
+ *
+ *
+ * @return The default configuration instance of {@code AzAnimatorConfig}, built with default values.
+ */
+ public static AzAnimatorConfig defaultConfig() {
+ return builder().build();
+ }
+
+ public static class Builder {
+
+ private double boneResetTime;
+
+ private boolean crashIfBoneMissing;
+
+ private boolean shouldPlayAnimationsWhileGamePaused;
+
+ private Builder() {
+ this.boneResetTime = 1;
+ this.crashIfBoneMissing = false;
+ this.shouldPlayAnimationsWhileGamePaused = false;
+ }
+
+ /**
+ * Configures the builder to crash if a required bone is missing during animation setup.
+ *
+ * @return The current Builder instance for method chaining.
+ */
+ public Builder crashIfBoneMissing() {
+ this.crashIfBoneMissing = true;
+ return this;
+ }
+
+ /**
+ * Configures the builder to enable animations to play even when the game is paused.
+ *
+ * @return The current Builder instance for method chaining.
+ */
+ public Builder shouldPlayAnimationsWhileGamePaused() {
+ this.shouldPlayAnimationsWhileGamePaused = true;
+ return this;
+ }
+
+ /**
+ * Sets the bone reset time duration. This value determines how long it takes to reset bones to their default
+ * state after an animation is completed.
+ *
+ * @param boneResetTime The duration (in seconds) for bone reset time.
+ * @return The current Builder instance to allow method chaining.
+ */
+ public Builder withBoneResetTime(double boneResetTime) {
+ this.boneResetTime = boneResetTime;
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link AzAnimatorConfig} instance with the specified configuration parameters defined in the
+ * Builder. The configuration includes options for bone reset timing, error handling when bones are missing, and
+ * animation playback behavior during game pause state.
+ *
+ * @return A new {@code AzAnimatorConfig} instance containing the configured settings.
+ */
+ public AzAnimatorConfig build() {
+ return new AzAnimatorConfig(
+ boneResetTime,
+ crashIfBoneMissing,
+ shouldPlayAnimationsWhileGamePaused
+ );
+ }
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java
new file mode 100644
index 000000000..16b10fad2
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzBoneAnimationUpdateUtil.java
@@ -0,0 +1,107 @@
+package mod.azure.azurelib.rewrite.animation;
+
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzBoneAnimationQueue;
+import mod.azure.azurelib.rewrite.animation.easing.AzEasingType;
+import mod.azure.azurelib.rewrite.animation.easing.AzEasingUtil;
+import mod.azure.azurelib.rewrite.model.AzBone;
+import mod.azure.azurelib.rewrite.model.AzBoneSnapshot;
+
+public class AzBoneAnimationUpdateUtil {
+
+ /**
+ * Updates the position of the given bone by interpolating the position values from the animation queue and applying
+ * the specified easing type. The method also updates the snapshot offsets and flags the bone's position as changed.
+ *
+ * @param boneAnimation The animation queue containing position data for the bone.
+ * @param bone The bone whose position is being updated.
+ * @param easingType The easing type used for interpolating the position values.
+ * @param snapshot The snapshot used to store the updated position offsets and start animations.
+ */
+ public static void updatePositions(
+ AzBoneAnimationQueue boneAnimation,
+ AzBone bone,
+ AzEasingType easingType,
+ AzBoneSnapshot snapshot
+ ) {
+ var posXPoint = boneAnimation.positionXQueue().poll();
+ var posYPoint = boneAnimation.positionYQueue().poll();
+ var posZPoint = boneAnimation.positionZQueue().poll();
+
+ if (posXPoint != null && posYPoint != null && posZPoint != null) {
+ bone.setPosX((float) AzEasingUtil.lerpWithOverride(posXPoint, easingType));
+ bone.setPosY((float) AzEasingUtil.lerpWithOverride(posYPoint, easingType));
+ bone.setPosZ((float) AzEasingUtil.lerpWithOverride(posZPoint, easingType));
+ snapshot.updateOffset(bone.getPosX(), bone.getPosY(), bone.getPosZ());
+ snapshot.startPosAnim();
+ bone.markPositionAsChanged();
+ }
+ }
+
+ /**
+ * Updates the rotation of the specified bone by interpolating the rotation values from the animation queue and
+ * applying the specified easing type. The method also updates the snapshot rotation values, starts the rotation
+ * animation, and marks the bone's rotation as changed.
+ *
+ * @param boneAnimation The animation queue containing rotation data for the bone.
+ * @param bone The bone whose rotation is being updated.
+ * @param easingType The easing type used for interpolating the rotation values.
+ * @param initialSnapshot The initial snapshot containing the original rotation offsets.
+ * @param snapshot The snapshot used to store the updated rotation values and start animations.
+ */
+ public static void updateRotations(
+ AzBoneAnimationQueue boneAnimation,
+ AzBone bone,
+ AzEasingType easingType,
+ AzBoneSnapshot initialSnapshot,
+ AzBoneSnapshot snapshot
+ ) {
+ var rotXPoint = boneAnimation.rotationXQueue().poll();
+ var rotYPoint = boneAnimation.rotationYQueue().poll();
+ var rotZPoint = boneAnimation.rotationZQueue().poll();
+
+ if (rotXPoint != null && rotYPoint != null && rotZPoint != null) {
+ bone.setRotX(
+ (float) AzEasingUtil.lerpWithOverride(rotXPoint, easingType) + initialSnapshot.getRotX()
+ );
+ bone.setRotY(
+ (float) AzEasingUtil.lerpWithOverride(rotYPoint, easingType) + initialSnapshot.getRotY()
+ );
+ bone.setRotZ(
+ (float) AzEasingUtil.lerpWithOverride(rotZPoint, easingType) + initialSnapshot.getRotZ()
+ );
+ snapshot.updateRotation(bone.getRotX(), bone.getRotY(), bone.getRotZ());
+ snapshot.startRotAnim();
+ bone.markRotationAsChanged();
+ }
+ }
+
+ /**
+ * Updates the scale of the specified bone by interpolating the scale values from the animation queue and applying
+ * the specified easing type. The method also updates the snapshot with the new scale values, starts the scale
+ * animation, and marks the bone's scale as changed.
+ *
+ * @param boneAnimation The animation queue containing scale data for the bone.
+ * @param bone The bone whose scale is being updated.
+ * @param easingType The easing type used for interpolating the scale values.
+ * @param snapshot The snapshot used to store the updated scale values and start animations.
+ */
+ public static void updateScale(
+ AzBoneAnimationQueue boneAnimation,
+ AzBone bone,
+ AzEasingType easingType,
+ AzBoneSnapshot snapshot
+ ) {
+ var scaleXPoint = boneAnimation.scaleXQueue().poll();
+ var scaleYPoint = boneAnimation.scaleYQueue().poll();
+ var scaleZPoint = boneAnimation.scaleZQueue().poll();
+
+ if (scaleXPoint != null && scaleYPoint != null && scaleZPoint != null) {
+ bone.setScaleX((float) AzEasingUtil.lerpWithOverride(scaleXPoint, easingType));
+ bone.setScaleY((float) AzEasingUtil.lerpWithOverride(scaleYPoint, easingType));
+ bone.setScaleZ((float) AzEasingUtil.lerpWithOverride(scaleZPoint, easingType));
+ snapshot.updateScale(bone.getScaleX(), bone.getScaleY(), bone.getScaleZ());
+ snapshot.startScaleAnim();
+ bone.markScaleAsChanged();
+ }
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java
new file mode 100644
index 000000000..ea1906e4d
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzCachedBoneUpdateUtil.java
@@ -0,0 +1,160 @@
+package mod.azure.azurelib.rewrite.animation;
+
+import mod.azure.azurelib.core.utils.Interpolations;
+import mod.azure.azurelib.rewrite.model.AzBone;
+import mod.azure.azurelib.rewrite.model.AzBoneSnapshot;
+
+import java.util.Map;
+
+public class AzCachedBoneUpdateUtil {
+
+ /**
+ * Updates the cached position of a given bone by interpolating its offsets towards its initial snapshot. Stops
+ * ongoing position animations if necessary and updates the bone's position based on the reset percentage.
+ *
+ * @param bone the bone whose position is to be updated
+ * @param boneSnapshots a map containing snapshots of bones by their names
+ * @param animTime the current animation time
+ * @param resetTickLength the duration over which the position reset occurs
+ */
+ public static void updateCachedBonePosition(
+ AzBone bone,
+ Map boneSnapshots,
+ double animTime,
+ double resetTickLength
+ ) {
+ if (bone.hasPositionChanged()) {
+ return;
+ }
+
+ var initialSnapshot = bone.getInitialAzSnapshot();
+ var saveSnapshot = boneSnapshots.get(bone.getName());
+
+ if (saveSnapshot.isPosAnimInProgress()) {
+ saveSnapshot.stopPosAnim(animTime);
+ }
+
+ var percentageReset = Math.min(
+ (animTime - saveSnapshot.getLastResetPositionTick()) / resetTickLength,
+ 1
+ );
+
+ bone.setPosX(
+ (float) Interpolations.lerp(
+ saveSnapshot.getOffsetX(),
+ initialSnapshot.getOffsetX(),
+ percentageReset
+ )
+ );
+ bone.setPosY(
+ (float) Interpolations.lerp(
+ saveSnapshot.getOffsetY(),
+ initialSnapshot.getOffsetY(),
+ percentageReset
+ )
+ );
+ bone.setPosZ(
+ (float) Interpolations.lerp(
+ saveSnapshot.getOffsetZ(),
+ initialSnapshot.getOffsetZ(),
+ percentageReset
+ )
+ );
+
+ if (percentageReset >= 1) {
+ saveSnapshot.updateOffset(bone.getPosX(), bone.getPosY(), bone.getPosZ());
+ }
+ }
+
+ /**
+ * Updates the cached rotation of a given bone by interpolating its rotation values towards its initial snapshot.
+ * Stops any ongoing rotation animations if necessary and updates the bone's rotation based on the reset percentage.
+ *
+ * @param bone the bone whose rotation is to be updated
+ * @param boneSnapshots a map containing snapshots of bones by their names
+ * @param animTime the current animation time
+ * @param resetTickLength the duration over which the rotation reset occurs
+ */
+ public static void updateCachedBoneRotation(
+ AzBone bone,
+ Map boneSnapshots,
+ double animTime,
+ double resetTickLength
+ ) {
+ if (bone.hasRotationChanged()) {
+ return;
+ }
+
+ var initialSnapshot = bone.getInitialAzSnapshot();
+ var saveSnapshot = boneSnapshots.get(bone.getName());
+
+ if (saveSnapshot.isRotAnimInProgress()) {
+ saveSnapshot.stopRotAnim(animTime);
+ }
+
+ double percentageReset = Math.min(
+ (animTime - saveSnapshot.getLastResetRotationTick()) / resetTickLength,
+ 1
+ );
+
+ bone.setRotX(
+ (float) Interpolations.lerp(saveSnapshot.getRotX(), initialSnapshot.getRotX(), percentageReset)
+ );
+ bone.setRotY(
+ (float) Interpolations.lerp(saveSnapshot.getRotY(), initialSnapshot.getRotY(), percentageReset)
+ );
+ bone.setRotZ(
+ (float) Interpolations.lerp(saveSnapshot.getRotZ(), initialSnapshot.getRotZ(), percentageReset)
+ );
+
+ if (percentageReset >= 1) {
+ saveSnapshot.updateRotation(bone.getRotX(), bone.getRotY(), bone.getRotZ());
+ }
+ }
+
+ /**
+ * Updates the cached scale of a given bone by interpolating its scale values towards its initial snapshot. Stops
+ * any ongoing scale animations if necessary and updates the bone's scale based on the reset percentage.
+ *
+ * @param bone the bone whose scale is to be updated
+ * @param boneSnapshots a map containing snapshots of bones by their names
+ * @param animTime the current animation time
+ * @param resetTickLength the duration over which the scale reset occurs
+ */
+ public static void updateCachedBoneScale(
+ AzBone bone,
+ Map boneSnapshots,
+ double animTime,
+ double resetTickLength
+ ) {
+ if (bone.hasScaleChanged()) {
+ return;
+ }
+
+ var initialSnapshot = bone.getInitialAzSnapshot();
+ var saveSnapshot = boneSnapshots.get(bone.getName());
+
+ if (saveSnapshot.isScaleAnimInProgress()) {
+ saveSnapshot.stopScaleAnim(animTime);
+ }
+
+ double percentageReset = Math.min(
+ (animTime - saveSnapshot.getLastResetScaleTick()) / resetTickLength,
+ 1
+ );
+
+ bone.setScaleX(
+ (float) Interpolations.lerp(saveSnapshot.getScaleX(), initialSnapshot.getScaleX(), percentageReset)
+ );
+ bone.setScaleY(
+ (float) Interpolations.lerp(saveSnapshot.getScaleY(), initialSnapshot.getScaleY(), percentageReset)
+ );
+ bone.setScaleZ(
+ (float) Interpolations.lerp(saveSnapshot.getScaleZ(), initialSnapshot.getScaleZ(), percentageReset)
+ );
+
+ if (percentageReset >= 1) {
+ saveSnapshot.updateScale(bone.getScaleX(), bone.getScaleY(), bone.getScaleZ());
+ }
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java
new file mode 100644
index 000000000..33702aee5
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBakedAnimationCache.java
@@ -0,0 +1,54 @@
+package mod.azure.azurelib.rewrite.animation.cache;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import mod.azure.azurelib.loading.FileLoader;
+import mod.azure.azurelib.rewrite.AzResourceCache;
+import mod.azure.azurelib.rewrite.animation.primitive.AzBakedAnimations;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.packs.resources.ResourceManager;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+/**
+ * AzBakedAnimationCache is a singleton cache to manage and store preloaded animation data of type
+ * {@link AzBakedAnimations}. It is an extension of {@link AzResourceCache} and provides mechanisms for managing
+ * animation resources in Minecraft modding. Aimed at efficient storage and retrieval, as well as background processing
+ * of animation data.
+ * Features:
+ *
+ * - Supports asynchronous loading of animation resources from the in-memory {@code ResourceManager}.
+ *
- Caches animation data keyed by {@link ResourceLocation}.
+ *
- Provides access to the cached animations or null values for non-existent records.
+ *
+ */
+public class AzBakedAnimationCache extends AzResourceCache {
+
+ private static final AzBakedAnimationCache INSTANCE = new AzBakedAnimationCache();
+
+ public static AzBakedAnimationCache getInstance() {
+ return INSTANCE;
+ }
+
+ private final Map bakedAnimations;
+
+ private AzBakedAnimationCache() {
+ this.bakedAnimations = new Object2ObjectOpenHashMap<>();
+ }
+
+ public CompletableFuture loadAnimations(Executor backgroundExecutor, ResourceManager resourceManager) {
+ return loadResources(
+ backgroundExecutor,
+ resourceManager,
+ "animations",
+ resource -> FileLoader.loadAzAnimationsFile(resource, resourceManager),
+ bakedAnimations::put
+ );
+ }
+
+ public @Nullable AzBakedAnimations getNullable(ResourceLocation resourceLocation) {
+ return bakedAnimations.get(resourceLocation);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java
new file mode 100644
index 000000000..57fd970ec
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzBoneCache.java
@@ -0,0 +1,87 @@
+package mod.azure.azurelib.rewrite.animation.cache;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import mod.azure.azurelib.rewrite.animation.AzAnimationContext;
+import mod.azure.azurelib.rewrite.animation.AzCachedBoneUpdateUtil;
+import mod.azure.azurelib.rewrite.model.AzBakedModel;
+import mod.azure.azurelib.rewrite.model.AzBone;
+import mod.azure.azurelib.rewrite.model.AzBoneSnapshot;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The AzBoneCache class is responsible for managing the state and cache of bones in a baked model. It provides
+ * functionality for updating animation contexts, managing snapshots of bone states, and resetting transformation
+ * markers in preparation for rendering.
+ */
+public class AzBoneCache {
+
+ private AzBakedModel bakedModel;
+
+ private final Map boneSnapshotsByName;
+
+ public AzBoneCache() {
+ this.bakedModel = AzBakedModel.EMPTY;
+ this.boneSnapshotsByName = new Object2ObjectOpenHashMap<>();
+ }
+
+ public boolean setActiveModel(AzBakedModel model) {
+ var willModelChange = !Objects.equals(bakedModel, model);
+ this.bakedModel = model;
+
+ if (willModelChange) {
+ snapshot();
+ }
+
+ return willModelChange;
+ }
+
+ public void update(AzAnimationContext> context) {
+ var config = context.config();
+ var timer = context.timer();
+ var animTime = timer.getAnimTime();
+ var boneSnapshots = getBoneSnapshotsByName();
+ var resetTickLength = config.boneResetTime();
+
+ // Updates the cached bone snapshots (only if they have changed).
+ for (var bone : bakedModel.getBonesByName().values()) {
+ AzCachedBoneUpdateUtil.updateCachedBoneRotation(bone, boneSnapshots, animTime, resetTickLength);
+ AzCachedBoneUpdateUtil.updateCachedBonePosition(bone, boneSnapshots, animTime, resetTickLength);
+ AzCachedBoneUpdateUtil.updateCachedBoneScale(bone, boneSnapshots, animTime, resetTickLength);
+ }
+
+ resetBoneTransformationMarkers();
+ }
+
+ /**
+ * Reset the transformation markers applied to each {@link AzBone} ready for the next render frame
+ */
+ private void resetBoneTransformationMarkers() {
+ bakedModel.getBonesByName().values().forEach(AzBone::resetStateChanges);
+ }
+
+ /**
+ * Create new bone {@link AzBoneSnapshot} based on the bone's initial snapshot for the currently registered
+ * {@link AzBone AzBones}, filtered by the bones already present in the master snapshots map
+ */
+ private void snapshot() {
+ boneSnapshotsByName.clear();
+
+ for (var bone : bakedModel.getBonesByName().values()) {
+ boneSnapshotsByName.put(bone.getName(), AzBoneSnapshot.copy(bone.getInitialAzSnapshot()));
+ }
+ }
+
+ public AzBakedModel getBakedModel() {
+ return bakedModel;
+ }
+
+ public Map getBoneSnapshotsByName() {
+ return boneSnapshotsByName;
+ }
+
+ public boolean isEmpty() {
+ return bakedModel.getBonesByName().isEmpty();
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java
new file mode 100644
index 000000000..d07913e51
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java
@@ -0,0 +1,40 @@
+package mod.azure.azurelib.rewrite.animation.cache;
+
+import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator;
+import net.minecraft.world.item.ItemStack;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * The AzIdentifiableItemStackAnimatorCache class is a singleton utility for managing a cache of {@link ItemStack}
+ * objects, each associated with a unique identifier (UUID). This class provides functionality to register and retrieve
+ * item animators that apply to specific {@link ItemStack}s using their respective UUIDs.
+ */
+public class AzIdentifiableItemStackAnimatorCache {
+
+ private static final AzIdentifiableItemStackAnimatorCache INSTANCE = new AzIdentifiableItemStackAnimatorCache();
+
+ // TODO: Purge animators periodically.
+ private static final Map ANIMATORS_BY_UUID = new HashMap<>();
+
+ public static AzIdentifiableItemStackAnimatorCache getInstance() {
+ return INSTANCE;
+ }
+
+ private AzIdentifiableItemStackAnimatorCache() {}
+
+ public void add(ItemStack itemStack, AzItemAnimator animator) {
+ var uuid = itemStack.getTag().getUUID("az_id");
+
+ if (uuid != null) {
+ ANIMATORS_BY_UUID.computeIfAbsent(uuid, ($) -> animator);
+ }
+ }
+
+ public @Nullable AzItemAnimator getOrNull(UUID uuid) {
+ return uuid == null ? null : ANIMATORS_BY_UUID.get(uuid);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java
new file mode 100644
index 000000000..b89b3c15f
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentityRegistry.java
@@ -0,0 +1,39 @@
+package mod.azure.azurelib.rewrite.animation.cache;
+
+import net.minecraft.world.item.Item;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The AzIdentityRegistry class provides functionality to register and check the identity of items. This class maintains
+ * a static registry of unique items, allowing you to determine if a specific item has been registered.
+ */
+public class AzIdentityRegistry {
+
+ private static final Set- IDENTITY_OF_ITEMS = new HashSet<>();
+
+ /**
+ * Registers one or more items into a static identity set, ensuring that the items are stored for identity tracking.
+ *
+ * @param first The first non-null item to be registered. This parameter is mandatory.
+ * @param rest A varargs array of additional items to register. These items can be null, but null values will not
+ * be added to the identity set.
+ */
+ public static void register(@NotNull Item first, Item... rest) {
+ IDENTITY_OF_ITEMS.add(first);
+ IDENTITY_OF_ITEMS.addAll(Arrays.asList(rest));
+ }
+
+ /**
+ * Checks if the specified item exists in the identity set.
+ *
+ * @param item The item to check for identity. Must not be null.
+ * @return true if the item exists in the identity set, false otherwise.
+ */
+ public static boolean hasIdentity(Item item) {
+ return IDENTITY_OF_ITEMS.contains(item);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java
new file mode 100644
index 000000000..a3a3f85a4
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAbstractAnimationController.java
@@ -0,0 +1,33 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide;
+import mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence;
+
+// TODO: This will eventually be usable in common-side code once animations are moved from assets to data.
+public class AzAbstractAnimationController {
+
+ private final String name;
+
+ protected AzAnimationSequence currentSequence;
+
+ protected AzDispatchSide currentSequenceOrigin;
+
+ protected AzAbstractAnimationController(String name) {
+ this.name = name;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Checks whether the last animation that was playing on this controller has finished or not.
+ * This will return true if the controller has had an animation set previously, and it has finished playing and
+ * isn't going to loop or proceed to another animation.
+ *
+ * @return Whether the previous animation finished or not
+ */
+ public boolean hasAnimationFinished() {
+ return currentSequence != null;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java
new file mode 100644
index 000000000..ea52d8459
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationController.java
@@ -0,0 +1,210 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+import mod.azure.azurelib.rewrite.animation.AzAnimator;
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks;
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeManager;
+import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationPauseState;
+import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationPlayState;
+import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationStopState;
+import mod.azure.azurelib.rewrite.animation.controller.state.impl.AzAnimationTransitionState;
+import mod.azure.azurelib.rewrite.animation.controller.state.machine.AzAnimationControllerStateMachine;
+import mod.azure.azurelib.rewrite.animation.dispatch.AzDispatchSide;
+import mod.azure.azurelib.rewrite.animation.dispatch.command.sequence.AzAnimationSequence;
+import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation;
+import mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The actual controller that handles the playing and usage of animations, including their various keyframes and
+ * instruction markers. Each controller can only play a single animation at a time - for example you may have one
+ * controller to animate walking, one to control attacks, one to control size, etc.
+ */
+public class AzAnimationController extends AzAbstractAnimationController {
+
+ protected static final Logger LOGGER = LoggerFactory.getLogger(AzAnimationController.class);
+
+ public static AzAnimationControllerBuilder builder(AzAnimator animator, String name) {
+ return new AzAnimationControllerBuilder<>(animator, name);
+ }
+
+ private final AzAnimationControllerTimer controllerTimer;
+
+ private final AzAnimationQueue animationQueue;
+
+ private final AzAnimationControllerStateMachine stateMachine;
+
+ private final AzAnimator animator;
+
+ private final AzBoneAnimationQueueCache boneAnimationQueueCache;
+
+ private final AzBoneSnapshotCache boneSnapshotCache;
+
+ private final AzKeyframeManager keyframeManager;
+
+ protected AzQueuedAnimation currentAnimation;
+
+ private AzAnimationProperties animationProperties;
+
+ AzAnimationController(
+ String name,
+ AzAnimator animator,
+ AzAnimationProperties animationProperties,
+ AzKeyframeCallbacks keyframeCallbacks
+ ) {
+ super(name);
+
+ this.animator = animator;
+ this.controllerTimer = new AzAnimationControllerTimer<>(this);
+ this.animationProperties = animationProperties;
+
+ this.animationQueue = new AzAnimationQueue();
+ this.boneAnimationQueueCache = new AzBoneAnimationQueueCache<>(animator.context().boneCache());
+ this.boneSnapshotCache = new AzBoneSnapshotCache();
+ this.keyframeManager = new AzKeyframeManager<>(
+ this,
+ boneAnimationQueueCache,
+ boneSnapshotCache,
+ keyframeCallbacks
+ );
+
+ var stateHolder = new AzAnimationControllerStateMachine.StateHolder(
+ new AzAnimationPlayState<>(),
+ new AzAnimationPauseState<>(),
+ new AzAnimationStopState<>(),
+ new AzAnimationTransitionState<>()
+ );
+
+ this.stateMachine = new AzAnimationControllerStateMachine<>(stateHolder, this, animator.context());
+ }
+
+ @Override
+ public boolean hasAnimationFinished() {
+ return super.hasAnimationFinished() && stateMachine.isStopped();
+ }
+
+ public List tryCreateAnimationQueue(T animatable, AzAnimationSequence sequence) {
+ var stages = sequence.stages();
+ var animations = new ArrayList();
+
+ for (var stage : stages) {
+ var animation = animator.getAnimation(animatable, stage.name());
+
+ if (animation == null) {
+ LOGGER.warn(
+ "Unable to find animation: {} for {}",
+ stage.name(),
+ animatable.getClass().getSimpleName()
+ );
+ return List.of();
+ } else {
+ animations.add(new AzQueuedAnimation(animation, stage.properties().playBehavior()));
+ }
+ }
+
+ return animations;
+ }
+
+ /**
+ * This method is called every frame in order to populate the animation point queues, and process animation state
+ * logic.
+ */
+ public void update() {
+ // Adjust the tick before making any updates.
+ controllerTimer.update();
+ // Run state machine updates.
+ stateMachine.update();
+ // Update bone animation queue cache.
+ boneAnimationQueueCache.update(animationProperties.easingType());
+ }
+
+ public void run(AzDispatchSide originSide, @NotNull AzAnimationSequence sequence) {
+ if (currentSequenceOrigin == AzDispatchSide.SERVER && originSide == AzDispatchSide.CLIENT) {
+ if (!hasAnimationFinished()) {
+ // If we're playing a server-side sequence, ignore client-side sequences.
+ return;
+ }
+ }
+
+ this.currentSequenceOrigin = originSide;
+
+ if (stateMachine.isStopped()) {
+ stateMachine.transition();
+ }
+
+ if (currentSequence == null || !currentSequence.equals(sequence)) {
+ this.currentAnimation = null;
+ }
+
+ var animatable = animator.context().animatable();
+
+ if (sequence.stages().isEmpty()) {
+ stateMachine.stop();
+ return;
+ }
+
+ if (!sequence.equals(currentSequence)) {
+ var animations = tryCreateAnimationQueue(animatable, sequence);
+
+ if (!animations.isEmpty()) {
+ animationQueue.clear();
+ animationQueue.addAll(animations);
+ this.currentSequence = sequence;
+ stateMachine.transition();
+ return;
+ }
+
+ stateMachine.stop();
+ }
+ }
+
+ public AzAnimationProperties animationProperties() {
+ return animationProperties;
+ }
+
+ public void setAnimationProperties(AzAnimationProperties animationProperties) {
+ this.animationProperties = animationProperties;
+ }
+
+ public AzAnimationQueue animationQueue() {
+ return animationQueue;
+ }
+
+ public AzBoneAnimationQueueCache boneAnimationQueueCache() {
+ return boneAnimationQueueCache;
+ }
+
+ public AzBoneSnapshotCache boneSnapshotCache() {
+ return boneSnapshotCache;
+ }
+
+ public AzAnimationControllerTimer controllerTimer() {
+ return controllerTimer;
+ }
+
+ public @Nullable AzQueuedAnimation currentAnimation() {
+ return currentAnimation;
+ }
+
+ public AzKeyframeManager keyframeManager() {
+ return keyframeManager;
+ }
+
+ public AzAnimationControllerStateMachine stateMachine() {
+ return stateMachine;
+ }
+
+ public void setCurrentAnimation(AzQueuedAnimation currentAnimation) {
+ this.currentAnimation = currentAnimation;
+
+ if (currentAnimation == null) {
+ this.currentSequence = null;
+ this.currentSequenceOrigin = null;
+ }
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java
new file mode 100644
index 000000000..0adbee4da
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerBuilder.java
@@ -0,0 +1,64 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+import mod.azure.azurelib.rewrite.animation.AzAnimator;
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzKeyframeCallbacks;
+import mod.azure.azurelib.rewrite.animation.easing.AzEasingType;
+import mod.azure.azurelib.rewrite.animation.property.AzAnimationProperties;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A builder class to construct {@link AzAnimationController} instances for managing animations in {@link AzAnimator}.
+ * This provides a fluent API to configure properties such as animation speed, keyframe callbacks, easing type,
+ * transition length, and triggerable animations.
+ *
+ * @param The type of object that the animation controller will operate on.
+ */
+public class AzAnimationControllerBuilder {
+
+ private final AzAnimator animator;
+
+ private final String name;
+
+ private AzAnimationProperties animationProperties;
+
+ private AzKeyframeCallbacks keyframeCallbacks;
+
+ public AzAnimationControllerBuilder(AzAnimator animator, String name) {
+ this.animator = animator;
+ this.name = name;
+ this.animationProperties = AzAnimationProperties.DEFAULT;
+ this.keyframeCallbacks = AzKeyframeCallbacks.noop();
+ }
+
+ public AzAnimationControllerBuilder setAnimationSpeed(double animationSpeed) {
+ animationProperties = animationProperties.withAnimationSpeed(animationSpeed);
+ return this;
+ }
+
+ public AzAnimationControllerBuilder setKeyframeCallbacks(@NotNull AzKeyframeCallbacks keyframeCallbacks) {
+ Objects.requireNonNull(keyframeCallbacks);
+ this.keyframeCallbacks = keyframeCallbacks;
+ return this;
+ }
+
+ public AzAnimationControllerBuilder setEasingType(AzEasingType easingType) {
+ animationProperties = animationProperties.withEasingType(easingType);
+ return this;
+ }
+
+ public AzAnimationControllerBuilder setTransitionLength(int transitionLength) {
+ animationProperties = animationProperties.withTransitionLength(transitionLength);
+ return this;
+ }
+
+ public AzAnimationController build() {
+ return new AzAnimationController<>(
+ name,
+ animator,
+ animationProperties,
+ keyframeCallbacks
+ );
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java
new file mode 100644
index 000000000..d62bad1b3
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerContainer.java
@@ -0,0 +1,39 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A container class for managing a collection of {@link AzAnimationController} instances. Provides methods to add,
+ * retrieve, and access animation controllers by their names.
+ *
+ * @param the type of the animation data or state managed by {@link AzAnimationController}.
+ */
+public class AzAnimationControllerContainer {
+
+ private final Map> animationControllersByName;
+
+ public AzAnimationControllerContainer() {
+ this.animationControllersByName = new Object2ObjectArrayMap<>();
+ }
+
+ @SafeVarargs
+ public final void add(AzAnimationController controller, AzAnimationController... controllers) {
+ animationControllersByName.put(controller.name(), controller);
+
+ for (var extraController : controllers) {
+ animationControllersByName.put(extraController.name(), extraController);
+ }
+ }
+
+ public @Nullable AzAnimationController getOrNull(String controllerName) {
+ return animationControllersByName.get(controllerName);
+ }
+
+ public Collection> getAll() {
+ return animationControllersByName.values();
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java
new file mode 100644
index 000000000..35bc94785
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationControllerTimer.java
@@ -0,0 +1,48 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+/**
+ * A timer utility that integrates directly with an {@link AzAnimationController} to track and adjust tick values for
+ * animation playback control, based on the controller's state and animation speed modifiers.
+ *
+ * @param The type of the animatable entity being controlled by the animation controller.
+ */
+public class AzAnimationControllerTimer {
+
+ private final AzAnimationController animationController;
+
+ private double adjustedTick;
+
+ private double tickOffset;
+
+ public AzAnimationControllerTimer(AzAnimationController animationController) {
+ this.animationController = animationController;
+ }
+
+ /**
+ * Adjust a tick value depending on the controller's current state and speed modifier.
+ * Is used when starting a new animation, transitioning, and a few other key areas
+ */
+ public void update() {
+ var stateMachine = animationController.stateMachine();
+ var animContext = stateMachine.getContext().animationContext();
+ var animationSpeed = animationController.animationProperties().animationSpeed();
+ var tick = animContext.timer().getAnimTime();
+
+ adjustedTick = animationSpeed * Math.max(tick - tickOffset, 0);
+ }
+
+ public void reset() {
+ var stateMachine = animationController.stateMachine();
+ var animContext = stateMachine.getContext().animationContext();
+ this.tickOffset = animContext.timer().getAnimTime();
+ this.adjustedTick = 0;
+ }
+
+ public double getAdjustedTick() {
+ return adjustedTick;
+ }
+
+ public void addToAdjustedTick(double adjustedTick) {
+ this.adjustedTick += adjustedTick;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java
new file mode 100644
index 000000000..e78b89f2d
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzAnimationQueue.java
@@ -0,0 +1,51 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * Represents a queue of animations to be processed in a sequential manner. This class manages a collection of
+ * {@link AzQueuedAnimation} objects, allowing animations to be queued, retrieved, and cleared efficiently. It ensures
+ * that animations are processed in the order they are added.
+ *
+ * The queue supports operations to inspect the next animation without removal, retrieve and remove the next animation,
+ * add individual or multiple animations, and clear the entire queue. Additionally, it provides a method to determine if
+ * the queue is empty.
+ */
+public class AzAnimationQueue {
+
+ private final Queue animationQueue;
+
+ public AzAnimationQueue() {
+ this.animationQueue = new LinkedList<>();
+ }
+
+ public void add(@NotNull AzQueuedAnimation queuedAnimation) {
+ animationQueue.add(queuedAnimation);
+ }
+
+ public void addAll(@NotNull Collection queuedAnimations) {
+ animationQueue.addAll(queuedAnimations);
+ }
+
+ public @Nullable AzQueuedAnimation peek() {
+ return animationQueue.peek();
+ }
+
+ public @Nullable AzQueuedAnimation next() {
+ return animationQueue.poll();
+ }
+
+ public void clear() {
+ animationQueue.clear();
+ }
+
+ public boolean isEmpty() {
+ return animationQueue.isEmpty();
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java
new file mode 100644
index 000000000..5b8232fff
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneAnimationQueueCache.java
@@ -0,0 +1,61 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import mod.azure.azurelib.rewrite.animation.AzBoneAnimationUpdateUtil;
+import mod.azure.azurelib.rewrite.animation.cache.AzBoneCache;
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.AzBoneAnimationQueue;
+import mod.azure.azurelib.rewrite.animation.easing.AzEasingType;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * The AzBoneAnimationQueueCache class is responsible for managing and updating animation queues for bones. It acts as a
+ * cache that maps bone names to their respective animation queues, enabling efficient updates and access.
+ *
+ * @param the type of the animatable object used in the animation context
+ */
+public class AzBoneAnimationQueueCache {
+
+ private final Map boneAnimationQueues;
+
+ private final AzBoneCache boneCache;
+
+ public AzBoneAnimationQueueCache(AzBoneCache boneCache) {
+ this.boneAnimationQueues = new Object2ObjectOpenHashMap<>();
+ this.boneCache = boneCache;
+ }
+
+ public void update(AzEasingType easingType) {
+ var boneSnapshots = boneCache.getBoneSnapshotsByName();
+
+ for (var boneAnimation : boneAnimationQueues.values()) {
+ var bone = boneAnimation.bone();
+ var snapshot = boneSnapshots.get(bone.getName());
+ var initialSnapshot = bone.getInitialAzSnapshot();
+
+ AzBoneAnimationUpdateUtil.updateRotations(boneAnimation, bone, easingType, initialSnapshot, snapshot);
+ AzBoneAnimationUpdateUtil.updatePositions(boneAnimation, bone, easingType, snapshot);
+ AzBoneAnimationUpdateUtil.updateScale(boneAnimation, bone, easingType, snapshot);
+ }
+ }
+
+ public Collection values() {
+ return boneAnimationQueues.values();
+ }
+
+ public @Nullable AzBoneAnimationQueue getOrNull(String boneName) {
+ var bone = boneCache.getBakedModel().getBoneOrNull(boneName);
+
+ if (bone == null) {
+ return null;
+ }
+
+ return boneAnimationQueues.computeIfAbsent(boneName, $ -> new AzBoneAnimationQueue(bone));
+ }
+
+ public void clear() {
+ boneAnimationQueues.clear();
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java
new file mode 100644
index 000000000..474db7337
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/AzBoneSnapshotCache.java
@@ -0,0 +1,53 @@
+package mod.azure.azurelib.rewrite.animation.controller;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation;
+import mod.azure.azurelib.rewrite.model.AzBoneSnapshot;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A cache system for managing and storing {@link AzBoneSnapshot} objects related to specific animations and their
+ * corresponding bone animations. This class is designed to optimize the retrieval and utilization of bone snapshots
+ * during animation interpolation (lerping).
+ *
+ * The cache works by filtering and storing relevant {@code AzBoneSnapshot} instances for a given
+ * {@link AzQueuedAnimation}, based on the associated bone animations defined within the animation. It ensures that only
+ * the required snapshots are cached, reducing redundancy and improving performance during animation processing.
+ */
+public class AzBoneSnapshotCache {
+
+ private final Map boneSnapshots;
+
+ public AzBoneSnapshotCache() {
+ this.boneSnapshots = new Object2ObjectOpenHashMap<>();
+ }
+
+ /**
+ * Cache the relevant {@link AzBoneSnapshot AzBoneSnapshots} for the current {@link AzQueuedAnimation} for animation
+ * lerping
+ *
+ * @param animation The {@code QueuedAnimation} to filter {@code BoneSnapshots} for
+ * @param snapshots The master snapshot collection to pull filter from
+ */
+ public void put(AzQueuedAnimation animation, Collection snapshots) {
+ if (animation.animation().boneAnimations() == null) {
+ return;
+ }
+
+ for (var snapshot : snapshots) {
+ for (var boneAnimation : animation.animation().boneAnimations()) {
+ if (boneAnimation.boneName().equals(snapshot.getBone().getName())) {
+ boneSnapshots.put(boneAnimation.boneName(), AzBoneSnapshot.copy(snapshot));
+ break;
+ }
+ }
+ }
+ }
+
+ public @Nullable AzBoneSnapshot getOrNull(String name) {
+ return boneSnapshots.get(name);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java
new file mode 100644
index 000000000..b20b107c5
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAbstractKeyframeExecutor.java
@@ -0,0 +1,79 @@
+package mod.azure.azurelib.rewrite.animation.controller.keyframe;
+
+import mod.azure.azurelib.core.keyframe.AnimationPoint;
+import mod.azure.azurelib.core.keyframe.Keyframe;
+import mod.azure.azurelib.core.keyframe.KeyframeLocation;
+import mod.azure.azurelib.core.math.Constant;
+import mod.azure.azurelib.core.math.IValue;
+import mod.azure.azurelib.core.object.Axis;
+
+import java.util.List;
+
+/**
+ * AzAbstractKeyframeExecutor is a base class designed to handle animations and transitions between keyframes in a
+ * generic and reusable fashion. It provides the foundational logic for determining the current state of an animation
+ * based on the tick time and computing the animation's required values.
+ */
+public class AzAbstractKeyframeExecutor {
+
+ protected AzAbstractKeyframeExecutor() {}
+
+ /**
+ * Convert a {@link KeyframeLocation} to an {@link AnimationPoint}
+ */
+ protected AzAnimationPoint getAnimationPointAtTick(
+ List> frames,
+ double tick,
+ boolean isRotation,
+ Axis axis
+ ) {
+ var location = getCurrentKeyframeLocation(frames, tick);
+ var currentFrame = location.keyframe();
+ var startValue = currentFrame.startValue().get();
+ var endValue = currentFrame.endValue().get();
+
+ if (isRotation) {
+ if (!(currentFrame.startValue() instanceof Constant)) {
+ startValue = Math.toRadians(startValue);
+
+ if (axis == Axis.X || axis == Axis.Y) {
+ startValue *= -1;
+ }
+ }
+
+ if (!(currentFrame.endValue() instanceof Constant)) {
+ endValue = Math.toRadians(endValue);
+
+ if (axis == Axis.X || axis == Axis.Y) {
+ endValue *= -1;
+ }
+ }
+ }
+
+ return new AzAnimationPoint(currentFrame, location.startTick(), currentFrame.length(), startValue, endValue);
+ }
+
+ /**
+ * Returns the {@link Keyframe} relevant to the current tick time
+ *
+ * @param frames The list of {@code Keyframes} to filter through
+ * @param ageInTicks The current tick time
+ * @return A new {@code KeyframeLocation} containing the current {@code Keyframe} and the tick time used to find it
+ */
+ protected AzKeyframeLocation> getCurrentKeyframeLocation(
+ List> frames,
+ double ageInTicks
+ ) {
+ var totalFrameTime = 0.0;
+
+ for (var frame : frames) {
+ totalFrameTime += frame.length();
+
+ if (totalFrameTime > ageInTicks) {
+ return new AzKeyframeLocation<>(frame, (ageInTicks - (totalFrameTime - frame.length())));
+ }
+ }
+
+ return new AzKeyframeLocation<>(frames.get(frames.size() - 1), ageInTicks);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java
new file mode 100644
index 000000000..094ea1cdc
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzAnimationPoint.java
@@ -0,0 +1,27 @@
+package mod.azure.azurelib.rewrite.animation.controller.keyframe;
+
+/**
+ * Animation state record that holds the state of an animation at a given point
+ *
+ * @param currentTick The lerped tick time (current tick + partial tick) of the point
+ * @param transitionLength The length of time (in ticks) that the point should take to transition
+ * @param animationStartValue The start value to provide to the animation handling system
+ * @param animationEndValue The end value to provide to the animation handling system
+ * @param keyframe The {@code Nullable} Keyframe
+ */
+public record AzAnimationPoint(
+ AzKeyframe> keyframe,
+ double currentTick,
+ double transitionLength,
+ double animationStartValue,
+ double animationEndValue
+) {
+
+ @Override
+ public String toString() {
+ return "Tick: " + this.currentTick +
+ " | Transition Length: " + this.transitionLength +
+ " | Start Value: " + this.animationStartValue +
+ " | End Value: " + this.animationEndValue;
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java
new file mode 100644
index 000000000..b76970f1d
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimation.java
@@ -0,0 +1,20 @@
+package mod.azure.azurelib.rewrite.animation.controller.keyframe;
+
+import mod.azure.azurelib.core.keyframe.Keyframe;
+import mod.azure.azurelib.core.math.IValue;
+
+/**
+ * A record of a deserialized animation for a given bone.
+ * Responsible for holding the various {@link Keyframe Keyframes} for the bone's animation transformations
+ *
+ * @param boneName The name of the bone as listed in the {@code animation.json}
+ * @param rotationKeyframes The deserialized rotation {@code Keyframe} stack
+ * @param positionKeyframes The deserialized position {@code Keyframe} stack
+ * @param scaleKeyframes The deserialized scale {@code Keyframe} stack
+ */
+public record AzBoneAnimation(
+ String boneName,
+ AzKeyframeStack> rotationKeyframes,
+ AzKeyframeStack> positionKeyframes,
+ AzKeyframeStack> scaleKeyframes
+) {}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java
new file mode 100644
index 000000000..4f636ee8e
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzBoneAnimationQueue.java
@@ -0,0 +1,400 @@
+/**
+ * This class is a fork of the matching class found in the Geckolib repository. Original source:
+ * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License.
+ * https://github.com/bernie-g/geckolib/blob/main/LICENSE
+ */
+package mod.azure.azurelib.rewrite.animation.controller.keyframe;
+
+import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController;
+import mod.azure.azurelib.rewrite.model.AzBone;
+import mod.azure.azurelib.rewrite.model.AzBoneSnapshot;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * A bone pseudo-stack for bone animation positions, scales, and rotations. Animation points are calculated then pushed
+ * onto their respective queues to be used for transformations in rendering
+ */
+public record AzBoneAnimationQueue(
+ AzBone bone,
+ Queue rotationXQueue,
+ Queue rotationYQueue,
+ Queue rotationZQueue,
+ Queue positionXQueue,
+ Queue positionYQueue,
+ Queue positionZQueue,
+ Queue scaleXQueue,
+ Queue scaleYQueue,
+ Queue scaleZQueue
+) {
+
+ public AzBoneAnimationQueue(AzBone bone) {
+ // TODO: Optimize
+ this(
+ bone,
+ new LinkedList<>(),
+ new LinkedList<>(),
+ new LinkedList<>(),
+ new LinkedList<>(),
+ new LinkedList<>(),
+ new LinkedList<>(),
+ new LinkedList<>(),
+ new LinkedList<>(),
+ new LinkedList<>()
+ );
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#positionXQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addPosXPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.positionXQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#positionYQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addPosYPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.positionYQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#positionZQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addPosZPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.positionZQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new X, Y, and Z position {@link AzAnimationPoint} to their respective queues
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (base on the {@link AzAnimationController}
+ * @param startSnapshot The {@link AzBoneSnapshot} that serves as the starting positions relevant to the keyframe
+ * provided
+ * @param nextXPoint The X {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ * @param nextYPoint The Y {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ * @param nextZPoint The Z {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ */
+ public void addNextPosition(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ AzBoneSnapshot startSnapshot,
+ AzAnimationPoint nextXPoint,
+ AzAnimationPoint nextYPoint,
+ AzAnimationPoint nextZPoint
+ ) {
+ addPosXPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getOffsetX(),
+ nextXPoint.animationStartValue()
+ );
+ addPosYPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getOffsetY(),
+ nextYPoint.animationStartValue()
+ );
+ addPosZPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getOffsetZ(),
+ nextZPoint.animationStartValue()
+ );
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#scaleXQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addScaleXPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.scaleXQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#scaleYQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addScaleYPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.scaleYQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#scaleZQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addScaleZPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.scaleZQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new X, Y, and Z scale {@link AzAnimationPoint} to their respective queues
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (base on the {@link AzAnimationController}
+ * @param startSnapshot The {@link AzBoneSnapshot} that serves as the starting scales relevant to the keyframe
+ * provided
+ * @param nextXPoint The X {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ * @param nextYPoint The Y {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ * @param nextZPoint The Z {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ */
+ public void addNextScale(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ AzBoneSnapshot startSnapshot,
+ AzAnimationPoint nextXPoint,
+ AzAnimationPoint nextYPoint,
+ AzAnimationPoint nextZPoint
+ ) {
+ addScaleXPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getScaleX(),
+ nextXPoint.animationStartValue()
+ );
+ addScaleYPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getScaleY(),
+ nextYPoint.animationStartValue()
+ );
+ addScaleZPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getScaleZ(),
+ nextZPoint.animationStartValue()
+ );
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#rotationXQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addRotationXPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.rotationXQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#rotationYQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addRotationYPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.rotationYQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new {@link AzAnimationPoint} to the {@link AzBoneAnimationQueue#rotationZQueue}
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (based on the {@link AzAnimationController})
+ * @param startValue The value of the point at the start of its transition
+ * @param endValue The value of the point at the end of its transition
+ */
+ public void addRotationZPoint(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ double startValue,
+ double endValue
+ ) {
+ this.rotationZQueue.add(new AzAnimationPoint(keyframe, lerpedTick, transitionLength, startValue, endValue));
+ }
+
+ /**
+ * Add a new X, Y, and Z scale {@link AzAnimationPoint} to their respective queues
+ *
+ * @param keyframe The {@code Nullable} Keyframe relevant to the animation point
+ * @param lerpedTick The lerped time (current tick + partial tick) that the point starts at
+ * @param transitionLength The length of the transition (base on the {@link AzAnimationController}
+ * @param startSnapshot The {@link AzBoneSnapshot} that serves as the starting rotations relevant to the keyframe
+ * provided
+ * @param initialSnapshot The {@link AzBoneSnapshot} that serves as the unmodified rotations of the bone
+ * @param nextXPoint The X {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ * @param nextYPoint The Y {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ * @param nextZPoint The Z {@code AnimationPoint} that is next in the queue, to serve as the end value of the
+ * new point
+ */
+ public void addNextRotation(
+ AzKeyframe> keyframe,
+ double lerpedTick,
+ double transitionLength,
+ AzBoneSnapshot startSnapshot,
+ AzBoneSnapshot initialSnapshot,
+ AzAnimationPoint nextXPoint,
+ AzAnimationPoint nextYPoint,
+ AzAnimationPoint nextZPoint
+ ) {
+ addRotationXPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getRotX() - initialSnapshot.getRotX(),
+ nextXPoint.animationStartValue()
+ );
+ addRotationYPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getRotY() - initialSnapshot.getRotY(),
+ nextYPoint.animationStartValue()
+ );
+ addRotationZPoint(
+ keyframe,
+ lerpedTick,
+ transitionLength,
+ startSnapshot.getRotZ() - initialSnapshot.getRotZ(),
+ nextZPoint.animationStartValue()
+ );
+ }
+
+ /**
+ * Add an X, Y, and Z position {@link AzAnimationPoint} to their respective queues
+ *
+ * @param xPoint The x position {@code AnimationPoint} to add
+ * @param yPoint The y position {@code AnimationPoint} to add
+ * @param zPoint The z position {@code AnimationPoint} to add
+ */
+ public void addPositions(AzAnimationPoint xPoint, AzAnimationPoint yPoint, AzAnimationPoint zPoint) {
+ this.positionXQueue.add(xPoint);
+ this.positionYQueue.add(yPoint);
+ this.positionZQueue.add(zPoint);
+ }
+
+ /**
+ * Add an X, Y, and Z scale {@link AzAnimationPoint} to their respective queues
+ *
+ * @param xPoint The x scale {@code AnimationPoint} to add
+ * @param yPoint The y scale {@code AnimationPoint} to add
+ * @param zPoint The z scale {@code AnimationPoint} to add
+ */
+ public void addScales(AzAnimationPoint xPoint, AzAnimationPoint yPoint, AzAnimationPoint zPoint) {
+ this.scaleXQueue.add(xPoint);
+ this.scaleYQueue.add(yPoint);
+ this.scaleZQueue.add(zPoint);
+ }
+
+ /**
+ * Add an X, Y, and Z rotation {@link AzAnimationPoint} to their respective queues
+ *
+ * @param xPoint The x rotation {@code AnimationPoint} to add
+ * @param yPoint The y rotation {@code AnimationPoint} to add
+ * @param zPoint The z rotation {@code AnimationPoint} to add
+ */
+ public void addRotations(AzAnimationPoint xPoint, AzAnimationPoint yPoint, AzAnimationPoint zPoint) {
+ this.rotationXQueue.add(xPoint);
+ this.rotationYQueue.add(yPoint);
+ this.rotationZQueue.add(zPoint);
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java
new file mode 100644
index 000000000..e03b4943f
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframe.java
@@ -0,0 +1,56 @@
+/**
+ * This class is a fork of the matching class found in the Geckolib repository. Original source:
+ * https://github.com/bernie-g/geckolib Copyright © 2024 Bernie-G. Licensed under the MIT License.
+ * https://github.com/bernie-g/geckolib/blob/main/LICENSE
+ */
+package mod.azure.azurelib.rewrite.animation.controller.keyframe;
+
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import mod.azure.azurelib.core.math.IValue;
+import mod.azure.azurelib.rewrite.animation.easing.AzEasingType;
+import mod.azure.azurelib.rewrite.animation.easing.AzEasingTypes;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Animation keyframe data
+ *
+ * @param length The length (in ticks) the keyframe lasts for
+ * @param startValue The value to start the keyframe's transformation with
+ * @param endValue The value to end the keyframe's transformation with
+ * @param easingType The {@code EasingType} to use for transformations
+ * @param easingArgs The arguments to provide to the easing calculation
+ */
+public record AzKeyframe(
+ double length,
+ T startValue,
+ T endValue,
+ AzEasingType easingType,
+ List easingArgs
+) {
+
+ public AzKeyframe(double length, T startValue, T endValue) {
+ this(length, startValue, endValue, AzEasingTypes.LINEAR);
+ }
+
+ public AzKeyframe(double length, T startValue, T endValue, AzEasingType easingType) {
+ this(length, startValue, endValue, easingType, new ObjectArrayList<>(0));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.length, this.startValue, this.endValue, this.easingType, this.easingArgs);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+
+ return hashCode() == obj.hashCode();
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java
new file mode 100644
index 000000000..70fb79e6f
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbackHandler.java
@@ -0,0 +1,126 @@
+package mod.azure.azurelib.rewrite.animation.controller.keyframe;
+
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import mod.azure.azurelib.core.keyframe.event.data.KeyFrameData;
+import mod.azure.azurelib.rewrite.animation.controller.AzAnimationController;
+import mod.azure.azurelib.rewrite.animation.event.AzCustomInstructionKeyframeEvent;
+import mod.azure.azurelib.rewrite.animation.event.AzParticleKeyframeEvent;
+import mod.azure.azurelib.rewrite.animation.event.AzSoundKeyframeEvent;
+import mod.azure.azurelib.rewrite.animation.primitive.AzQueuedAnimation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+/**
+ * AzKeyframeCallbackHandler acts as a handler for managing animation keyframe events such as sound, particle, or custom
+ * events during a specific animation. It works in conjunction with an animation controller and a set of keyframe
+ * callbacks, executing them as appropriate based on the animation's progress.
+ * This class is generic and operates on a user-defined animatable type to handle various keyframe events related to
+ * animations.
+ *
+ * @param the type of the animatable object being handled
+ */
+// TODO: reduce the boilerplate of the specialized handle functions in this class.
+public class AzKeyframeCallbackHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AzKeyframeCallbackHandler.class);
+
+ private final AzAnimationController animationController;
+
+ private final Set executedKeyframes;
+
+ private final AzKeyframeCallbacks keyframeCallbacks;
+
+ public AzKeyframeCallbackHandler(
+ AzAnimationController animationController,
+ AzKeyframeCallbacks keyframeCallbacks
+ ) {
+ this.animationController = animationController;
+ this.executedKeyframes = new ObjectOpenHashSet<>();
+ this.keyframeCallbacks = keyframeCallbacks;
+ }
+
+ public void handle(T animatable, double adjustedTick) {
+ handleSoundKeyframes(animatable, adjustedTick);
+ handleParticleKeyframes(animatable, adjustedTick);
+ handleCustomKeyframes(animatable, adjustedTick);
+ }
+
+ private void handleCustomKeyframes(T animatable, double adjustedTick) {
+ var customKeyframeHandler = keyframeCallbacks.customKeyframeHandler();
+ var customInstructions = currentAnimation().animation().keyframes().customInstructions();
+
+ for (var keyframeData : customInstructions) {
+ if (adjustedTick >= keyframeData.getStartTick() && executedKeyframes.add(keyframeData)) {
+ if (customKeyframeHandler == null) {
+ LOGGER.warn(
+ "Custom Instruction Keyframe found for {} -> {}, but no keyframe handler registered",
+ animatable.getClass().getSimpleName(),
+ animationController.name()
+ );
+ break;
+ }
+
+ customKeyframeHandler.handle(
+ new AzCustomInstructionKeyframeEvent<>(animatable, adjustedTick, animationController, keyframeData)
+ );
+ }
+ }
+ }
+
+ private void handleParticleKeyframes(T animatable, double adjustedTick) {
+ var particleKeyframeHandler = keyframeCallbacks.particleKeyframeHandler();
+ var particleInstructions = currentAnimation().animation().keyframes().particles();
+
+ for (var keyframeData : particleInstructions) {
+ if (adjustedTick >= keyframeData.getStartTick() && executedKeyframes.add(keyframeData)) {
+ if (particleKeyframeHandler == null) {
+ LOGGER.warn(
+ "Particle Keyframe found for {} -> {}, but no keyframe handler registered",
+ animatable.getClass().getSimpleName(),
+ animationController.name()
+ );
+ break;
+ }
+
+ particleKeyframeHandler.handle(
+ new AzParticleKeyframeEvent<>(animatable, adjustedTick, animationController, keyframeData)
+ );
+ }
+ }
+ }
+
+ private void handleSoundKeyframes(T animatable, double adjustedTick) {
+ var soundKeyframeHandler = keyframeCallbacks.soundKeyframeHandler();
+ var soundInstructions = currentAnimation().animation().keyframes().sounds();
+
+ for (var keyframeData : soundInstructions) {
+ if (adjustedTick >= keyframeData.getStartTick() && executedKeyframes.add(keyframeData)) {
+ if (soundKeyframeHandler == null) {
+ LOGGER.warn(
+ "Sound Keyframe found for {} -> {}, but no keyframe handler registered",
+ animatable.getClass().getSimpleName(),
+ animationController.name()
+ );
+ break;
+ }
+
+ soundKeyframeHandler.handle(
+ new AzSoundKeyframeEvent<>(animatable, adjustedTick, animationController, keyframeData)
+ );
+ }
+ }
+ }
+
+ /**
+ * Clear the {@link KeyFrameData} cache in preparation for the next animation
+ */
+ public void reset() {
+ executedKeyframes.clear();
+ }
+
+ private AzQueuedAnimation currentAnimation() {
+ return animationController.currentAnimation();
+ }
+}
diff --git a/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java
new file mode 100644
index 000000000..342795f97
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/controller/keyframe/AzKeyframeCallbacks.java
@@ -0,0 +1,105 @@
+package mod.azure.azurelib.rewrite.animation.controller.keyframe;
+
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.handler.AzCustomKeyframeHandler;
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.handler.AzParticleKeyframeHandler;
+import mod.azure.azurelib.rewrite.animation.controller.keyframe.handler.AzSoundKeyframeHandler;
+import mod.azure.azurelib.rewrite.animation.event.AzCustomInstructionKeyframeEvent;
+import mod.azure.azurelib.rewrite.animation.event.AzParticleKeyframeEvent;
+import mod.azure.azurelib.rewrite.animation.event.AzSoundKeyframeEvent;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * The AzKeyframeCallbacks class manages callbacks for different types of keyframe events, enabling the handling of
+ * sound, particle, and custom-defined keyframe instructions during an animation sequence.
+ *
+ * @param The type of entity or object this keyframe callback interacts with.
+ */
+public class AzKeyframeCallbacks {
+
+ private static final AzKeyframeCallbacks> NO_OP = new AzKeyframeCallbacks<>(null, null, null);
+
+ @SuppressWarnings("unchecked")
+ public static AzKeyframeCallbacks noop() {
+ return (AzKeyframeCallbacks) NO_OP;
+ }
+
+ private final @Nullable AzCustomKeyframeHandler customKeyframeHandler;
+
+ private final @Nullable AzParticleKeyframeHandler particleKeyframeHandler;
+
+ private final @Nullable AzSoundKeyframeHandler soundKeyframeHandler;
+
+ private AzKeyframeCallbacks(
+ @Nullable AzCustomKeyframeHandler customKeyframeHandler,
+ @Nullable AzParticleKeyframeHandler particleKeyframeHandler,
+ @Nullable AzSoundKeyframeHandler soundKeyframeHandler
+ ) {
+ this.customKeyframeHandler = customKeyframeHandler;
+ this.particleKeyframeHandler = particleKeyframeHandler;
+ this.soundKeyframeHandler = soundKeyframeHandler;
+ }
+
+ public @Nullable AzCustomKeyframeHandler customKeyframeHandler() {
+ return customKeyframeHandler;
+ }
+
+ public @Nullable AzParticleKeyframeHandler particleKeyframeHandler() {
+ return particleKeyframeHandler;
+ }
+
+ public @Nullable AzSoundKeyframeHandler soundKeyframeHandler() {
+ return soundKeyframeHandler;
+ }
+
+ public static Builder builder() {
+ return new Builder<>();
+ }
+
+ public static class Builder {
+
+ private @Nullable AzCustomKeyframeHandler customKeyframeHandler;
+
+ private @Nullable AzParticleKeyframeHandler particleKeyframeHandler;
+
+ private @Nullable AzSoundKeyframeHandler soundKeyframeHandler;
+
+ private Builder() {}
+
+ /**
+ * Applies the given {@link AzSoundKeyframeHandler} to this controller, for handling {@link AzSoundKeyframeEvent
+ * sound keyframe instructions}.
+ *
+ * @return this
+ */
+ public Builder