From c7bd7b84441471afb5f3ec80ecb60f0f7d30b391 Mon Sep 17 00:00:00 2001 From: Julian Lechner Date: Tue, 14 Jul 2026 15:26:52 +0200 Subject: [PATCH] feat: add per-player cosmetic state --- .../client/cosmetic/CosmeticData.java | 6 +++ .../client/cosmetic/CosmeticRenderData.java | 6 +++ .../cosmetic/CosmeticRenderStateKeys.java | 14 ++++++ .../client/cosmetic/PlayerCosmetic.java | 24 +++++++++++ .../cosmetic/PlayerCosmeticManager.java | 43 +++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticData.java create mode 100644 src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderData.java create mode 100644 src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderStateKeys.java create mode 100644 src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmetic.java create mode 100644 src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmeticManager.java diff --git a/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticData.java b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticData.java new file mode 100644 index 0000000..aaf93e8 --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticData.java @@ -0,0 +1,6 @@ +package de.fullrisk.citrusDev.client.cosmetic; + +import net.minecraft.resources.Identifier; + +public record CosmeticData(Identifier id, boolean visible) { +} diff --git a/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderData.java b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderData.java new file mode 100644 index 0000000..9257e3d --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderData.java @@ -0,0 +1,6 @@ +package de.fullrisk.citrusDev.client.cosmetic; + +import java.util.UUID; + +public record CosmeticRenderData(UUID playerId, CosmeticData cosmetic, float partialTick) { +} diff --git a/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderStateKeys.java b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderStateKeys.java new file mode 100644 index 0000000..cc12ce6 --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/CosmeticRenderStateKeys.java @@ -0,0 +1,14 @@ +package de.fullrisk.citrusDev.client.cosmetic; + +import net.fabricmc.fabric.api.client.rendering.v1.RenderStateDataKey; +import net.minecraft.client.renderer.state.level.CameraRenderState; + +public final class CosmeticRenderStateKeys { + public static final RenderStateDataKey COSMETIC = + RenderStateDataKey.create(() -> "citrus-dev player cosmetic"); + public static final RenderStateDataKey CAMERA = + RenderStateDataKey.create(() -> "citrus-dev camera render state"); + + private CosmeticRenderStateKeys() { + } +} diff --git a/src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmetic.java b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmetic.java new file mode 100644 index 0000000..183cdeb --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmetic.java @@ -0,0 +1,24 @@ +package de.fullrisk.citrusDev.client.cosmetic; + +import com.geckolib.animatable.GeoAnimatable; +import com.geckolib.animatable.instance.AnimatableInstanceCache; +import com.geckolib.animatable.manager.AnimatableManager; +import com.geckolib.animation.AnimationController; +import com.geckolib.animation.RawAnimation; +import com.geckolib.util.GeckoLibUtil; + +public final class PlayerCosmetic implements GeoAnimatable { + private static final RawAnimation IDLE = RawAnimation.begin().thenLoop("animation.backpack.idle"); + + private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this, false); + + @Override + public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { + controllers.add(new AnimationController(state -> state.setAndContinue(IDLE))); + } + + @Override + public AnimatableInstanceCache getAnimatableInstanceCache() { + return cache; + } +} diff --git a/src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmeticManager.java b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmeticManager.java new file mode 100644 index 0000000..5d6867a --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/client/cosmetic/PlayerCosmeticManager.java @@ -0,0 +1,43 @@ +package de.fullrisk.citrusDev.client.cosmetic; + +import net.minecraft.resources.Identifier; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public final class PlayerCosmeticManager { + private static final ConcurrentHashMap COSMETICS = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap ANIMATABLES = new ConcurrentHashMap<>(); + private static final CosmeticData TEST_BACKPACK = new CosmeticData( + Identifier.fromNamespaceAndPath("citrus-dev", "backpack"), true); + + private PlayerCosmeticManager() { + } + + public static Optional getCosmetic(UUID playerId) { + return Optional.ofNullable(COSMETICS.get(playerId)); + } + + public static void setCosmetic(UUID playerId, CosmeticData cosmetic) { + COSMETICS.put(playerId, cosmetic); + } + + public static void removeCosmetic(UUID playerId) { + COSMETICS.remove(playerId); + ANIMATABLES.remove(playerId); + } + + public static PlayerCosmetic getOrCreateAnimatable(UUID playerId) { + return ANIMATABLES.computeIfAbsent(playerId, ignored -> new PlayerCosmetic()); + } + + public static void clear() { + COSMETICS.clear(); + ANIMATABLES.clear(); + } + + public static void enableTestCosmetic(UUID playerId) { + setCosmetic(playerId, TEST_BACKPACK); + } +}