diff --git a/README.md b/README.md
index 0dfee53a7..d737f152a 100644
--- a/README.md
+++ b/README.md
@@ -5,32 +5,6 @@
AzureLib represents a branch derived from Geckolib 4.x, serving as an animation engine tailored for Minecraft Mods. It boasts various features, including support for intricate 3D keyframe-driven animations, over 30 different easing functions, concurrent animation capabilities, sound and particle keyframes, event-based keyframes, and numerous other functionalities. Currently, I'll focus on maintaining and supporting AzureLib; no help will be given to Geckolib.
-
-Are you a developer and want to use this library in your mod? Add the following to your build.gradle
-
-
-
-```
-repositories {
- // The Maven with the mods source
- maven {url 'https://maven.azuredoom.com/mods'}
-}
-
-dependencies {
-
- //Common 1.20.1+ Latest Only
- compileOnly "mod.azure.azurelib:azurelib-common-MCVERSION:MODVERSION"
-
- //Fabric or Quilt
- modImplementation "mod.azure.azurelib:azurelib-fabric-MCVERSION:MODVERSION"
-
- //NeoForge or Forge 1.20.1
- implementation fg.deobf("mod.azure.azurelib:azurelib-neo-MCVERSION:MODVERSION")
-}
-```
-
-
-
Wiki
You can find the AzureLib Wiki here: https://wiki.azuredoom.com/
diff --git a/build.gradle b/build.gradle
index f4bd4d6e5..db7ceb094 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,119 +1,5 @@
-import groovy.json.JsonOutput
-import groovy.json.JsonSlurper
-
plugins {
- id 'fabric-loom' version '1.2-SNAPSHOT' apply(false)
- id 'net.minecraftforge.gradle' version '[6.0,6.2)' apply(false)
- id 'org.spongepowered.gradle.vanilla' version '0.2.1-SNAPSHOT' apply(false)
- id "org.spongepowered.mixin" version "0.7-SNAPSHOT" apply(false)
-}
-
-version = version
-group = maven_group
-
-subprojects {
- apply plugin: 'java'
-
- java.toolchain.languageVersion = JavaLanguageVersion.of(17)
- java.withSourcesJar()
- java.withJavadocJar()
-
- jar {
- from(rootProject.file("LICENSE")) {
- rename { "${it}_${mod_name}" }
- }
- manifest {
- attributes([
- "Specification-Title" : "AzureLib",
- "Specification-Vendor" : "AzureDoom",
- "Specification-Version" : "1", // We are version 1 of ourselves
- "Implementation-Title" : project.name,
- "Implementation-Version" : project.version,
- "Implementation-Vendor" : "AzureDoom",
- "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
- ])
- }
- }
-
- sourcesJar {
- from(rootProject.file("LICENSE")) {
- rename { "${it}_${mod_name}" }
- }
- }
-
- repositories {
- mavenCentral()
- maven {
- name = 'Sponge / Mixin'
- url = 'https://repo.spongepowered.org/repository/maven-public/'
- }
- maven {
- name = 'BlameJared Maven (JEI / CraftTweaker / Bookshelf)'
- url = 'https://maven.blamejared.com'
- }
- }
-
- tasks.withType(JavaCompile).configureEach {
-
- it.options.encoding = 'UTF-8'
- it.options.getRelease().set(17)
- }
-
- processResources {
- def expandProps = [
- "version" : project.version,
- "group" : project.group, //Else we target the task's group.
- "minecraft_version" : project.minecraft_version,
- "neo_version" : project.neo_version,
- "loader_version_range" : project.loader_version_range,
- "neo_version_range" : project.neo_version_range,
- "minecraft_version_range" : project.minecraft_version_range,
- "fabric_version" : project.fabric_version,
- "fabric_loader_version" : project.fabric_loader_version,
- "mod_name" : project.mod_name,
- "mod_author" : project.mod_author,
- "mod_id" : project.mod_id,
- "mod_license" : project.mod_license,
- "mod_description" : project.mod_description,
- "mod_credits" : project.mod_credits,
- "mod_logo" : project.mod_logo,
- "mod_url" : project.mod_url,
- "mod_issues" : project.mod_issues,
- "mod_sources" : project.mod_sources,
- ]
-
- filesMatching(['pack.mcmeta', 'fabric.mod.json', '*.mixins.json', 'META-INF/mods.toml']) {
- expand expandProps
- }
- inputs.properties(expandProps)
- doLast {
- def jsonMinifyStart = System.currentTimeMillis()
- def jsonMinified = 0
- def jsonBytesSaved = 0
- fileTree(dir: outputs.files.asPath, include: '**/*.json').each {
- File file = it
- jsonMinified++
- def oldLength = file.length()
- file.text = JsonOutput.toJson(new JsonSlurper().parse(file))
- jsonBytesSaved += oldLength - file.length()
- }
- println('Minified ' + jsonMinified + ' json files. Saved ' + jsonBytesSaved + ' bytes. Took ' + (System.currentTimeMillis() - jsonMinifyStart) + 'ms.')
- }
- }
-
- // Disables Gradle's custom module metadata from being published to maven. The
- // metadata includes mapped dependencies which are not reasonably consumable by
- // other mod developers.
- tasks.withType(GenerateModuleMetadata).configureEach {
-
- enabled = false
- }
-
- // Tells gradle to show 1000 errors instead of the default count of 100.
- // See: https://stackoverflow.com/a/31905248
- gradle.projectsEvaluated {
- tasks.withType(JavaCompile) {
- options.compilerArgs << "-Xmaxerrs" << "1000"
- }
- }
+ id 'fabric-loom' version '1.9-SNAPSHOT' apply(false)
+ id 'net.neoforged.moddev.legacyforge' version '2.0.88' apply(false)
+ id 'me.modmuss50.mod-publish-plugin' version "${modmuss50_mod_publish_version}" apply(false)
}
\ No newline at end of file
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 000000000..678405245
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,3 @@
+plugins {
+ id 'groovy-gradle-plugin'
+}
diff --git a/buildSrc/src/main/groovy/multiloader-common.gradle b/buildSrc/src/main/groovy/multiloader-common.gradle
new file mode 100644
index 000000000..15b726215
--- /dev/null
+++ b/buildSrc/src/main/groovy/multiloader-common.gradle
@@ -0,0 +1,185 @@
+plugins {
+ id 'com.diffplug.spotless'
+ id 'java-library'
+ id 'maven-publish'
+ id 'idea'
+}
+
+base {
+ archivesName = "${mod_id}-${project.name}-${minecraft_version}"
+}
+
+java {
+ toolchain.languageVersion = JavaLanguageVersion.of(java_version)
+ withSourcesJar()
+ withJavadocJar()
+}
+
+repositories {
+ mavenCentral()
+ // https://docs.gradle.org/current/userguide/declaring_repositories.html#declaring_content_exclusively_found_in_one_repository
+ exclusiveContent {
+ forRepository {
+ maven {
+ name = 'Sponge'
+ url = 'https://repo.spongepowered.org/repository/maven-public'
+ }
+ }
+ filter { includeGroupAndSubgroups('org.spongepowered') }
+ }
+ exclusiveContent {
+ forRepositories(
+ maven {
+ name = 'ParchmentMC'
+ url = 'https://maven.parchmentmc.org/'
+ },
+ maven {
+ name = "NeoForge"
+ url = 'https://maven.neoforged.net/releases'
+ }
+ )
+ filter { includeGroup('org.parchmentmc.data') }
+ }
+ maven {
+ name = 'BlameJared'
+ url = 'https://maven.blamejared.com'
+ }
+ maven { url "https://api.modrinth.com/maven" }
+ maven { url "https://cfa2.cursemaven.com" }
+ maven { url "https://maven.terraformersmc.com/" }
+ maven { url "https://maven.terraformersmc.com/releases" } // modmenu
+}
+
+dependencies {
+ implementation 'org.jetbrains:annotations:24.1.0'
+}
+
+['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant ->
+ configurations."$variant".outgoing {
+ capability("$group:${project.name}:$version")
+ capability("$group:${base.archivesName.get()}:$version")
+ capability("$group:$mod_id-${project.name}-${minecraft_version}:$version")
+ capability("$group:$mod_id:$version")
+ }
+ publishing.publications.configureEach {
+ suppressPomMetadataWarningsFor(variant)
+ }
+}
+
+sourcesJar {
+ from(rootProject.file('LICENSE')) {
+ rename { "${it}_${mod_name}" }
+ }
+}
+
+jar {
+ from(rootProject.file('LICENSE')) {
+ rename { "${it}_${mod_name}" }
+ }
+
+ manifest {
+ attributes([
+ 'Specification-Title' : mod_name,
+ 'Specification-Vendor' : mod_author,
+ 'Specification-Version' : project.jar.archiveVersion,
+ 'Implementation-Title' : project.name,
+ 'Implementation-Version': project.jar.archiveVersion,
+ 'Implementation-Vendor' : mod_author,
+ 'Built-On-Minecraft' : minecraft_version
+ ])
+ }
+}
+
+processResources {
+ var expandProps = [
+ "version" : project.version,
+ "group" : project.group, //Else we target the task's group.
+ "minecraft_version" : project.minecraft_version,
+ "neo_version" : project.neo_version,
+ "loader_version_range" : project.loader_version_range,
+ "neo_version_range" : project.neo_version_range,
+ "minecraft_version_range" : project.minecraft_version_range,
+ "fabric_version" : project.fabric_version,
+ "fabric_loader_version" : project.fabric_loader_version,
+ "mod_name" : project.mod_name,
+ "mod_author" : project.mod_author,
+ "mod_id" : project.mod_id,
+ "mod_license" : project.mod_license,
+ "mod_description" : project.mod_description,
+ "mod_credits" : project.mod_credits,
+ "mod_logo" : project.mod_logo,
+ "mod_url" : project.mod_url,
+ "mod_issues" : project.mod_issues,
+ "mod_sources" : project.mod_sources,
+ ]
+
+ var jsonExpandProps = expandProps.collectEntries {
+ key, value -> [(key): value instanceof String ? value.replace("\n", "\\\\n") : value]
+ }
+
+ filesMatching(['META-INF/mods.toml']) {
+ expand expandProps
+ }
+
+ filesMatching(['pack.mcmeta', 'fabric.mod.json', '*.mixins.json']) {
+ expand jsonExpandProps
+ }
+
+ inputs.properties(expandProps)
+}
+
+publishing {
+ repositories {
+ maven {
+ name = project.mod_id
+ url = project.maven_url
+ credentials(PasswordCredentials)
+ authentication {
+ basic(BasicAuthentication)
+ }
+ }
+ }
+ publications {
+ maven(MavenPublication) {
+ artifactId base.archivesName.get()
+ from components.java
+ pom.withXml {
+ asNode().dependencies.dependency.each { dep ->
+ if (dep.groupId.text() == 'com.terraformersmc' && dep.artifactId.text() == 'modmenu') {
+ dep.parent().remove(dep)
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// Disables Gradle's custom module metadata from being published to maven. The
+// metadata includes mapped dependencies which are not reasonably consumable by
+// other mod developers.
+tasks.withType(GenerateModuleMetadata).configureEach {
+ enabled = false
+}
+
+idea {
+ module {
+ downloadSources = true
+ downloadJavadoc = true
+ }
+}
+
+spotless {
+ java {
+ eclipse().configFile("$rootDir/eclipse-formatter.xml")
+ endWithNewline()
+ importOrder("", "java", group.toString(), "\\#")
+ indentWithSpaces(4)
+ removeUnusedImports()
+ trimTrailingWhitespace()
+ }
+}
+
+tasks.build {
+ dependsOn("spotlessApply")
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/multiloader-loader.gradle b/buildSrc/src/main/groovy/multiloader-loader.gradle
new file mode 100644
index 000000000..92e23251e
--- /dev/null
+++ b/buildSrc/src/main/groovy/multiloader-loader.gradle
@@ -0,0 +1,44 @@
+plugins {
+ id 'multiloader-common'
+}
+
+configurations {
+ commonJava{
+ canBeResolved = true
+ }
+ commonResources{
+ canBeResolved = true
+ }
+}
+
+dependencies {
+ compileOnly(project(':common')) {
+ capabilities {
+ requireCapability "$group:$mod_id"
+ }
+ }
+ commonJava project(path: ':common', configuration: 'commonJava')
+ commonResources project(path: ':common', configuration: 'commonResources')
+}
+
+tasks.named('compileJava', JavaCompile) {
+ dependsOn(configurations.commonJava)
+ source(configurations.commonJava)
+}
+
+processResources {
+ dependsOn(configurations.commonResources)
+ from(configurations.commonResources)
+}
+
+tasks.named('javadoc', Javadoc).configure {
+ dependsOn(configurations.commonJava)
+ source(configurations.commonJava)
+}
+
+tasks.named('sourcesJar', Jar) {
+ dependsOn(configurations.commonJava)
+ from(configurations.commonJava)
+ dependsOn(configurations.commonResources)
+ from(configurations.commonResources)
+}
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 000000000..c4f548f8b
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,3 @@
+v3.0.0
+
+- Complete backport of AzureLib 3.x, please see: https://moddedmc.wiki/en/project/azurelib/docs Updating Guide for updating to this version.
\ No newline at end of file
diff --git a/changelog.txt b/changelog.txt
deleted file mode 100644
index cd8206488..000000000
--- a/changelog.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-v2.0.41
-
-- Revert GeoGlowingTextureMeta change.
\ No newline at end of file
diff --git a/common/build.gradle b/common/build.gradle
index 8216986b1..b5f367c8c 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -1,39 +1,39 @@
plugins {
- id 'idea'
- id 'java'
- id 'maven-publish'
- id 'org.spongepowered.gradle.vanilla'
-}
-base {
- archivesName = "${mod_id}-common-${minecraft_version}"
+ id 'multiloader-common'
+ id 'net.neoforged.moddev.legacyforge'
+ id 'com.diffplug.spotless' version "7.0.0.BETA3"
}
-minecraft {
- version(minecraft_version)
- if (file("src/main/resources/${mod_id}.aw").exists()) {
- accessWideners(file("src/main/resources/${mod_id}.aw"))
+legacyForge {
+ mcpVersion = minecraft_version
+ def at = file('src/main/resources/META-INF/accesstransformer.cfg')
+ if (at.exists()) {
+ accessTransformers.from(at.absolutePath)
+ }
+ parchment {
+ minecraftVersion = parchment_minecraft
+ mappingsVersion = parchment_version
}
}
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 {
- repositories {
- maven {
- name = "azurelib"
- url = "https://maven.azuredoom.com/mods"
- credentials(PasswordCredentials)
- authentication {
- basic(BasicAuthentication)
- }
- }
+configurations {
+ commonJava {
+ canBeResolved = false
+ canBeConsumed = true
}
- publications {
- maven(MavenPublication) {
- artifactId base.archivesName.get()
- from components.java
- }
+ commonResources {
+ canBeResolved = false
+ canBeConsumed = true
}
+}
+
+artifacts {
+ commonJava sourceSets.main.java.sourceDirectories.singleFile
+ commonResources sourceSets.main.resources.sourceDirectories.singleFile
}
\ No newline at end of file
diff --git a/common/src/main/java/mod/azure/azurelib/AzureLib.java b/common/src/main/java/mod/azure/azurelib/AzureLib.java
index 51d207d90..46e0247b1 100644
--- a/common/src/main/java/mod/azure/azurelib/AzureLib.java
+++ b/common/src/main/java/mod/azure/azurelib/AzureLib.java
@@ -18,6 +18,7 @@ public class AzureLib {
public static final Logger LOGGER = LogManager.getLogger("azurelib");
public static final Marker MAIN_MARKER = MarkerManager.getMarker("main");
public static final String MOD_ID = "azurelib";
+ public static final String ITEM_UUID_TAG = "az_id";
public static boolean hasInitialized;
public static void initialize() {
@@ -27,7 +28,7 @@ public static void initialize() {
hasInitialized = true;
}
- public static final ResourceLocation modResource(String name) {
+ public static ResourceLocation modResource(String name) {
return new ResourceLocation(MOD_ID, name);
}
}
diff --git a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java
index 96119c668..ff06c9692 100644
--- a/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java
+++ b/common/src/main/java/mod/azure/azurelib/ai/pathing/AzureNavigation.java
@@ -112,12 +112,31 @@ public boolean moveTo(Entity entity, double d) {
return true;
}
- @Override
- public void tick() {
- super.tick();
+ /**
+ * Ensures a minimum width of 1.0 for entities, addressing an issue
+ * where smaller entities (less than 0.8 units in width) encounter pathfinding
+ * failures. This resolves bugs such as MC-226637, where small entities
+ * end up "spinning" due to improper navigation logic.
+ *
+ * By enforcing a minimum calculated width of 1.0, this method prevents
+ * the pathfinding system from failing on smaller entities, while leaving
+ * the behavior of larger entities unchanged. It may also reduce
+ * performance overhead by preventing frequent hitbox and AI updates
+ * caused by entity spinning.
+ *
+ * @author Modrome
+ * @return the maximum of the entity's actual width and 1.0, ensuring a minimum width for correct pathing.
+ */
+ public float getMinimumWidth() {
+ return Math.max(this.mob.getBbWidth(),1.0F); //Return whichever value is greater, for small entities, this returns 1.0 no matter what, fixing our spinning entities.
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
if (this.isDone()) {
if (this.pathToPosition != null) {
- if (this.pathToPosition.closerToCenterThan(this.mob.position(), this.mob.getBbWidth()) || this.mob.getY() > (double)this.pathToPosition.getY() && BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ()).closerToCenterThan(this.mob.position(), this.mob.getBbWidth())) {
+ if (this.pathToPosition.closerToCenterThan(this.mob.position(), getMinimumWidth()) || this.mob.getY() > this.pathToPosition.getY() && BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ()).closerToCenterThan(this.mob.position(), getMinimumWidth())) {
this.pathToPosition = null;
} else {
this.mob.getMoveControl().setWantedPosition(this.pathToPosition.getX(), this.pathToPosition.getY(), this.pathToPosition.getZ(), this.speedModifier);
@@ -125,9 +144,9 @@ public void tick() {
}
return;
}
- if (this.getTargetPos() != null)
- this.mob.getLookControl().setLookAt(this.getTargetPos().getX(), this.getTargetPos().getY(), this.getTargetPos().getZ());
- }
+ if (this.getTargetPos() != null)
+ this.mob.getLookControl().setLookAt(this.getTargetPos().getX(), this.getTargetPos().getY(), this.getTargetPos().getZ());
+ }
private boolean isAt(Path path, float threshold) {
final Vec3 pathPos = path.getNextEntityPos(this.mob);
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/builders/AzureGunProperties.java b/common/src/main/java/mod/azure/azurelib/builders/AzureGunProperties.java
index d7dc2e2dc..aa727039f 100644
--- a/common/src/main/java/mod/azure/azurelib/builders/AzureGunProperties.java
+++ b/common/src/main/java/mod/azure/azurelib/builders/AzureGunProperties.java
@@ -10,6 +10,7 @@
*
* @author AzureDoom/Boston Vanseghi
*/
+@Deprecated(forRemoval = true)
public class AzureGunProperties {
private AzureGunProperties properties;
private int ammoCount;
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..9f0550184 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/AzureLibCache.java
@@ -7,11 +7,12 @@
import mod.azure.azurelib.cache.object.BakedGeoModel;
import mod.azure.azurelib.core.animatable.model.CoreGeoModel;
import mod.azure.azurelib.loading.FileLoader;
-import mod.azure.azurelib.loading.json.FormatVersion;
import mod.azure.azurelib.loading.json.raw.Model;
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;
@@ -34,6 +35,7 @@
* {@link mod.azure.azurelib.core.animation.Animation Animations} and
* {@link CoreGeoModel Models}
*/
+@Deprecated(forRemoval = true)
public final class AzureLibCache {
private static final Set EXCLUDED_NAMESPACES = ObjectOpenHashSet.of("moreplayermodels", "customnpcs", "gunsrpg", "born_in_chaos_v1");
@@ -55,7 +57,7 @@ public static Map getBakedModels() {
}
public static void registerReloadListener() {
- Minecraft mc = Minecraft.getInstance();
+ var mc = Minecraft.getInstance();
if (mc == null) return;
@@ -72,20 +74,33 @@ 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);
}
+ /**
+ * @deprecated
+ */
private static CompletableFuture loadAnimations(Executor backgroundExecutor, ResourceManager resourceManager,
BiConsumer elementConsumer) {
return loadResources(backgroundExecutor, resourceManager, "animations",
resource -> FileLoader.loadAnimationsFile(resource, resourceManager), elementConsumer);
}
+ /**
+ * @deprecated
+ */
private static CompletableFuture loadModels(Executor backgroundExecutor, ResourceManager resourceManager,
BiConsumer elementConsumer) {
return loadResources(backgroundExecutor, resourceManager, "geo", resource -> {
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/texture/AnimatableTexture.java b/common/src/main/java/mod/azure/azurelib/cache/texture/AnimatableTexture.java
index 347d038b1..910a8fdba 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/texture/AnimatableTexture.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/texture/AnimatableTexture.java
@@ -15,6 +15,7 @@
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import mod.azure.azurelib.AzureLib;
+import mod.azure.azurelib.util.RenderUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.AbstractTexture;
import net.minecraft.client.renderer.texture.SimpleTexture;
@@ -37,8 +38,9 @@
* Wrapper for {@link SimpleTexture SimpleTexture} implementation allowing for casual use of animated non-atlas textures
*/
public class AnimatableTexture extends SimpleTexture {
- private AnimationContents animationContents = null;
- private boolean isAnimated = false;
+ protected AnimationContents animationContents = null;
+
+ protected boolean isAnimated = false;
public AnimatableTexture(final ResourceLocation location) {
super(location);
@@ -47,33 +49,29 @@ public AnimatableTexture(final ResourceLocation location) {
@Override
public void load(ResourceManager manager) throws IOException {
Resource resource = manager.getResourceOrThrow(this.location);
+ AnimationMetadataSection animMeta = resource.metadata().getSection(AnimationMetadataSection.SERIALIZER).orElse(null);
- try {
+ if (animMeta != null) {
NativeImage nativeImage;
try (InputStream inputstream = resource.open()) {
nativeImage = NativeImage.read(inputstream);
}
- this.animationContents = resource.metadata().getSection(AnimationMetadataSection.SERIALIZER).map(animMeta -> new AnimationContents(nativeImage, animMeta)).orElse(null);
+ this.animationContents = new AnimationContents(nativeImage, animMeta);
- if (this.animationContents != null) {
- if (!this.animationContents.isValid()) {
- nativeImage.close();
+ if (!this.animationContents.isValid()) {
+ nativeImage.close();
- return;
- }
+ return;
+ }
- this.isAnimated = true;
+ this.isAnimated = true;
- onRenderThread(() -> {
- TextureUtil.prepareImage(getId(), 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height());
- nativeImage.upload(0, 0, 0, 0, 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height(), false, false);
- });
- }
- }
- catch (RuntimeException exception) {
- AzureLib.LOGGER.warn("Failed reading metadata of: {}", this.location, exception);
+ onRenderThread(() -> {
+ TextureUtil.prepareImage(getId(), 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height());
+ nativeImage.upload(0, 0, 0, 0, 0, this.animationContents.frameSize.width(), this.animationContents.frameSize.height(), false, false);
+ });
}
}
@@ -86,6 +84,10 @@ public boolean isAnimated() {
return this.isAnimated;
}
+ public static void setAndUpdate(ResourceLocation texturePath) {
+ setAndUpdate(texturePath, (int) RenderUtils.getCurrentTick());
+ }
+
public static void setAndUpdate(ResourceLocation texturePath, int frameTick) {
AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(texturePath);
@@ -108,9 +110,9 @@ private static void onRenderThread(RenderCall renderCall) {
}
}
- private class AnimationContents {
- private final FrameSize frameSize;
- private final Texture animatedTexture;
+ protected class AnimationContents {
+ protected final FrameSize frameSize;
+ protected final Texture animatedTexture;
private AnimationContents(NativeImage image, AnimationMetadataSection animMeta) {
this.frameSize = animMeta.calculateFrameSize(image.getWidth(), image.getHeight());
@@ -172,7 +174,7 @@ private Texture generateAnimatedTexture(NativeImage image, AnimationMetadataSect
private record Frame(int index, int time) {
}
- private class Texture implements AutoCloseable {
+ public class Texture implements AutoCloseable {
private final NativeImage baseImage;
private final Frame[] frames;
private final int framePanelSize;
@@ -180,6 +182,10 @@ private class Texture implements AutoCloseable {
private final NativeImage interpolatedFrame;
private final int totalFrameTime;
+ protected int glowMaskTextureId = -1;
+ protected NativeImage glowmaskImage = null;
+ protected NativeImage glowmaskInterpolatedFrame = null;
+
private int currentFrame;
private int currentSubframe;
@@ -207,6 +213,13 @@ private int getFrameY(int frameIndex) {
return frameIndex / this.framePanelSize;
}
+ public void setGlowMaskTexture(AutoGlowingTexture texture, NativeImage baseImage, NativeImage glowMask) {
+ this.glowMaskTextureId = texture.getId();
+ this.glowmaskImage = glowMask;
+ this.glowmaskInterpolatedFrame = this.interpolating ? new NativeImage(AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(), false) : null;
+ this.baseImage.copyFrom(baseImage);
+ }
+
public void setCurrentFrame(int ticks) {
ticks %= this.totalFrameTime;
@@ -237,43 +250,49 @@ public void setCurrentFrame(int ticks) {
getFrameY(this.currentFrame) * AnimationContents.this.frameSize.height(),
AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(),
false, false);
+
+ if (this.glowmaskImage != null) {
+ TextureUtil.prepareImage(this.glowMaskTextureId, 0, AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height());
+ this.glowmaskImage.upload(0, 0, 0, getFrameX(this.currentFrame) * AnimationContents.this.frameSize.width(), getFrameY(this.currentFrame) * AnimationContents.this.frameSize.height(), AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(), false, false);
+ }
});
} else if (this.currentSubframe != lastSubframe && this.interpolating) {
- onRenderThread(this::generateInterpolatedFrame);
+ onRenderThread(() -> {
+ generateInterpolatedFrame(getId(), this.baseImage, this.interpolatedFrame);
+
+ if (this.glowmaskImage != null) {
+ generateInterpolatedFrame(this.glowMaskTextureId, this.glowmaskImage,
+ this.glowmaskInterpolatedFrame);
+ }
+ });
}
}
- private void generateInterpolatedFrame() {
+ private void generateInterpolatedFrame(int textureId, NativeImage image, NativeImage interpolatedFrame) {
Frame frame = this.frames[this.currentFrame];
- double frameProgress = 1 - (double) this.currentSubframe / (double) frame.time;
- int nextFrameIndex = this.frames[(this.currentFrame + 1) % this.frames.length].index;
-
- if (frame.index != nextFrameIndex) {
- for (int y = 0; y < this.interpolatedFrame.getHeight(); ++y) {
- for (int x = 0; x < this.interpolatedFrame.getWidth(); ++x) {
- int prevFramePixel = getPixel(frame.index, x, y);
- int nextFramePixel = getPixel(nextFrameIndex, x, y);
- int blendedRed = interpolate(frameProgress, prevFramePixel >> 16 & 255,
- nextFramePixel >> 16 & 255);
- int blendedGreen = interpolate(frameProgress, prevFramePixel >> 8 & 255,
- nextFramePixel >> 8 & 255);
+ double frameProgress = 1 - (double)this.currentSubframe / (double)frame.time();
+ int nextFrameIndex = this.frames[(this.currentFrame + 1) % this.frames.length].index();
+
+ if (frame.index() != nextFrameIndex) {
+ for (int y = 0; y < interpolatedFrame.getHeight(); ++y) {
+ for (int x = 0; x < interpolatedFrame.getWidth(); ++x) {
+ int prevFramePixel = getPixel(image, frame.index(), x, y);
+ int nextFramePixel = getPixel(image, nextFrameIndex, x, y);
+ int blendedRed = interpolate(frameProgress, prevFramePixel >> 16 & 255, nextFramePixel >> 16 & 255);
+ int blendedGreen = interpolate(frameProgress, prevFramePixel >> 8 & 255, nextFramePixel >> 8 & 255);
int blendedBlue = interpolate(frameProgress, prevFramePixel & 255, nextFramePixel & 255);
- this.interpolatedFrame.setPixelRGBA(x, y,
- prevFramePixel & -16777216 | blendedRed << 16 | blendedGreen << 8 | blendedBlue);
+ interpolatedFrame.setPixelRGBA(x, y, prevFramePixel & -16777216 | blendedRed << 16 | blendedGreen << 8 | blendedBlue);
}
}
- TextureUtil.prepareImage(AnimatableTexture.this.getId(), 0,
- AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height());
- this.interpolatedFrame.upload(0, 0, 0, 0, 0, AnimationContents.this.frameSize.width(),
- AnimationContents.this.frameSize.height(), false, false);
+ TextureUtil.prepareImage(textureId, 0, AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height());
+ interpolatedFrame.upload(0, 0, 0, 0, 0, AnimationContents.this.frameSize.width(), AnimationContents.this.frameSize.height(), false, false);
}
}
- private int getPixel(int frameIndex, int x, int y) {
- return this.baseImage.getPixelRGBA(x + getFrameX(frameIndex) * AnimationContents.this.frameSize.width(),
- y + getFrameY(frameIndex) * AnimationContents.this.frameSize.height());
+ private int getPixel(NativeImage image, int frameIndex, int x, int y) {
+ return image.getPixelRGBA(x + getFrameX(frameIndex) * AnimationContents.this.frameSize.width(), y + getFrameY(frameIndex) * AnimationContents.this.frameSize.height());
}
private int interpolate(double frameProgress, double prevColour, double nextColour) {
@@ -284,8 +303,17 @@ private int interpolate(double frameProgress, double prevColour, double nextColo
public void close() {
this.baseImage.close();
- if (this.interpolatedFrame != null)
+ if (this.interpolatedFrame != null) {
this.interpolatedFrame.close();
+ }
+
+ if (this.glowmaskImage != null) {
+ this.glowmaskImage.close();
+ }
+
+ if (this.glowmaskInterpolatedFrame != null) {
+ this.glowmaskInterpolatedFrame.close();
+ }
}
}
}
diff --git a/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java b/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java
index 012f561d7..92818b59c 100644
--- a/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java
+++ b/common/src/main/java/mod/azure/azurelib/cache/texture/AutoGlowingTexture.java
@@ -32,6 +32,7 @@
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
+import java.util.function.BiFunction;
import java.util.function.Function;
/**
@@ -47,11 +48,31 @@ public class AutoGlowingTexture extends GeoAbstractTexture {
RenderSystem.defaultBlendFunc();
});
private static final RenderStateShard.WriteMaskStateShard WRITE_MASK = new RenderStateShard.WriteMaskStateShard(true, true);
- private static final Function RENDER_TYPE_FUNCTION = Util.memoize(texture -> {
- RenderStateShard.TextureStateShard textureState = new RenderStateShard.TextureStateShard(texture, false, false);
- return RenderType.create("geo_glowing_layer", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, true, RenderType.CompositeState.builder().setShaderState(SHADER_STATE).setTextureState(textureState).setTransparencyState(TRANSPARENCY_STATE).setWriteMaskState(WRITE_MASK).createCompositeState(false));
- });
+ protected static final BiFunction GLOWING_RENDER_TYPE = Util.memoize(
+ (texture, isGlowing) -> {
+ RenderStateShard.TextureStateShard textureState = new RenderStateShard.TextureStateShard(
+ texture,
+ false,
+ false
+ );
+
+ return RenderType.create(
+ "az_glowing_layer",
+ DefaultVertexFormat.NEW_ENTITY,
+ VertexFormat.Mode.QUADS,
+ 256,
+ false,
+ true,
+ RenderType.CompositeState.builder()
+ .setShaderState(SHADER_STATE)
+ .setTextureState(textureState)
+ .setTransparencyState(TRANSPARENCY_STATE)
+ .setWriteMaskState(WRITE_MASK)
+ .createCompositeState(isGlowing)
+ );
+ }
+ );
private static final String APPENDIX = "_glowmask";
@@ -128,11 +149,20 @@ protected RenderCall loadTexture(ResourceManager resourceManager, Minecraft mc)
NativeImage mask = glowImage;
- if (mask == null)
+ if (mask == null) {
+ String expectedGlowmask = this.textureBase.toString().replace(".png", "_glowmask.png");
+ AzureLib.LOGGER.warn("Missing glowmask texture. Base texture: {}, Expected glowmask: {}", this.textureBase, expectedGlowmask);
return null;
+ }
+
+ boolean animated = originalTexture instanceof AnimatableTexture animatableTexture && animatableTexture.isAnimated();
+
+ if (animated)
+ ((AnimatableTexture)originalTexture).animationContents.animatedTexture.setGlowMaskTexture(this, baseImage, mask);
return () -> {
- uploadSimple(getId(), mask, blur, clamp);
+ if (!animated)
+ uploadSimple(getId(), mask, blur, clamp);
if (originalTexture instanceof DynamicTexture dynamicTexture) {
dynamicTexture.upload();
@@ -148,6 +178,16 @@ protected RenderCall loadTexture(ResourceManager resourceManager, Minecraft mc)
* @param texture The texture of the resource to apply a glow layer to
*/
public static RenderType getRenderType(ResourceLocation texture) {
- return RENDER_TYPE_FUNCTION.apply(getEmissiveResource(texture));
+ return GLOWING_RENDER_TYPE.apply(getEmissiveResource(texture), false);
+ }
+
+ /**
+ * Return a cached instance of the RenderType for the given texture for AutoGlowingGeoLayer rendering, while the
+ * entity has an outline
+ *
+ * @param texture The texture of the resource to apply a glow layer to
+ */
+ public static RenderType getOutlineRenderType(ResourceLocation texture) {
+ return GLOWING_RENDER_TYPE.apply(getEmissiveResource(texture), true);
}
}
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/client/screen/OptifineWarningScreen.java b/common/src/main/java/mod/azure/azurelib/client/screen/OptifineWarningScreen.java
deleted file mode 100644
index 702f31f07..000000000
--- a/common/src/main/java/mod/azure/azurelib/client/screen/OptifineWarningScreen.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package mod.azure.azurelib.client.screen;
-
-import mod.azure.azurelib.mixins.AccessorWarningScreen;
-import mod.azure.azurelib.platform.Services;
-import net.minecraft.ChatFormatting;
-import net.minecraft.Util;
-import net.minecraft.client.gui.components.Button;
-import net.minecraft.client.gui.components.MultiLineLabel;
-import net.minecraft.client.gui.screens.multiplayer.WarningScreen;
-import net.minecraft.network.chat.Component;
-import net.minecraft.network.chat.MutableComponent;
-
-public class OptifineWarningScreen extends WarningScreen {
- public OptifineWarningScreen() {
- super(HEADER, MESSAGE, CHECK_MESSAGE, NARRATED_TEXT);
- }
-
- @Override
- protected void initButtons(int yOffset) {
- addRenderableWidget(
- Button.builder(OPEN_MODS_FOLDER, buttonWidget -> Util.getPlatform().openFile(Services.PLATFORM.modsDir().toFile()))
- .bounds(width / 2 - 155, 100 + yOffset, 150, 20)
- .build()
- );
-
- addRenderableWidget(
- Button.builder(OPTIFINE_ALTERNATIVES, buttonWidget -> Util.getPlatform().openUri(
- "https://prismlauncher.org/wiki/getting-started/install-of-alternatives/"
- ))
- .bounds(width / 2 - 155 + 160, 100 + yOffset, 150, 20)
- .build()
- );
-
- addRenderableWidget(
- Button.builder(QUIT_GAME, buttonWidget -> this.minecraft.stop())
- .bounds(width / 2 - 75, 130 + yOffset, 150, 20)
- .build()
- );
- }
-
- @Override
- protected void init() {
- ((AccessorWarningScreen) this).setMessageText(MultiLineLabel.create(font, MESSAGE, width - 50));
- int yOffset = (((AccessorWarningScreen) this).getMessageText().getLineCount() + 1) * font.lineHeight * 2 - 20;
- initButtons(yOffset);
- }
-
-
- @Override
- public boolean shouldCloseOnEsc() {
- return false;
- }
-
- private static final MutableComponent HEADER = Component.translatable("header.azurelib.optifine").withStyle(ChatFormatting.DARK_RED, ChatFormatting.BOLD);
- private static final Component MESSAGE = Component.translatable("message.azurelib.optifine");
- private static final Component CHECK_MESSAGE = Component.translatable("multiplayerWarning.check");
- private static final Component NARRATED_TEXT = HEADER.copy().append("\n").append(MESSAGE);
-
- private static final Component OPEN_MODS_FOLDER = Component.translatable("label.azurelib.open_mods_folder");
- private static final Component OPTIFINE_ALTERNATIVES = Component.translatable("label.azurelib.optifine_alternatives");
- private static final Component QUIT_GAME = Component.translatable("menu.quit");
-}
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/core/animatable/GeoAnimatable.java b/common/src/main/java/mod/azure/azurelib/core/animatable/GeoAnimatable.java
index 3f7dacc2a..38ca97058 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animatable/GeoAnimatable.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animatable/GeoAnimatable.java
@@ -28,6 +28,7 @@
* {@code GeoItem}
*
*/
+@Deprecated(forRemoval = true)
public interface GeoAnimatable {
/**
* Register your {@link AnimationController AnimationControllers} and their respective animations and conditions.
diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java
index 5a12d9137..b9136e1b6 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/AnimatableInstanceCache.java
@@ -15,6 +15,7 @@
* The base cache class responsible for returning the {@link AnimatableManager} for a given instanceof of a {@link GeoAnimatable}.
* This class is abstracted and not intended for direct use. See either {@link SingletonAnimatableInstanceCache} or {@link InstancedAnimatableInstanceCache}
*/
+@Deprecated(forRemoval = true)
public abstract class AnimatableInstanceCache {
protected final GeoAnimatable animatable;
diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java
index 6ce4b735c..fe9a1fff8 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/InstancedAnimatableInstanceCache.java
@@ -13,6 +13,7 @@
/**
* AnimatableInstanceCache implementation for instantiated objects such as Entities or BlockEntities. Returns a single {@link AnimatableManager} instance per cache.
*/
+@Deprecated(forRemoval = true)
public class InstancedAnimatableInstanceCache extends AnimatableInstanceCache {
protected AnimatableManager> manager;
diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java
index e9cea59f0..716d12650 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animatable/instance/SingletonAnimatableInstanceCache.java
@@ -15,6 +15,7 @@
/**
* AnimatableInstanceCache implementation for singleton/flyweight objects such as Items. Utilises a keyed map to differentiate different instances of the object.
*/
+@Deprecated(forRemoval = true)
public class SingletonAnimatableInstanceCache extends AnimatableInstanceCache {
protected final Long2ObjectMap> managers = new Long2ObjectOpenHashMap<>();
diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java
index 6664b8c81..7e253b4b8 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreBakedGeoModel.java
@@ -14,6 +14,7 @@
* Baked model object for AzureLib models.
* Mostly an internal placeholder to allow for splitting up core (non-Minecraft) libraries
*/
+@Deprecated(forRemoval = true)
public interface CoreBakedGeoModel {
List extends CoreGeoBone> getBones();
/**
diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java
index c1c4d249e..95cfa23d2 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoBone.java
@@ -15,6 +15,7 @@
* Base class for AzureLib {@link CoreGeoModel model} bones.
* Mostly a placeholder to allow for splitting up core (non-Minecraft) libraries
*/
+@Deprecated(forRemoval = true)
public interface CoreGeoBone {
String getName();
diff --git a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java
index cb05f9d03..81171d9e1 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animatable/model/CoreGeoModel.java
@@ -18,6 +18,7 @@
* Base class for AzureLib models.
* Mostly an internal placeholder to allow for splitting up core (non-Minecraft) libraries
*/
+@Deprecated(forRemoval = true)
public interface CoreGeoModel {
/**
* Get the baked model data for this model based on the provided string location
diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java
index a3e382bf0..45933dee6 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimatableManager.java
@@ -29,6 +29,7 @@
* will have a single instance of {@code AnimatableManager} associated with it.
*
*/
+@Deprecated(forRemoval = true)
public class AnimatableManager {
private final Map boneSnapshotCollection = new Object2ObjectOpenHashMap<>();
private final Map> animationControllers;
diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java
index df788df5c..1d168b415 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationController.java
@@ -44,6 +44,7 @@
* 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.
*/
+@Deprecated(forRemoval = true)
public class AnimationController {
protected final T animatable;
protected final String name;
diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java
index fee3ff5f3..fedb1898c 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationProcessor.java
@@ -22,6 +22,7 @@
import mod.azure.azurelib.core.state.BoneSnapshot;
import mod.azure.azurelib.core.utils.Interpolations;
+@Deprecated(forRemoval = true)
public class AnimationProcessor {
private final Map bones = new Object2ObjectOpenHashMap<>();
private final CoreGeoModel model;
diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java
index 99781c56e..6f6e7bd41 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animation/AnimationState.java
@@ -20,6 +20,7 @@
* This is where users would set their selected animation to play,
* stop the controller, or any number of other animation-related actions.
*/
+@Deprecated(forRemoval = true)
public class AnimationState {
private final T animatable;
private final float limbSwing;
diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java b/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java
index ed374d424..0fb62707e 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animation/ContextAwareAnimatableManager.java
@@ -20,6 +20,7 @@
* This can be used for things like perspective-dependent animation handling and other similar functionality.
* This relies entirely on data present in {@link AnimatableManager#extraData} saved to this manager to determine context
*/
+@Deprecated(forRemoval = true)
public abstract class ContextAwareAnimatableManager extends AnimatableManager {
private final Map> managers;
diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java b/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java
index 732b04d67..d55ade474 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animation/EasingType.java
@@ -27,6 +27,7 @@
* Easings.net
* Cubic-Bezier.com
*/
+@Deprecated(forRemoval = true)
@FunctionalInterface
public interface EasingType {
Map EASING_TYPES = new ConcurrentHashMap<>(64);
diff --git a/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java b/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java
index c19c0199d..dab66ee5f 100644
--- a/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java
+++ b/common/src/main/java/mod/azure/azurelib/core/animation/RawAnimation.java
@@ -27,6 +27,7 @@
* Example usage:
* {@code RawAnimation.begin().thenPlay("action.open_box").thenLoop("state.stay_open")}
*/
+@Deprecated(forRemoval = true)
public final class RawAnimation {
private final List animationList = new ObjectArrayList<>();
diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java
index 713e9b2e8..0aa2af659 100644
--- a/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java
+++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/AnimationPointQueue.java
@@ -19,6 +19,7 @@
* An {@link AnimationPoint} queue holds a queue of {@code AnimationPoints} which are used in
* the {@link mod.azure.azurelib.core.animation.AnimationController} to lerp between values
*/
+@Deprecated(forRemoval = true)
public final class AnimationPointQueue extends LinkedList {
@Serial
private static final long serialVersionUID = 5472797438476621193L;
diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java
index 5bd33545c..c6b756cea 100644
--- a/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java
+++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/BoneAnimationQueue.java
@@ -19,6 +19,7 @@
* 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
*/
+@Deprecated(forRemoval = true)
public record BoneAnimationQueue(CoreGeoBone bone, AnimationPointQueue rotationXQueue, AnimationPointQueue rotationYQueue,
AnimationPointQueue rotationZQueue, AnimationPointQueue positionXQueue, AnimationPointQueue positionYQueue,
AnimationPointQueue positionZQueue, AnimationPointQueue scaleXQueue, AnimationPointQueue scaleYQueue,
diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java
index 24047210c..82f594804 100644
--- a/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java
+++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/KeyframeLocation.java
@@ -12,8 +12,6 @@
package mod.azure.azurelib.core.keyframe;
-import mod.azure.azurelib.core.math.IValue;
-
/**
* A named pair object that stores a {@link Keyframe} and a double representing a temporally placed {@code Keyframe}
* @param keyframe The {@code Keyframe} at the tick time
diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java
index 6a54b6f2b..b30757c41 100644
--- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java
+++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/CustomInstructionKeyframeEvent.java
@@ -20,6 +20,7 @@
* The {@link KeyFrameEvent} specific to the {@link AnimationController#customKeyframeHandler}.
* Called when a custom instruction keyframe is encountered
*/
+@Deprecated(forRemoval = true)
public class CustomInstructionKeyframeEvent extends KeyFrameEvent {
public CustomInstructionKeyframeEvent(T entity, double animationTick, AnimationController controller,
CustomInstructionKeyframeData customInstructionKeyframeData) {
diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java
index ce6b60373..083a115df 100644
--- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java
+++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/KeyFrameEvent.java
@@ -24,6 +24,7 @@
* @see ParticleKeyframeEvent
* @see SoundKeyframeEvent
*/
+@Deprecated(forRemoval = true)
public abstract class KeyFrameEvent {
private final T animatable;
private final double animationTick;
diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java
index 738e7a510..3abe01798 100644
--- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java
+++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/ParticleKeyframeEvent.java
@@ -15,6 +15,7 @@
* The {@link KeyFrameEvent} specific to the {@link AnimationController#particleKeyframeHandler}.
* Called when a particle instruction keyframe is encountered
*/
+@Deprecated(forRemoval = true)
public class ParticleKeyframeEvent extends KeyFrameEvent {
public ParticleKeyframeEvent(T animatable, double animationTick, AnimationController controller, ParticleKeyframeData particleKeyFrameData) {
super(animatable, animationTick, controller, particleKeyFrameData);
diff --git a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java
index b2c98a3ba..2f5f1e035 100644
--- a/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java
+++ b/common/src/main/java/mod/azure/azurelib/core/keyframe/event/SoundKeyframeEvent.java
@@ -20,6 +20,7 @@
* The {@link KeyFrameEvent} specific to the {@link AnimationController#soundKeyframeHandler}.
* Called when a sound instruction keyframe is encountered
*/
+@Deprecated(forRemoval = true)
public class SoundKeyframeEvent extends KeyFrameEvent {
/**
* This stores all the fields that are needed in the AnimationTestEvent
diff --git a/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java b/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java
index 65437bc5c..435c83902 100644
--- a/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java
+++ b/common/src/main/java/mod/azure/azurelib/core/molang/MolangParser.java
@@ -39,7 +39,7 @@ public class MolangParser extends MathBuilder {
public static final MolangParser INSTANCE = new MolangParser();
- private MolangParser() {
+ public MolangParser() {
super();
// Remap functions to be intact with Molang specification
diff --git a/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java b/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java
index a0cd8e7f1..9e694e225 100644
--- a/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java
+++ b/common/src/main/java/mod/azure/azurelib/core/molang/MolangQueries.java
@@ -12,20 +12,48 @@
* These do not constitute a definitive list of queries; merely the default ones
*/
public final class MolangQueries {
- public static final String ANIM_TIME = "query.anim_time";
- public static final String LIFE_TIME = "query.life_time";
- public static final String ACTOR_COUNT = "query.actor_count";
- public static final String TIME_OF_DAY = "query.time_of_day";
- public static final String MOON_PHASE = "query.moon_phase";
- public static final String DISTANCE_FROM_CAMERA = "query.distance_from_camera";
- public static final String IS_ON_GROUND = "query.is_on_ground";
- public static final String IS_IN_WATER = "query.is_in_water";
- public static final String IS_IN_WATER_OR_RAIN = "query.is_in_water_or_rain";
- public static final String HEALTH = "query.health";
- public static final String MAX_HEALTH = "query.max_health";
- public static final String IS_ON_FIRE = "query.is_on_fire";
- public static final String GROUND_SPEED = "query.ground_speed";
- public static final String YAW_SPEED = "query.yaw_speed";
+
+ private static final String QUERY_PREFIX = "query.";
+
+ private static final String SHORT_PREFIX = "q.";
+
+ public static final String ANIM_TIME = normalize("query.anim_time");
+
+ public static final String LIFE_TIME = normalize("query.life_time");
+
+ public static final String ACTOR_COUNT = normalize("query.actor_count");
+
+ public static final String TIME_OF_DAY = normalize("query.time_of_day");
+
+ public static final String MOON_PHASE = normalize("query.moon_phase");
+
+ public static final String DISTANCE_FROM_CAMERA = normalize("query.distance_from_camera");
+
+ public static final String IS_ON_GROUND = normalize("query.is_on_ground");
+
+ public static final String IS_IN_WATER = normalize("query.is_in_water");
+
+ public static final String IS_IN_WATER_OR_RAIN = normalize("query.is_in_water_or_rain");
+
+ public static final String HEALTH = normalize("query.health");
+
+ public static final String MAX_HEALTH = normalize("query.max_health");
+
+ public static final String IS_ON_FIRE = normalize("query.is_on_fire");
+
+ public static final String GROUND_SPEED = normalize("query.ground_speed");
+
+ public static final String YAW_SPEED = normalize("query.yaw_speed");
+
+ public static String normalize(String queryName) {
+ if (queryName.startsWith(QUERY_PREFIX)) {
+ return queryName;
+ } else if (queryName.startsWith(SHORT_PREFIX)) {
+ return QUERY_PREFIX + queryName.substring(SHORT_PREFIX.length());
+ } else {
+ throw new IllegalArgumentException("Invalid query name: " + queryName);
+ }
+ }
private MolangQueries() {
throw new UnsupportedOperationException();
diff --git a/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java b/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java
index 4f1a87f1c..b4233d86a 100644
--- a/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java
+++ b/common/src/main/java/mod/azure/azurelib/core/object/DataTicket.java
@@ -13,6 +13,7 @@
/**
* Ticket object to define a typed data object
*/
+@Deprecated(forRemoval = true)
public class DataTicket {
private final String id;
private final Class extends D> objectType;
diff --git a/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java b/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java
index 25f4f4736..6e4b39cd0 100644
--- a/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java
+++ b/common/src/main/java/mod/azure/azurelib/core/object/PlayState.java
@@ -10,6 +10,7 @@
/**
* State enum to define whether an {@link mod.azure.azurelib.core.animation.AnimationController} should continue or stop
*/
+@Deprecated(forRemoval = true)
public enum PlayState {
CONTINUE,
STOP
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..66f825ffe 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;
@@ -36,6 +38,16 @@ public static BakedAnimations loadAnimationsFile(ResourceLocation location, Reso
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
*
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/AbstractContainerMenuMixin_AzItemIDFix.java b/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java
new file mode 100644
index 000000000..06012c8bb
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/mixins/AbstractContainerMenuMixin_AzItemIDFix.java
@@ -0,0 +1,125 @@
+package mod.azure.azurelib.mixins;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+import mod.azure.azurelib.AzureLib;
+import mod.azure.azurelib.rewrite.animation.cache.AzIdentityRegistry;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.item.ItemStack;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+import java.util.UUID;
+
+/**
+ * A Mixin extension for the {@code AbstractContainerMenu} class that introduces support for AzureLib-specific {@code ItemStack}
+ * identity management (Az ID). This Mixin ensures the proper handling, synchronization, and comparison of
+ * AzureLib-registered item stacks with custom identifiers during container interactions.
+ */
+@Mixin(AbstractContainerMenu.class)
+public abstract class AbstractContainerMenuMixin_AzItemIDFix {
+
+ @Shadow public abstract void removed(Player player);
+
+ @Unique
+ private static final int DEFAULT_AZ_ID = -1;
+
+ /**
+ * Removes the AzureLib-specific ID (Az ID) from a copied `ItemStack` during a container click action. This is only
+ * performed if the original stack's item is registered with AzureLib's identity registry.
+ *
+ * Tooltip: Prevents the propagation of the Az ID when `ItemStack` objects are copied during container interactions,
+ * keeping custom IDs only for registered items.
+ */
+ @Redirect(
+ method = "doClick", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/world/item/ItemStack;copyWithCount(I)Lnet/minecraft/world/item/ItemStack;",
+ ordinal = 1
+ )
+ )
+ public ItemStack azurelib$syncAzureIDWithRemote(ItemStack itemStack, int count) {
+ var copyStack = itemStack.copyWithCount(count);
+
+ if (AzIdentityRegistry.hasIdentity(itemStack.getItem()) && copyStack.hasTag() && copyStack.getTag().contains(
+ AzureLib.ITEM_UUID_TAG)) {
+ copyStack.getTag().putUUID(AzureLib.ITEM_UUID_TAG, UUID.randomUUID());
+ }
+
+ return copyStack;
+ }
+
+ /**
+ * Validates AzureLib-specific IDs (Az ID) between two `ItemStack` objects during remote slot synchronization. This
+ * ensures that items with custom IDs remain synchronized correctly during slot operations.
+ *
+ * Tooltip: Compares two `ItemStack` objects, ensuring their Az IDs (if present) also match.
+ */
+ @WrapOperation(
+ method = "synchronizeSlotToRemote", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/world/item/ItemStack;matches(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z"
+ )
+ )
+ public boolean azurelib$syncAzureIDWithRemote(
+ ItemStack itemStack,
+ ItemStack comparisonItemStack,
+ Operation original
+ ) {
+ return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack, original);
+ }
+
+ /**
+ * Forces an Az ID comparison when listening for slot changes involving two `ItemStack` objects.
+ *
+ * Tooltip: Ensures that slot listeners detect changes in AzureLib-registered item stacks based not only on their
+ * normal properties but also their Az ID values, if applicable.
+ */
+ @WrapOperation(
+ method = "triggerSlotListeners", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/world/item/ItemStack;matches(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z"
+ )
+ )
+ public boolean azurelib$detectSlotChangeWithAzureID(
+ ItemStack itemStack,
+ ItemStack comparisonItemStack,
+ Operation original
+ ) {
+ return azurelib$compareStacksWithAzureID(itemStack, comparisonItemStack, original);
+ }
+
+ /**
+ * Compares two ItemStacks while considering AzureLib-specific IDs (Az ID) if the item is registered with AzureLib.
+ *
+ * @param itemStack The first ItemStack to compare.
+ * @param comparisonItemStack The second ItemStack to compare.
+ * @return True if the base comparison is true and the Az IDs (if present) match; false otherwise.
+ */
+ @Unique
+ private boolean azurelib$compareStacksWithAzureID(ItemStack itemStack, ItemStack comparisonItemStack, Operation original) {
+ if (!AzIdentityRegistry.hasIdentity(itemStack.getItem())) {
+ return original.call(itemStack, comparisonItemStack);
+ }
+
+ return original.call(itemStack, comparisonItemStack) && azurelib$checkAzIDMatch(itemStack.getTag(), comparisonItemStack.getTag());
+ }
+
+ /**
+ * Performs an exclusive-NOR (XNOR) operation on the Az ID tags of two ItemStacks to check for matching IDs.
+ *
+ * @param tag1 The CompoundTag of the first ItemStack.
+ * @param tag2 The CompoundTag of the second ItemStack.
+ * @return True if both tags either have matching Az IDs or are null, false otherwise.
+ */
+ @Unique
+ private static boolean azurelib$checkAzIDMatch(CompoundTag tag1, CompoundTag tag2) {
+ return (tag1 == null ? DEFAULT_AZ_ID : tag1.getInt(AzureLib.ITEM_UUID_TAG)) == (tag2 == null ? DEFAULT_AZ_ID : tag2.getInt(AzureLib.ITEM_UUID_TAG));
+ }
+
+}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/AccessorWarningScreen.java b/common/src/main/java/mod/azure/azurelib/mixins/AccessorWarningScreen.java
deleted file mode 100644
index daa4f9adf..000000000
--- a/common/src/main/java/mod/azure/azurelib/mixins/AccessorWarningScreen.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package mod.azure.azurelib.mixins;
-
-import net.minecraft.client.gui.components.MultiLineLabel;
-import net.minecraft.client.gui.screens.multiplayer.WarningScreen;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Accessor;
-
-@Mixin(WarningScreen.class)
-public interface AccessorWarningScreen {
- @Accessor("message")
- MultiLineLabel getMessageText();
-
- @Accessor("message")
- void setMessageText(MultiLineLabel messageText);
-}
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..0bd9ea632 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,28 @@
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.model.PlayerModel;
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.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
+import org.jetbrains.annotations.Nullable;
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 +30,89 @@
*/
@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;
+ @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;
+ }
+
+ @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);
+ }
- @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);
+ 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);
+ azurelib$testVisibility((A) typedHumanoidModel, entity, equipmentSlot);
+ armorModel.renderToBuffer(poseStack, null, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1);
+ ci.cancel();
+ }
}
- @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; }
+ @Unique
+ private void azurelib$testVisibility(A model, @Nullable Entity entity, EquipmentSlot equipmentSlot) {
+ if (entity instanceof Player && model instanceof PlayerModel> playerModel) {
+ switch (equipmentSlot) {
+ case HEAD -> {
+ playerModel.hat.visible = false;
+ playerModel.ear.visible = false;
+ }
+ case CHEST -> {
+ playerModel.jacket.visible = false;
+ playerModel.rightSleeve.visible = false;
+ playerModel.leftSleeve.visible = false;
+ }
+ case LEGS -> {
+ playerModel.leftPants.visible = false;
+ playerModel.rightPants.visible = false;
+ }
+ }
+ }
+ }
}
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..0d6d12e3d
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemAnimatorCache.java
@@ -0,0 +1,28 @@
+package mod.azure.azurelib.mixins;
+
+import mod.azure.azurelib.AzureLib;
+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(AzureLib.ITEM_UUID_TAG);
+ 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..ace602c39
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/mixins/ItemStackMixin_AzItemStackIdentityRegistry.java
@@ -0,0 +1,94 @@
+package mod.azure.azurelib.mixins;
+
+import mod.azure.azurelib.AzureLib;
+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 net.minecraft.world.level.ItemLike;
+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.callback.CallbackInfo;
+
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * A mixin class for injecting additional functionality into the {@link ItemStack}
+ * constructor to handle identity registration via AzureLib.
+ *
+ * This mixin ensures that every {@link ItemStack} is assigned a unique identifier
+ * when its corresponding item has been registered in the {@link AzIdentityRegistry} and
+ * no existing UUID is present in the item's {@link CompoundTag}.
+ *
+ * The mixin method `az_addIdentityComponent` is invoked at the "TAIL" of the
+ * {@link ItemStack} constructor, which takes a {@link CompoundTag} as a parameter.
+ */
+@Mixin(ItemStack.class)
+public class ItemStackMixin_AzItemStackIdentityRegistry {
+
+ /**
+ * Injects into the constructor of the {@link ItemStack} that takes a {@link CompoundTag} parameter to initialize a unique AzureLib ID (Az ID)
+ * if the item is registered in the {@link AzIdentityRegistry}.
+ *
+ * @param compoundTag The {@link CompoundTag} associated with the {@link ItemStack}.
+ * @param ci The {@link CallbackInfo} for the mixin injection.
+ */
+
+ @Inject(
+ method = "(Lnet/minecraft/nbt/CompoundTag;)V",
+ at = @At("TAIL")
+ )
+ public void azurelib$initializeAzIdFromCompoundTag(CompoundTag compoundTag, CallbackInfo ci) {
+ azureLib$initializeAzIdOnStack(this, compoundTag);
+ }
+
+ /**
+ * Injects into the constructor of the {@link ItemStack} that takes an {@link ItemLike}, an integer item count, and an {@link Optional} for
+ * the compound tag. This ensures that a unique AzureLib ID (Az ID) is initialized if the item is registered in {@link AzIdentityRegistry}.
+ *
+ * @param ci The {@link CallbackInfo} for the mixin injection.
+ */
+ @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;ILjava/util/Optional;)V", at = @At("TAIL"))
+ public void azurelib$initializeAzIdForConstructorWithOptional(CallbackInfo ci) {
+ azureLib$initializeAzIdOnStack(this, null);
+ }
+
+ /**
+ * Injects into the constructor of the {@link ItemStack} that takes an {@link ItemLike} and an integer count.
+ * This ensures that a unique AzureLib ID (Az ID) is initialized if the item is registered in {@link AzIdentityRegistry}.
+ *
+ * @param ci The {@link CallbackInfo} for the mixin injection.
+ */
+ @Inject(method = "Lnet/minecraft/world/item/ItemStack;(Lnet/minecraft/world/level/ItemLike;I)V", at = @At("TAIL"))
+ public void azurelib$initializeAzIdForConstructor(CallbackInfo ci) {
+ azureLib$initializeAzIdOnStack(this, null);
+ }
+
+ /**
+ * Ensures that a unique AzureLib ID (Az ID) is initialized on the provided stack object if the item it represents
+ * is registered in the {@link AzIdentityRegistry} and does not already have a unique identifier. If necessary,
+ * assigns a new {@link CompoundTag} for the stack and generates a new UUID.
+ *
+ * @param stackObject The object representing the stack, expected to be an instance of {@link ItemStack}.
+ * @param tag The {@link CompoundTag} associated with the stack, used for storing or retrieving data.
+ */
+ @Unique
+ private void azureLib$initializeAzIdOnStack(Object stackObject, CompoundTag tag) {
+ var self = AzureLibUtil.self(stackObject);
+
+ if (!self.hasTag()) {
+ self.setTag(new CompoundTag());
+ }
+
+ var stackTag = self.getTag();
+
+ if (stackTag != null && AzIdentityRegistry.hasIdentity(self.getItem()) && !stackTag.hasUUID(
+ AzureLib.ITEM_UUID_TAG)) {
+ stackTag.putUUID(AzureLib.ITEM_UUID_TAG, UUID.randomUUID());
+ }
+ }
+
+}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java b/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java
index 47edeb44b..87771d453 100644
--- a/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java
+++ b/common/src/main/java/mod/azure/azurelib/mixins/MinecraftMixin.java
@@ -35,7 +35,7 @@ public MinecraftMixin(String p_i50401_1_) {
}
@Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;resetData()V"))
- private void configuration_reloadClientConfigs(Screen screen, CallbackInfo ci) {
+ private void azurelib$reloadClientConfigs(Screen screen, CallbackInfo ci) {
ConfigHolder.getSynchronizedConfigs().stream()
.map(ConfigHolder::getConfig)
.filter(Optional::isPresent)
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java b/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java
index 789ef5851..ac6c4fa69 100644
--- a/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java
+++ b/common/src/main/java/mod/azure/azurelib/mixins/MixinItemRenderer.java
@@ -1,5 +1,6 @@
package mod.azure.azurelib.mixins;
+import mod.azure.azurelib.rewrite.render.item.AzItemRendererRegistry;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -21,8 +22,22 @@
@Mixin(ItemRenderer.class)
public class MixinItemRenderer {
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/BlockEntityWithoutLevelRenderer;renderByItem(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;II)V"), cancellable = true)
- public void itemModelHook(ItemStack itemStack, ItemDisplayContext transformType, boolean bl, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j, BakedModel bakedModel, CallbackInfo ci) {
- if (itemStack.getItem() instanceof GeoItem)
- RenderProvider.of(itemStack).getCustomRenderer().renderByItem(itemStack, transformType, poseStack, multiBufferSource, i, j);
+ public void azurelib$itemModelHook(ItemStack itemStack, ItemDisplayContext transformType, boolean bl, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j, BakedModel bakedModel, CallbackInfo ci) {
+ // TODO: Remove this along with Geo-code.
+ if (itemStack.getItem() instanceof GeoItem) {
+ RenderProvider.of(itemStack)
+ .getCustomRenderer()
+ .renderByItem(itemStack, transformType, poseStack, multiBufferSource, i, j);
+ }
+
+ var item = itemStack.getItem();
+ var renderer = AzItemRendererRegistry.getOrNull(item);
+
+ if (renderer != null) {
+ switch (transformType) {
+ case GUI -> renderer.renderByGui(itemStack, poseStack, multiBufferSource, i);
+ default -> renderer.renderByItem(itemStack, poseStack, multiBufferSource, i);
+ }
+ }
}
}
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/mixins/PlayerListMixin.java b/common/src/main/java/mod/azure/azurelib/mixins/PlayerListMixin.java
index a29b579c9..f767c2f99 100644
--- a/common/src/main/java/mod/azure/azurelib/mixins/PlayerListMixin.java
+++ b/common/src/main/java/mod/azure/azurelib/mixins/PlayerListMixin.java
@@ -28,7 +28,7 @@
public abstract class PlayerListMixin {
@Inject(method = "placeNewPlayer", at = @At("TAIL"))
- private void configuration_sendServerConfigs(Connection connection, ServerPlayer player, CallbackInfo ci) {
+ private void azurelib$sendServerConfigs(Connection connection, ServerPlayer player, CallbackInfo ci) {
Set set = ConfigHolder.getSynchronizedConfigs();
set.forEach(id -> Services.NETWORK.sendClientPacket(player, id));
}
diff --git a/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java b/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java
index 6904f5640..0242e1a0a 100644
--- a/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java
+++ b/common/src/main/java/mod/azure/azurelib/mixins/TextureManagerMixin.java
@@ -28,7 +28,7 @@ public abstract class TextureManagerMixin {
@Shadow public abstract void register(ResourceLocation resourceLocation, AbstractTexture abstractTexture);
@Inject(method = "getTexture(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/texture/AbstractTexture;", at = @At("HEAD"))
- private void wrapAnimatableTexture(ResourceLocation path, CallbackInfoReturnable callback) {
+ private void azurelib$wrapAnimatableTexture(ResourceLocation path, CallbackInfoReturnable callback) {
AbstractTexture existing = this.byPath.get(path);
if (existing == null) {
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..66f798cba
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzBlockEntityDispatchCommandPacket.java
@@ -0,0 +1,58 @@
+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 AzBlockEntityDispatchCommandPacket(
+ BlockPos blockPos,
+ AzCommand dispatchCommand
+ ) {
+ this.blockPos = blockPos;
+ this.dispatchCommand = dispatchCommand;
+ }
+
+ @Override
+ public void encode(FriendlyByteBuf buf) {
+ buf.writeBlockPos(this.blockPos);
+ AzCommand.ENCODER.accept(buf, this.dispatchCommand);
+ }
+
+ @Override
+ public ResourceLocation getPacketID() {
+ return AzureLibNetwork.AZ_BLOCKENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID;
+ }
+
+ public static AzBlockEntityDispatchCommandPacket receive(FriendlyByteBuf buf) {
+ BlockPos blockPos = buf.readBlockPos(); // Decode block position
+ AzCommand dispatchCommand = AzCommand.DECODER.apply(buf); // Decode AzCommand
+ return new AzBlockEntityDispatchCommandPacket(blockPos, dispatchCommand); // Create a new packet instance
+ }
+
+ @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));
+ }
+ }
+}
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..7509249d1
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzEntityDispatchCommandPacket.java
@@ -0,0 +1,55 @@
+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);
+ AzCommand.ENCODER.accept(buf, this.dispatchCommand);
+ }
+
+ @Override
+ public ResourceLocation getPacketID() {
+ return AzureLibNetwork.AZ_ENTITY_DISPATCH_COMMAND_SYNC_PACKET_ID;
+ }
+
+ public static AzEntityDispatchCommandPacket receive(FriendlyByteBuf buf) {
+ int entityId = buf.readInt(); // Decode integer entity ID
+ AzCommand dispatchCommand = AzCommand.DECODER.apply(buf); // Decode AzCommand
+ return new AzEntityDispatchCommandPacket(entityId, dispatchCommand); // Create and return the packet instance
+ }
+
+ 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));
+ }
+ }
+}
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..720b148c4
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/network/packet/AzItemStackDispatchCommandPacket.java
@@ -0,0 +1,50 @@
+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); // Encode the UUID
+ AzCommand.ENCODER.accept(buf, this.dispatchCommand); // Encode AzCommand
+ }
+
+ @Override
+ public ResourceLocation getPacketID() {
+ return AzureLibNetwork.AZ_ITEM_STACK_DISPATCH_COMMAND_SYNC_PACKET_ID;
+ }
+
+ public static AzItemStackDispatchCommandPacket receive(FriendlyByteBuf buf) {
+ UUID itemStackId = buf.readUUID(); // Decode UUID
+ AzCommand dispatchCommand = AzCommand.DECODER.apply(buf); // Decode AzCommand
+ return new AzItemStackDispatchCommandPacket(itemStackId, dispatchCommand); // Create and return the packet instance
+ }
+
+ public void handle() {
+ var animator = AzIdentifiableItemStackAnimatorCache.getInstance().getOrNull(itemStackId);
+
+ if (animator != null) {
+ dispatchCommand.actions().forEach(action -> action.handle(AzDispatchSide.SERVER, animator));
+ }
+ }
+}
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..8f290d2d1
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/AzAnimator.java
@@ -0,0 +1,138 @@
+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 = Minecraft.getInstance().level;
+ var parser = MolangParser.INSTANCE;
+
+ if (level == null) {
+ return;
+ }
+
+ 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..3a16d6794
--- /dev/null
+++ b/common/src/main/java/mod/azure/azurelib/rewrite/animation/cache/AzIdentifiableItemStackAnimatorCache.java
@@ -0,0 +1,47 @@
+package mod.azure.azurelib.rewrite.animation.cache;
+
+import mod.azure.azurelib.AzureLib;
+import mod.azure.azurelib.rewrite.animation.impl.AzItemAnimator;
+import net.minecraft.nbt.CompoundTag;
+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) {
+ if (!itemStack.hasTag()) {
+ itemStack.setTag(new CompoundTag());
+ }
+
+ var tag = itemStack.getTag();
+ var uuid = tag.getUUID(AzureLib.ITEM_UUID_TAG);
+
+ 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