feat: add per-player cosmetic state

This commit is contained in:
2026-07-14 15:26:52 +02:00
parent 7bc09ec76c
commit c7bd7b8444
5 changed files with 93 additions and 0 deletions
@@ -0,0 +1,6 @@
package de.fullrisk.citrusDev.client.cosmetic;
import net.minecraft.resources.Identifier;
public record CosmeticData(Identifier id, boolean visible) {
}
@@ -0,0 +1,6 @@
package de.fullrisk.citrusDev.client.cosmetic;
import java.util.UUID;
public record CosmeticRenderData(UUID playerId, CosmeticData cosmetic, float partialTick) {
}
@@ -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<CosmeticRenderData> COSMETIC =
RenderStateDataKey.create(() -> "citrus-dev player cosmetic");
public static final RenderStateDataKey<CameraRenderState> CAMERA =
RenderStateDataKey.create(() -> "citrus-dev camera render state");
private CosmeticRenderStateKeys() {
}
}
@@ -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<PlayerCosmetic>(state -> state.setAndContinue(IDLE)));
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return cache;
}
}
@@ -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<UUID, CosmeticData> COSMETICS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<UUID, PlayerCosmetic> ANIMATABLES = new ConcurrentHashMap<>();
private static final CosmeticData TEST_BACKPACK = new CosmeticData(
Identifier.fromNamespaceAndPath("citrus-dev", "backpack"), true);
private PlayerCosmeticManager() {
}
public static Optional<CosmeticData> 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);
}
}