From 600d628decaa69d2b6957140377fac04ce595d94 Mon Sep 17 00:00:00 2001 From: 3944Realms Date: Sat, 24 May 2025 23:39:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=A1=94=E5=90=8AAPI=20?= =?UTF-8?q?=E5=B0=86=E8=AF=A5=E9=A1=B9=E7=9B=AE=20=E8=B7=A8=E6=A8=A1?= =?UTF-8?q?=E7=BB=84=E5=8A=A0=E8=BD=BD=E5=99=A8=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jsonem/{Jsonem.java => JsonEm.java} | 7 +- .../jsonem/mixin/DilationAccess.java | 17 +++ .../jsonem/mixin/EntityModelLoaderMixin.java | 26 ++++ .../jsonem/mixin/EntityModelsMixin.java | 33 +++++ .../jsonem/mixin/ModelCuboidDataAccess.java | 45 +++++++ .../jsonem/mixin/ModelPartDataAccess.java | 28 ++++ .../jsonem/mixin/TextureDimensionsAccess.java | 14 ++ .../jsonem/mixin/TexturedModelDataAccess.java | 22 ++++ .../jsonem/platform/IPlatFormHelper.java | 5 + .../jsonem/platform/Services.java | 16 +++ .../jsonem/serialization/JsonEMCodecs.java | 123 ++++++++++++++++++ .../LazyTypeUnboundedMapCodec.java | 51 ++++++++ .../jsonem/util/JsonEntityModelUtil.java | 76 +++++++++++ .../jsonem/util/Vector2fComparable.java | 24 ++++ common/src/main/resources/jsonem.mixins.json | 6 + .../src/main/resources/pack.mcmeta | 0 .../jsonem/fabric/JsonEmFabric.java | 23 ++++ .../jsonem/fabric/JsonemFabric.java | 16 --- .../fabric/client/JsonemFabricClient.java | 10 -- .../jsonem/fabric/config/JsonEmConfig.java | 49 +++++++ .../fabric/platform/FabricPlatformHelper.java | 11 ++ ...sonem.fabric.platform.FabricPlatformHelper | 1 + .../src/main/resources/assets/jsonem/icon.png | Bin 0 -> 18118 bytes fabric/src/main/resources/fabric.mod.json | 5 +- forge/build.gradle | 4 +- .../jsonem/forge/JsonEmForge.java | 21 +++ .../jsonem/forge/JsonemForge.java | 18 --- .../jsonem/forge/config/JsonEmConfig.java | 20 +++ .../forge/platform/ForgePlatformHelper.java | 11 ++ forge/src/main/resources/META-INF/mods.toml | 2 +- ....jsonem.forge.platform.ForgePlatformHelper | 1 + .../src/main/resources/assets/jsonem/icon.png | Bin 0 -> 18118 bytes settings.gradle | 4 + 33 files changed, 636 insertions(+), 53 deletions(-) rename common/src/main/java/com/leisuretimedock/jsonem/{Jsonem.java => JsonEm.java} (51%) create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/mixin/DilationAccess.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelLoaderMixin.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelsMixin.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelCuboidDataAccess.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelPartDataAccess.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/mixin/TextureDimensionsAccess.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/mixin/TexturedModelDataAccess.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/platform/IPlatFormHelper.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/platform/Services.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/serialization/JsonEMCodecs.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/serialization/LazyTypeUnboundedMapCodec.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/util/JsonEntityModelUtil.java create mode 100644 common/src/main/java/com/leisuretimedock/jsonem/util/Vector2fComparable.java rename {forge => common}/src/main/resources/pack.mcmeta (100%) create mode 100644 fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonEmFabric.java delete mode 100644 fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonemFabric.java delete mode 100644 fabric/src/main/java/com/leisuretimedock/jsonem/fabric/client/JsonemFabricClient.java create mode 100644 fabric/src/main/java/com/leisuretimedock/jsonem/fabric/config/JsonEmConfig.java create mode 100644 fabric/src/main/java/com/leisuretimedock/jsonem/fabric/platform/FabricPlatformHelper.java create mode 100644 fabric/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper create mode 100644 fabric/src/main/resources/assets/jsonem/icon.png create mode 100644 forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonEmForge.java delete mode 100644 forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonemForge.java create mode 100644 forge/src/main/java/com/leisuretimedock/jsonem/forge/config/JsonEmConfig.java create mode 100644 forge/src/main/java/com/leisuretimedock/jsonem/forge/platform/ForgePlatformHelper.java create mode 100644 forge/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper create mode 100644 forge/src/main/resources/assets/jsonem/icon.png diff --git a/common/src/main/java/com/leisuretimedock/jsonem/Jsonem.java b/common/src/main/java/com/leisuretimedock/jsonem/JsonEm.java similarity index 51% rename from common/src/main/java/com/leisuretimedock/jsonem/Jsonem.java rename to common/src/main/java/com/leisuretimedock/jsonem/JsonEm.java index dd2120a..5bbb2cf 100644 --- a/common/src/main/java/com/leisuretimedock/jsonem/Jsonem.java +++ b/common/src/main/java/com/leisuretimedock/jsonem/JsonEm.java @@ -1,8 +1,11 @@ package com.leisuretimedock.jsonem; -public final class Jsonem { - public static final String MOD_ID = "jsonem"; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +public final class JsonEm { + public static final String MOD_ID = "jsonem"; + public static final Logger LOG = LoggerFactory.getLogger(JsonEm.class); public static void init() { // Write common init code here. } diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/DilationAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/DilationAccess.java new file mode 100644 index 0000000..1101a76 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/DilationAccess.java @@ -0,0 +1,17 @@ +package com.leisuretimedock.jsonem.mixin; + +import net.minecraft.client.model.geom.builders.CubeDeformation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(CubeDeformation.class) +public interface DilationAccess { + @Accessor("growX") + float jsonem$growX(); + + @Accessor("growY") + float jsonem$growY(); + + @Accessor("growZ") + float jsonem$growZ(); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelLoaderMixin.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelLoaderMixin.java new file mode 100644 index 0000000..7da2fe4 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelLoaderMixin.java @@ -0,0 +1,26 @@ +package com.leisuretimedock.jsonem.mixin; + +import com.leisuretimedock.jsonem.util.JsonEntityModelUtil; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.HashMap; +import java.util.Map; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.server.packs.resources.ResourceManager; + +@Mixin(EntityModelSet.class) +public class EntityModelLoaderMixin { + @Shadow private Map roots; + + @Inject(method = "onResourceManagerReload", at = @At("TAIL")) + private void jsonem$loadJsonEntityModels(ResourceManager manager, CallbackInfo ci) { + this.roots = new HashMap<>(this.roots); + JsonEntityModelUtil.loadModels(manager, this.roots); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelsMixin.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelsMixin.java new file mode 100644 index 0000000..c606ea9 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelsMixin.java @@ -0,0 +1,33 @@ +package com.leisuretimedock.jsonem.mixin; + +import com.google.common.collect.ImmutableMap; +import com.leisuretimedock.jsonem.JsonEm; +import com.leisuretimedock.jsonem.platform.Services; +import com.leisuretimedock.jsonem.util.JsonEntityModelUtil; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.io.IOException; +import java.util.Map; +import net.minecraft.client.model.geom.LayerDefinitions; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; + +@Mixin(LayerDefinitions.class) +public class EntityModelsMixin { + @Inject(method = "createRoots", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;"), locals = LocalCapture.CAPTURE_FAILSOFT) + private static void jsonem$dumpModels(CallbackInfoReturnable> cir, ImmutableMap.Builder layers) { + if (Services.PLATFORM.shouldDumpModels()) { + layers.build().forEach((layer, data) -> { + try { + JsonEntityModelUtil.dump(layer, data); + } catch (IOException e) { + JsonEm.LOG.error("Error",e); + } + }); + } + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelCuboidDataAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelCuboidDataAccess.java new file mode 100644 index 0000000..1aa5548 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelCuboidDataAccess.java @@ -0,0 +1,45 @@ +package com.leisuretimedock.jsonem.mixin; + +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.Set; +import net.minecraft.client.model.geom.builders.CubeDefinition; +import net.minecraft.client.model.geom.builders.CubeDeformation; +import net.minecraft.client.model.geom.builders.UVPair; +import net.minecraft.core.Direction; + +@Mixin(CubeDefinition.class) +public interface ModelCuboidDataAccess { + @Accessor("comment") + String jsonem$comment(); + + @Accessor("origin") + Vector3f jsonem$origin(); + + @Accessor("dimensions") + Vector3f jsonem$dimensions(); + + @Accessor("grow") + CubeDeformation jsonem$grow(); + + @Accessor("mirror") + boolean jsonem$mirror(); + + @Accessor("texCoord") + UVPair jsonem$uv(); + + @Accessor("texScale") + UVPair jsonem$uvScale(); + + @Accessor("visibleFaces") + Set jsonem$faces(); + + @Invoker("") + static CubeDefinition jsonem$create(@Nullable String name, float textureX, float textureY, float offsetX, float offsetY, float offsetZ, float sizeX, float sizeY, float sizeZ, CubeDeformation extra, boolean mirror, float textureScaleX, float textureScaleY, Set directions) { + throw new AssertionError("mixin broke"); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelPartDataAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelPartDataAccess.java new file mode 100644 index 0000000..a2a56e1 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelPartDataAccess.java @@ -0,0 +1,28 @@ +package com.leisuretimedock.jsonem.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; +import java.util.Map; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.CubeDefinition; +import net.minecraft.client.model.geom.builders.PartDefinition; + +@Mixin(PartDefinition.class) +public interface ModelPartDataAccess { + @Accessor("cubes") + List jsonem$cubes(); + + @Accessor("partPose") + PartPose jsonem$transform(); + + @Accessor("children") + Map jsonem$children(); + + @Invoker("") + static PartDefinition create(List cuboids, PartPose rotation) { + throw new AssertionError("mixin broke"); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/TextureDimensionsAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TextureDimensionsAccess.java new file mode 100644 index 0000000..fb7c73f --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TextureDimensionsAccess.java @@ -0,0 +1,14 @@ +package com.leisuretimedock.jsonem.mixin; + +import net.minecraft.client.model.geom.builders.MaterialDefinition; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MaterialDefinition.class) +public interface TextureDimensionsAccess { + @Accessor("xTexSize") + int jsonem$width(); + + @Accessor("yTexSize") + int jsonem$height(); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/TexturedModelDataAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TexturedModelDataAccess.java new file mode 100644 index 0000000..73d9e8d --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TexturedModelDataAccess.java @@ -0,0 +1,22 @@ +package com.leisuretimedock.jsonem.mixin; + +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MaterialDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(LayerDefinition.class) +public interface TexturedModelDataAccess { + @Accessor("mesh") + MeshDefinition jsonem$root(); + + @Accessor("material") + MaterialDefinition jsonem$texture(); + + @Invoker("") + static LayerDefinition create(MeshDefinition data, MaterialDefinition dimensions) { + throw new AssertionError("mixin broke"); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/platform/IPlatFormHelper.java b/common/src/main/java/com/leisuretimedock/jsonem/platform/IPlatFormHelper.java new file mode 100644 index 0000000..a198a0f --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/platform/IPlatFormHelper.java @@ -0,0 +1,5 @@ +package com.leisuretimedock.jsonem.platform; + +public interface IPlatFormHelper { + boolean shouldDumpModels(); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/platform/Services.java b/common/src/main/java/com/leisuretimedock/jsonem/platform/Services.java new file mode 100644 index 0000000..511e18c --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/platform/Services.java @@ -0,0 +1,16 @@ +package com.leisuretimedock.jsonem.platform; + +import com.leisuretimedock.jsonem.JsonEm; + +import java.util.ServiceLoader; + +public class Services { + public static final IPlatFormHelper PLATFORM = load(IPlatFormHelper.class); + public static T load(Class clazz) { + final T loadedService = ServiceLoader.load(clazz) + .findFirst() + .orElseThrow(() -> new RuntimeException("Could not find service for " + clazz.getSimpleName())); + JsonEm.LOG.debug("Loaded {} service for {}", loadedService, clazz); + return loadedService; + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/serialization/JsonEMCodecs.java b/common/src/main/java/com/leisuretimedock/jsonem/serialization/JsonEMCodecs.java new file mode 100644 index 0000000..5a61ce2 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/serialization/JsonEMCodecs.java @@ -0,0 +1,123 @@ +package com.leisuretimedock.jsonem.serialization; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.leisuretimedock.jsonem.mixin.DilationAccess; +import com.leisuretimedock.jsonem.mixin.ModelCuboidDataAccess; +import com.leisuretimedock.jsonem.mixin.ModelPartDataAccess; +import com.leisuretimedock.jsonem.mixin.TextureDimensionsAccess; +import com.leisuretimedock.jsonem.mixin.TexturedModelDataAccess; +import com.leisuretimedock.jsonem.util.Vector2fComparable; +import org.joml.Vector3f; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import net.minecraft.Util; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.CubeDefinition; +import net.minecraft.client.model.geom.builders.CubeDeformation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MaterialDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; +import net.minecraft.client.model.geom.builders.PartDefinition; +import net.minecraft.client.model.geom.builders.UVPair; +import net.minecraft.core.Direction; +import net.minecraft.util.ExtraCodecs; + +public class JsonEMCodecs { + private static final Set ALL_DIRECTIONS = EnumSet.allOf(Direction.class); + + public static final Codec VECTOR2F = Codec.FLOAT.listOf().comapFlatMap((vec) -> + Util.fixedSize(vec, 2).map((arr) -> new Vector2fComparable(arr.get(0), arr.get(1))), + (vec) -> ImmutableList.of(vec.u(), vec.v()) + ); + + public static final Codec TEXTURE_DIMENSIONS = RecordCodecBuilder.create((instance) -> + instance.group( + Codec.INT.fieldOf("width").forGetter(obj -> ((TextureDimensionsAccess) obj).jsonem$width()), + Codec.INT.fieldOf("height").forGetter(obj -> ((TextureDimensionsAccess) obj).jsonem$height()) + ).apply(instance, MaterialDefinition::new) + ); + + public static final Codec MODEL_TRANSFORM = RecordCodecBuilder.create((instance) -> + instance.group( + ExtraCodecs.VECTOR3F.optionalFieldOf("origin", new Vector3f(0)).forGetter(obj -> new Vector3f(obj.x, obj.y, obj.z)), + ExtraCodecs.VECTOR3F.optionalFieldOf("rotation", new Vector3f(0)).forGetter(obj -> new Vector3f(obj.xRot, obj.yRot, obj.zRot)) + ).apply(instance, (origin, rot) -> PartPose.offsetAndRotation(origin.x(), origin.y(), origin.z(), rot.x(), rot.y(), rot.z())) + ); + + public static final Codec DILATION = ExtraCodecs.VECTOR3F.xmap( + vec -> new CubeDeformation(vec.x(), vec.y(), vec.z()), + dil -> new Vector3f( + ((DilationAccess) dil).jsonem$growX(), + ((DilationAccess) dil).jsonem$growY(), + ((DilationAccess) dil).jsonem$growZ()) + ); + + private static CubeDefinition createCuboidData(Optional name, Vector3f offset, Vector3f dimensions, CubeDeformation dilation, boolean mirror, UVPair uv, UVPair uvSize, Optional> faces) { + return ModelCuboidDataAccess.jsonem$create(name.orElse(null), + uv.u(), uv.v(), + offset.x(), offset.y(), offset.z(), + dimensions.x(), dimensions.y(), dimensions.z(), + dilation, mirror, + uvSize.u(), uvSize.v(), + faces.map(Set::copyOf).orElse(ALL_DIRECTIONS)); + } + + // If the set has all faces, return empty + private static Optional> optionalFaceList(Set faces) { + for (Direction direction : Direction.values()) { + if (!faces.contains(direction)) { + return Optional.of(List.copyOf(faces)); + } + } + + return Optional.empty(); + } + + private static final UVPair DEFAULT_UV_SCALE = new Vector2fComparable(1.0f, 1.0f); + + public static final Codec MODEL_CUBOID_DATA = RecordCodecBuilder.create((instance) -> + instance.group( + Codec.STRING.optionalFieldOf("name").forGetter(obj -> Optional.ofNullable(((ModelCuboidDataAccess) (Object) obj).jsonem$comment())), + ExtraCodecs.VECTOR3F.fieldOf("offset").forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$origin()), + ExtraCodecs.VECTOR3F.fieldOf("dimensions").forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$dimensions()), + DILATION.optionalFieldOf("dilation", CubeDeformation.NONE).forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$grow()), + Codec.BOOL.optionalFieldOf("mirror", false).forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$mirror()), + VECTOR2F.fieldOf("uv").forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$uv()), + VECTOR2F.optionalFieldOf("uv_scale", DEFAULT_UV_SCALE).forGetter(obj -> Vector2fComparable.of(((ModelCuboidDataAccess)(Object)obj).jsonem$uvScale())), + Codec.list(Direction.CODEC).optionalFieldOf("faces").forGetter(obj -> optionalFaceList(((ModelCuboidDataAccess)(Object)obj).jsonem$faces())) + ).apply(instance, JsonEMCodecs::createCuboidData) + ); + + private static Codec createPartDataCodec() { + return RecordCodecBuilder.create((instance) -> + instance.group( + MODEL_TRANSFORM.optionalFieldOf("transform", PartPose.ZERO).forGetter(obj -> ((ModelPartDataAccess) obj).jsonem$transform()), + Codec.list(MODEL_CUBOID_DATA).fieldOf("cuboids").forGetter(obj -> ((ModelPartDataAccess) obj).jsonem$cubes()), + LazyTypeUnboundedMapCodec.of(Codec.STRING, JsonEMCodecs::createPartDataCodec).optionalFieldOf("children", new HashMap<>()).forGetter(obj -> ((ModelPartDataAccess) obj).jsonem$children()) + ).apply(instance, (transform, cuboids, children) -> { + var data = ModelPartDataAccess.create(cuboids, transform); + ((ModelPartDataAccess) data).jsonem$children().putAll(children); + return data; + }) + ); + } + + public static final Codec MODEL_PART_DATA = createPartDataCodec(); + + public static final Codec TEXTURED_MODEL_DATA = RecordCodecBuilder.create((instance) -> + instance.group( + TEXTURE_DIMENSIONS.fieldOf("texture").forGetter(obj -> ((TexturedModelDataAccess) obj).jsonem$texture()), + Codec.unboundedMap(Codec.STRING, MODEL_PART_DATA).fieldOf("bones").forGetter(obj -> ((ModelPartDataAccess) ((TexturedModelDataAccess) obj).jsonem$root().getRoot()).jsonem$children()) + ).apply(instance, (texture, bones) -> { + var data = new MeshDefinition(); + ((ModelPartDataAccess) data.getRoot()).jsonem$children().putAll(bones); + return TexturedModelDataAccess.create(data, texture); + }) + ); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/serialization/LazyTypeUnboundedMapCodec.java b/common/src/main/java/com/leisuretimedock/jsonem/serialization/LazyTypeUnboundedMapCodec.java new file mode 100644 index 0000000..98f749c --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/serialization/LazyTypeUnboundedMapCodec.java @@ -0,0 +1,51 @@ +package com.leisuretimedock.jsonem.serialization; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.codecs.BaseMapCodec; + +import java.util.Map; +import java.util.function.Supplier; + +public class LazyTypeUnboundedMapCodec implements BaseMapCodec, Codec> { + private final Codec keyCodec; + private final Supplier> elementCodecProvider; + + private Codec elementCodec; + + public LazyTypeUnboundedMapCodec(Codec keyCodec, Supplier> elementCodecProvider) { + this.keyCodec = keyCodec; + this.elementCodecProvider = elementCodecProvider; + } + + public static LazyTypeUnboundedMapCodec of(Codec keyCodec, Supplier> elementCodecProvider) { + return new LazyTypeUnboundedMapCodec<>(keyCodec, elementCodecProvider); + } + + @Override + public DataResult, T>> decode(DynamicOps ops, T input) { + return ops.getMap(input).setLifecycle(Lifecycle.stable()).flatMap(map -> decode(ops, map)).map(r -> Pair.of(r, input)); + } + + @Override + public DataResult encode(Map input, DynamicOps ops, T prefix) { + return encode(input, ops, ops.mapBuilder()).build(prefix); + } + + @Override + public Codec keyCodec() { + return keyCodec; + } + + @Override + public Codec elementCodec() { + if (elementCodec == null) { + elementCodec = elementCodecProvider.get(); + } + + return elementCodec; + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/util/JsonEntityModelUtil.java b/common/src/main/java/com/leisuretimedock/jsonem/util/JsonEntityModelUtil.java new file mode 100644 index 0000000..9ee53ed --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/util/JsonEntityModelUtil.java @@ -0,0 +1,76 @@ +package com.leisuretimedock.jsonem.util; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.leisuretimedock.jsonem.JsonEm; +import com.leisuretimedock.jsonem.serialization.JsonEMCodecs; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.JsonOps; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import org.jetbrains.annotations.ApiStatus; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; + +public class JsonEntityModelUtil { + public static Path DUMP_DIR; + public static final Gson GSON = new Gson(); + + public static Optional readJson(InputStream data) { + JsonElement json = GSON.fromJson(GSON.newJsonReader(new InputStreamReader(data)), JsonObject.class); + + return JsonEMCodecs.TEXTURED_MODEL_DATA.decode(JsonOps.INSTANCE, json).result().map(Pair::getFirst); + } + + public static void loadModels(ResourceManager manager, Map models) { + ModelLayers.getKnownLocations().forEach(layer -> { + var modelLoc = new ResourceLocation(layer.getModel().getNamespace(), "models/entity/"+layer.getModel().getPath()+"/"+layer.getLayer()+".json"); + + var res = manager.getResource(modelLoc); + + if (res.isPresent()) { + try { + try (var in = res.get().open()) { + var data = JsonEntityModelUtil.readJson(in); + data.ifPresent(model -> models.put(layer, model)); + } + } catch (IOException e) { + JsonEm.LOG.error("Error: {}",e.getMessage()); + } + } + }); + } + + public static void dump(ModelLayerLocation layer, LayerDefinition data) throws IOException { + if (!Files.exists(DUMP_DIR)) { + Files.createDirectories(DUMP_DIR); + } + + var modelResult = JsonEMCodecs.TEXTURED_MODEL_DATA.encode(data, JsonOps.INSTANCE, new JsonObject()); + var modelFolder = DUMP_DIR.resolve("assets").resolve(layer.getModel().getNamespace()).resolve("models").resolve("entity").resolve(layer.getModel().getPath()); + var modelFile = modelFolder.resolve(layer.getLayer()+".json"); + + if (!Files.exists(modelFolder)) { + Files.createDirectories(modelFolder); + } + + var element = modelResult.get().left(); + if (element.isPresent()) { + var writer = GSON.newJsonWriter(Files.newBufferedWriter(modelFile)); + writer.setIndent(" "); + GSON.toJson(element.get(), writer); + + writer.close(); + } + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/util/Vector2fComparable.java b/common/src/main/java/com/leisuretimedock/jsonem/util/Vector2fComparable.java new file mode 100644 index 0000000..7724a45 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/util/Vector2fComparable.java @@ -0,0 +1,24 @@ +package com.leisuretimedock.jsonem.util; + +import net.minecraft.client.model.geom.builders.UVPair; + +/** + * Implementation of {@code equals()} on {@link UVPair} + */ +public class Vector2fComparable extends UVPair { + public Vector2fComparable(float x, float y) { + super(x, y); + } + + public static Vector2fComparable of(UVPair vec) { + return new Vector2fComparable(vec.u(), vec.v()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof UVPair vec) { + return this.u() == vec.u() && this.v() == vec.v(); + } + return super.equals(obj); + } +} diff --git a/common/src/main/resources/jsonem.mixins.json b/common/src/main/resources/jsonem.mixins.json index 596aa72..ab68579 100644 --- a/common/src/main/resources/jsonem.mixins.json +++ b/common/src/main/resources/jsonem.mixins.json @@ -4,6 +4,12 @@ "compatibilityLevel": "JAVA_17", "minVersion": "0.8", "client": [ + "DilationAccess", + "EntityModelLoaderMixin", + "EntityModelsMixin", + "ModelPartDataAccess", + "TextureDimensionsAccess", + "TexturedModelDataAccess" ], "mixins": [ ], diff --git a/forge/src/main/resources/pack.mcmeta b/common/src/main/resources/pack.mcmeta similarity index 100% rename from forge/src/main/resources/pack.mcmeta rename to common/src/main/resources/pack.mcmeta diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonEmFabric.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonEmFabric.java new file mode 100644 index 0000000..8f27530 --- /dev/null +++ b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonEmFabric.java @@ -0,0 +1,23 @@ +package com.leisuretimedock.jsonem.fabric; + +import com.leisuretimedock.jsonem.fabric.config.JsonEmConfig; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; + + +public class JsonEmFabric implements ClientModInitializer { + + public static final JsonEmConfig CONFIG = new JsonEmConfig(); + + @Override + public void onInitializeClient() { + CONFIG.load(); + } + + public static void registerModelLayer(ModelLayerLocation layer) { + EntityModelLayerRegistry.registerModelLayer(layer, () -> LayerDefinition.create(new MeshDefinition(), 32, 32)); + } +} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonemFabric.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonemFabric.java deleted file mode 100644 index 7735d40..0000000 --- a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonemFabric.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.leisuretimedock.jsonem.fabric; - -import com.leisuretimedock.jsonem.Jsonem; -import net.fabricmc.api.ModInitializer; - -public final class JsonemFabric implements ModInitializer { - @Override - public void onInitialize() { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. - - // Run our common setup. - Jsonem.init(); - } -} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/client/JsonemFabricClient.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/client/JsonemFabricClient.java deleted file mode 100644 index 978c72b..0000000 --- a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/client/JsonemFabricClient.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.leisuretimedock.jsonem.fabric.client; - -import net.fabricmc.api.ClientModInitializer; - -public final class JsonemFabricClient implements ClientModInitializer { - @Override - public void onInitializeClient() { - // This entrypoint is suitable for setting up client-specific logic, such as rendering. - } -} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/config/JsonEmConfig.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/config/JsonEmConfig.java new file mode 100644 index 0000000..1497d03 --- /dev/null +++ b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/config/JsonEmConfig.java @@ -0,0 +1,49 @@ +package com.leisuretimedock.jsonem.fabric.config; + +import com.leisuretimedock.jsonem.JsonEm; +import net.fabricmc.loader.api.FabricLoader; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +public final class JsonEmConfig { + public static final Path FILE = FabricLoader.getInstance().getConfigDir().resolve("jsonem.properties"); + + public final Properties values = new Properties(); + + private static void defaultValues(Properties values) { + values.setProperty("dump_models", "false"); + } + + public void load() { + try { + if (!Files.exists(FILE)) { + save(); + } + + this.values.clear(); + try (var in = Files.newInputStream(FILE)) { + this.values.load(in); + } + } catch (IOException ex) { + JsonEm.LOG.error("Error loading config file for Json Entity Models", ex); + } + } + + public void save() { + try { + if (!Files.exists(FILE)) { + Files.createFile(FILE); + } + + defaultValues(this.values); + try (var out = Files.newOutputStream(FILE)) { + this.values.store(out, null); + } + } catch (IOException ex) { + JsonEm.LOG.error("Error saving config file for Json Entity Models", ex); + } + } +} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/platform/FabricPlatformHelper.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/platform/FabricPlatformHelper.java new file mode 100644 index 0000000..d758269 --- /dev/null +++ b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/platform/FabricPlatformHelper.java @@ -0,0 +1,11 @@ +package com.leisuretimedock.jsonem.fabric.platform; + +import com.leisuretimedock.jsonem.fabric.JsonEmFabric; +import com.leisuretimedock.jsonem.platform.IPlatFormHelper; + +public class FabricPlatformHelper implements IPlatFormHelper { + @Override + public boolean shouldDumpModels() { + return "true".equals(JsonEmFabric.CONFIG.values.getProperty("dump_models")); + } +} diff --git a/fabric/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper b/fabric/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper new file mode 100644 index 0000000..bab57b4 --- /dev/null +++ b/fabric/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper @@ -0,0 +1 @@ +com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper \ No newline at end of file diff --git a/fabric/src/main/resources/assets/jsonem/icon.png b/fabric/src/main/resources/assets/jsonem/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..54c2a1fe51d897ec145f7da4698704a2ac5b4e3a GIT binary patch literal 18118 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 literal 0 HcmV?d00001 diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 5145b6f..a81ee2f 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -15,11 +15,8 @@ "icon": "assets/jsonem/icon.png", "environment": "*", "entrypoints": { - "main": [ - "com.leisuretimedock.jsonem.fabric.JsonemFabric" - ], "client": [ - "com.leisuretimedock.jsonem.fabric.client.JsonemFabricClient" + "com.leisuretimedock.jsonem.fabric.JsonEmFabric" ] }, "mixins": [ diff --git a/forge/build.gradle b/forge/build.gradle index af89834..1bce9fc 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -18,6 +18,7 @@ configurations { canBeResolved = true canBeConsumed = false } + shadowCommon compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common developmentForge.extendsFrom common @@ -31,12 +32,11 @@ configurations { } dependencies { + compileOnly project(":common") forge "net.minecraftforge:forge:$rootProject.forge_version" modImplementation "dev.architectury:architectury-forge:$rootProject.architectury_api_version" - common(project(path: ':common', configuration: 'namedElements')) { transitive false } - shadowBundle project(path: ':common', configuration: 'transformProductionForge') } processResources { diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonEmForge.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonEmForge.java new file mode 100644 index 0000000..1acea9b --- /dev/null +++ b/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonEmForge.java @@ -0,0 +1,21 @@ +package com.leisuretimedock.jsonem.forge; + +import com.leisuretimedock.jsonem.JsonEm; +import com.leisuretimedock.jsonem.forge.config.JsonEmConfig; +import dev.architectury.platform.forge.EventBuses; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; + +@Mod(JsonEm.MOD_ID) +public final class JsonEmForge { + public JsonEmForge(IEventBus eventBus) { + // Submit our event bus to let Architectury API register our content on the right time. + EventBuses.registerModEventBus(JsonEm.MOD_ID, eventBus); + //noinspection removal + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, JsonEmConfig.spec, "jsonem.toml"); + // Run our common setup. + JsonEm.init(); + } +} diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonemForge.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonemForge.java deleted file mode 100644 index 4e5f15f..0000000 --- a/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonemForge.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.leisuretimedock.jsonem.forge; - -import com.leisuretimedock.jsonem.Jsonem; -import dev.architectury.platform.forge.EventBuses; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; - -@Mod(Jsonem.MOD_ID) -public final class JsonemForge { - @SuppressWarnings("removal") - public JsonemForge() { - // Submit our event bus to let Architectury API register our content on the right time. - EventBuses.registerModEventBus(Jsonem.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); - - // Run our common setup. - Jsonem.init(); - } -} diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/config/JsonEmConfig.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/config/JsonEmConfig.java new file mode 100644 index 0000000..f8539ed --- /dev/null +++ b/forge/src/main/java/com/leisuretimedock/jsonem/forge/config/JsonEmConfig.java @@ -0,0 +1,20 @@ +package com.leisuretimedock.jsonem.forge.config; + +import net.minecraftforge.common.ForgeConfigSpec; +import org.apache.commons.lang3.tuple.Pair; + +public class JsonEmConfig { + public static final ForgeConfigSpec spec; + public static final JsonEmConfig INSTANCE; + public final ForgeConfigSpec.BooleanValue DUMP_MODELS; + public JsonEmConfig(ForgeConfigSpec.Builder builder) { + builder.push("jsonem"); + DUMP_MODELS = builder.comment("Whether to dump models").define("dump_models", false); + builder.pop(); + } + static { + Pair config = new ForgeConfigSpec.Builder().configure(JsonEmConfig::new); + spec = config.getRight(); + INSTANCE = config.getLeft(); + } +} diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/platform/ForgePlatformHelper.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/platform/ForgePlatformHelper.java new file mode 100644 index 0000000..bf0ae75 --- /dev/null +++ b/forge/src/main/java/com/leisuretimedock/jsonem/forge/platform/ForgePlatformHelper.java @@ -0,0 +1,11 @@ +package com.leisuretimedock.jsonem.forge.platform; + +import com.leisuretimedock.jsonem.forge.config.JsonEmConfig; +import com.leisuretimedock.jsonem.platform.IPlatFormHelper; + +public class ForgePlatformHelper implements IPlatFormHelper { + @Override + public boolean shouldDumpModels() { + return JsonEmConfig.INSTANCE.DUMP_MODELS.get(); + } +} diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml index 0ba0e0d..842f504 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -11,7 +11,7 @@ authors = "FoundationGames, R3944Realms" description = ''' Migrate from Fabric 1.20.1 ''' -#logoFile = "" +logoFile = "assets/jsonem/icon.png" [[dependencies.jsonem]] modId = "forge" diff --git a/forge/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper b/forge/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper new file mode 100644 index 0000000..d5ed12c --- /dev/null +++ b/forge/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper @@ -0,0 +1 @@ +com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper \ No newline at end of file diff --git a/forge/src/main/resources/assets/jsonem/icon.png b/forge/src/main/resources/assets/jsonem/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..54c2a1fe51d897ec145f7da4698704a2ac5b4e3a GIT binary patch literal 18118 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 literal 0 HcmV?d00001 diff --git a/settings.gradle b/settings.gradle index 1b9240f..fadf72c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,3 +12,7 @@ rootProject.name = 'jsonem' include 'common' include 'fabric' include 'forge' + +project(':common').projectDir = file('common') +project(':forge').projectDir = file('forge') +project(':fabric').projectDir = file('fabric')