From f673900737cef0a1b1e3c5d796204e77a6ea3831 Mon Sep 17 00:00:00 2001 From: sylvxa Date: Sun, 8 Jun 2025 08:02:37 -0500 Subject: [PATCH 1/9] add gradle dependencies --- build.gradle | 7 +++++++ gradle.properties | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 84fe47d..57cf4c1 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,11 @@ repositories { maven { url = uri("https://repo.opencollab.dev/main/") } + + // Serves "creative" library, needed for pack converter + maven { + url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + } } sourceSets { @@ -75,6 +80,8 @@ dependencies { // Geyser compileOnly("org.geysermc.geyser:api:${project.geyser_version}") compileOnly("org.geysermc.geyser:core:${project.geyser_version}") + + api("org.geysermc.pack:converter:${project.pack_converter_version}") } processResources { diff --git a/gradle.properties b/gradle.properties index ba0369b..fc2a42c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,4 +24,7 @@ polymer_version=0.12.6+1.21.5 server_translations_version=2.5.0+1.21.5-rc1 # Find updates at https://repo.opencollab.dev/#/maven-snapshots/org/geysermc/geyser/api -geyser_version=2.7.1-SNAPSHOT \ No newline at end of file +geyser_version=2.7.1-SNAPSHOT + +# Find updates at https://repo.opencollab.dev/#/maven-snapshots/org/geysermc/pack/converter +pack_converter_version=3.3.2-SNAPSHOT \ No newline at end of file From 5f49d74cd034208753caa44ea3c72e9a5700204e Mon Sep 17 00:00:00 2001 From: sylvxa Date: Sun, 8 Jun 2025 08:42:23 -0500 Subject: [PATCH 2/9] remove ModelData in favor of creative's Model --- .../geyser/translator/BlockTranslator.java | 96 +++++++++++-------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index fcc3b33..580975e 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -7,8 +7,10 @@ import lol.sylvie.bedframe.geyser.Translator; import lol.sylvie.bedframe.mixin.BlockResourceCreatorAccessor; import lol.sylvie.bedframe.mixin.PolymerBlockResourceUtilsAccessor; +import lol.sylvie.bedframe.util.BedframeConstants; import lol.sylvie.bedframe.util.JsonHelper; import lol.sylvie.bedframe.util.ResourceHelper; +import net.kyori.adventure.key.Key; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.command.argument.BlockArgumentParser; @@ -33,7 +35,13 @@ import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent; import org.geysermc.geyser.api.util.CreativeCategory; +import org.intellij.lang.annotations.Subst; import org.joml.Vector3f; +import team.unnamed.creative.model.Model; +import team.unnamed.creative.model.ModelTexture; +import team.unnamed.creative.model.ModelTextures; +import team.unnamed.creative.serialize.minecraft.font.FontSerializer; +import team.unnamed.creative.serialize.minecraft.model.ModelSerializer; import xyz.nucleoid.packettweaker.PacketContext; import java.nio.file.Path; @@ -188,51 +196,67 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { // Geometry // TODO: More geometry types - JsonObject blockModel = ResourceHelper.readJsonResource(modelEntry.model().getNamespace(), "models/" + modelEntry.model().getPath() + ".json"); + Identifier blockModelId = modelEntry.model(); // i hate the java compiler + Key key = Key.key(blockModelId.toString()); + JsonObject blockModel = ResourceHelper.readJsonResource(blockModelId.getNamespace(), "models/" + blockModelId.getPath() + ".json"); if (blockModel == null) { LOGGER.warn("Couldn't load model for blockstate {}", state); continue; } - ModelData modelData = ModelData.fromJson(blockModel); - Identifier modelParent = modelData.parent(); + // This is unstable (https://unnamed.team/docs/creative/latest/serialization/minecraft) + Model model = ModelSerializer.INSTANCE.deserializeFromJson(blockModel, Key.key(blockModelId.toString())); + + Key modelParent = model.parent(); + String renderMethod = "opaque"; if (modelParent == null) { - LOGGER.error("Model for blockstate {} has no parent, defaulting to full block", state); - modelParent = Identifier.of("minecraft", "block/cube_all"); + // TODO: MODEL CONVERSION + } else { + // TODO: actually handle non-vanilla parents + boolean cross = modelParent.toString().equals("minecraft:block/cross"); + String geometryIdentifier = cross ? "minecraft:geometry.cross" : "minecraft:geometry.full_block"; + if (cross) renderMethod = "alpha_test_single_sided"; + + GeometryComponent geometryComponent = GeometryComponent.builder().identifier(geometryIdentifier).build(); + stateComponentBuilder.geometry(geometryComponent); } - boolean cross = modelParent.toString().equals("minecraft:block/cross"); - String geometryIdentifier = cross ? "minecraft:geometry.cross" : "minecraft:geometry.full_block"; - String renderMethod = cross ? "alpha_test_single_sided" : "opaque"; - GeometryComponent geometryComponent = GeometryComponent.builder().identifier(geometryIdentifier).build(); - stateComponentBuilder.geometry(geometryComponent); // Textures - List> faceMap = parentFaceMap.getOrDefault(modelParent.getPath(), parentFaceMap.get("block/cube_all")); - for (Pair face : faceMap) { - String javaFaceName = face.getLeft(); - String bedrockFaceName = face.getRight(); - if (!modelData.textures.containsKey(javaFaceName)) continue; - - String textureName = modelData.textures.get(javaFaceName); - Identifier textureIdentifier = Identifier.of(textureName); - String texturePath = "textures/" + textureIdentifier.getPath(); - String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); - - JsonObject thisTexture = new JsonObject(); - thisTexture.addProperty("textures", bedrockPath); - textureDataObject.add(textureName, thisTexture); - - stateComponentBuilder.materialInstance(bedrockFaceName, MaterialInstance.builder() - .renderMethod(renderMethod) - .texture(textureName) - .faceDimming(true) - .ambientOcclusion(true) - .build()); - - ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); + ModelTextures textures = model.textures(); + Map textureMap = textures.variables(); + List> faceMap = parentFaceMap.get(modelParent.value()); + if (faceMap != null) { + for (Pair face : faceMap) { + String javaFaceName = face.getLeft(); + String bedrockFaceName = face.getRight(); + if (!textureMap.containsKey(javaFaceName)) continue; + + ModelTexture textureObject = textureMap.get(javaFaceName); + String textureName = textureObject.key().asString(); + Identifier textureIdentifier = Identifier.of(textureName); + String texturePath = "textures/" + textureIdentifier.getPath(); + String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); + + JsonObject thisTexture = new JsonObject(); + thisTexture.addProperty("textures", bedrockPath); + textureDataObject.add(textureName, thisTexture); + + stateComponentBuilder.materialInstance(bedrockFaceName, MaterialInstance.builder() + .renderMethod(renderMethod) + .texture(textureName) + .faceDimming(true) + .ambientOcclusion(true) + .build()); + + ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); + } + } else { + LOGGER.error("no parent found; fixing as we speak lmfao"); + continue; } + stateComponentBuilder.collisionBox(voxelShapeToBoxComponent(realBlock.getDefaultState().getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); CustomBlockComponents stateComponents = stateComponentBuilder.build(); @@ -294,10 +318,4 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { public void register(EventBus eventBus, Path packRoot) { eventBus.subscribe(this, GeyserDefineCustomBlocksEvent.class, event -> handle(event, packRoot)); } - - record ModelData(Identifier parent, Map textures) { - public static ModelData fromJson(JsonObject object) { - return JsonHelper.GSON.fromJson(object, ModelData.class); - } - } } From f8754808401ad37b8bbf63613b5d9baa3d7d4d5c Mon Sep 17 00:00:00 2001 From: sylvxa Date: Sun, 8 Jun 2025 12:11:33 -0500 Subject: [PATCH 3/9] crummy model converter, BlockTranslator needs refactoring atm --- .../sylvie/bedframe/geyser/PackGenerator.java | 6 + .../sylvie/bedframe/geyser/Translator.java | 2 +- .../geyser/model/JavaGeometryConverter.java | 175 ++++++++++++++++++ .../geyser/translator/BlockTranslator.java | 116 +++++++++--- .../bedframe/util/BedframeConstants.java | 1 + 5 files changed, 271 insertions(+), 29 deletions(-) create mode 100644 src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java diff --git a/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java b/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java index b6b6843..965b139 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java @@ -9,6 +9,9 @@ import lol.sylvie.bedframe.util.ZipHelper; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.Version; +import org.geysermc.pack.converter.util.NioDirectoryFileTreeReader; +import team.unnamed.creative.ResourcePack; +import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackReader; import java.io.File; import java.io.FileWriter; @@ -16,6 +19,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.function.Function; import static lol.sylvie.bedframe.util.BedframeConstants.GSON; import static lol.sylvie.bedframe.util.BedframeConstants.METADATA; @@ -24,6 +28,8 @@ * Compiles the output of the {@link Translator} classes into a Bedrock resource pack */ public class PackGenerator { + private static final HashMap RESOURCE_PACK_MAP = new HashMap<>(); + private static JsonArray getVersionArray() { // TODO: A regex would be more inclusive Version version = BedframeConstants.METADATA.getVersion(); diff --git a/src/main/java/lol/sylvie/bedframe/geyser/Translator.java b/src/main/java/lol/sylvie/bedframe/geyser/Translator.java index 8fd4576..c1e5cd1 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/Translator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/Translator.java @@ -58,7 +58,7 @@ protected void writeOrThrow(FileWriter writer, String content) { } catch (IOException e) { throw new RuntimeException(e); } } - protected static void writeJsonToFile(JsonObject object, File file) { + protected static void writeJsonToFile(Object object, File file) { try (FileWriter writer = new FileWriter(file)) { GSON.toJson(object, writer); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java new file mode 100644 index 0000000..826274a --- /dev/null +++ b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java @@ -0,0 +1,175 @@ +package lol.sylvie.bedframe.geyser.model; + +import net.kyori.adventure.key.Key; +import net.minecraft.util.Pair; +import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity; +import org.geysermc.pack.bedrock.resource.models.entity.modelentity.Geometry; +import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Bones; +import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Description; +import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.Cubes; +import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.TextureMeshes; +import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.Uv; +import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.*; +import team.unnamed.creative.base.Axis3D; +import team.unnamed.creative.base.CubeFace; +import team.unnamed.creative.model.Element; +import team.unnamed.creative.model.ElementFace; +import team.unnamed.creative.model.ElementRotation; +import team.unnamed.creative.model.Model; +import team.unnamed.creative.texture.TextureUV; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static lol.sylvie.bedframe.util.BedframeConstants.LOGGER; + + +/* + * Concepts here were inspired by: + * tomalbrc's fork: https://github.com/tomalbrc/bedframe/blob/main/src/main/java/lol/sylvie/bedframe/geyser/translator/JavaToBedrockGeometryTranslator.java + * Pack Converter's ModelConverter: https://github.com/GeyserMC/PackConverter/blob/master/converter/src/main/java/org/geysermc/pack/converter/converter/model/ModelConverter.java#L62 + * (I'm not using ModelConverter directly as it requires some resource pack boilerplate we don't need) + */ +public class JavaGeometryConverter { + private static final String FORMAT_VERSION = "1.16.0"; + private static final String GEOMETRY_FORMAT = "geometry.%s"; + + private static float[] javaPosToBedrock(float[] java) { + return new float[] { java[0] - 8.0f, java[1], java[2] - 8.0f }; + } + + private static void applyFaceUv(Uv uv, CubeFace cubeFace, float[] uvValue, String texture) { + switch (cubeFace) { + case UP -> { + Up up = new Up(); + up.uv(uvValue); + up.materialInstance(texture); + uv.up(up); + } + case DOWN -> { + Down down = new Down(); + down.uv(uvValue); + down.materialInstance(texture); + uv.down(down); + } + case NORTH -> { + North north = new North(); + north.uv(uvValue); + north.materialInstance(texture); + uv.north(north); + } + case SOUTH -> { + South south = new South(); + south.uv(uvValue); + south.materialInstance(texture); + uv.south(south); + } + case EAST -> { + East east = new East(); + east.uv(uvValue); + east.materialInstance(texture); + uv.east(east); + } + case WEST -> { + West west = new West(); + west.uv(uvValue); + west.materialInstance(texture); + uv.west(west); + } + } + } + + public static Pair convert(Model model) { + List elements = model.elements(); + if (elements.isEmpty()) { + LOGGER.error("Model {} is empty :(", model.key()); + return null; + } + + ModelEntity modelEntity = new ModelEntity(); + modelEntity.formatVersion(FORMAT_VERSION); + + Geometry geometry = new Geometry(); + List bones = new ArrayList<>(); + + int nthElement = 0; + for (Element element : elements) { + float[] javaFrom = element.from().toArray(); + float[] javaTo = element.to().toArray(); + + // TODO: I've seen a lot of discussion over whether it should be one bone per cube or one bone for all cubes + Bones bone = new Bones(); + bone.name("element_" + nthElement); + + // Transform + Cubes cube = new Cubes(); + cube.origin(javaPosToBedrock(javaFrom)); + cube.size(new float[] { javaTo[0] - javaFrom[0], javaTo[1] - javaFrom[1], javaTo[2] - javaFrom[2] }); + cube.inflate(0f); + + // Rotation + ElementRotation rotation = element.rotation(); + if (rotation != null) { // This can be null actually + float[] rotOrigin = rotation.origin().toArray(); + bone.pivot(javaPosToBedrock(rotOrigin)); + + // We are given an angle and an axis, and we need to provide a vector + float rotValue = rotation.angle(); + Axis3D axis = rotation.axis(); + float[] rotArray = new float[] { + axis == Axis3D.X ? rotValue : 0f, + axis == Axis3D.Y ? rotValue : 0f, + axis == Axis3D.Z ? rotValue : 0f + }; + bone.rotation(rotArray); + } else { + // this might be default, not sure + bone.pivot(new float[] { 0f, 0f, 0f }); + } + + // UV + Uv uv = new Uv(); + if (!element.faces().isEmpty()) { + for (Map.Entry faceEntry : element.faces().entrySet()) { + ElementFace face = faceEntry.getValue(); + TextureUV textureUV = face.uv0(); + if (textureUV == null) textureUV = TextureUV.uv(0, 0, 16, 16); + float[] uvValue = new float[] { textureUV.from().x(), textureUV.from().y() }; + // TODO: see if uv size is necessary + + applyFaceUv(uv, faceEntry.getKey(), uvValue, face.texture().replace("#", "")); + } + } else { + float[] fallbackValue = new float[] { 0, 0 }; + for (CubeFace face : CubeFace.values()) { + applyFaceUv(uv, face, fallbackValue, face.name()); + } + } + + // Textures + + cube.uv(uv); + bone.cubes(List.of(cube)); + bone.textureMeshes(null); + + bones.add(bone); + + nthElement++; + } + + geometry.bones(bones); + modelEntity.geometry(List.of(geometry)); + + String namespace = model.key().namespace(); + String geometryName = String.format(GEOMETRY_FORMAT, (namespace.equals(Key.MINECRAFT_NAMESPACE) ? "" : namespace + ".") + model.key().value().replace(":", ".").replace("/", ".")); + + Description description = new Description(); + description.identifier(geometryName); + description.textureWidth(16); + description.textureHeight(16); + geometry.description(description); + + return new Pair<>(geometryName, modelEntity); + } +} diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index 580975e..67601a4 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -1,18 +1,23 @@ package lol.sylvie.bedframe.geyser.translator; import com.google.gson.JsonObject; +import com.mojang.logging.LogListeners; import eu.pb4.polymer.blocks.api.BlockResourceCreator; import eu.pb4.polymer.blocks.api.PolymerBlockModel; import eu.pb4.polymer.blocks.api.PolymerTexturedBlock; +import lol.sylvie.bedframe.geyser.PackGenerator; import lol.sylvie.bedframe.geyser.Translator; +import lol.sylvie.bedframe.geyser.model.JavaGeometryConverter; import lol.sylvie.bedframe.mixin.BlockResourceCreatorAccessor; import lol.sylvie.bedframe.mixin.PolymerBlockResourceUtilsAccessor; import lol.sylvie.bedframe.util.BedframeConstants; import lol.sylvie.bedframe.util.JsonHelper; import lol.sylvie.bedframe.util.ResourceHelper; +import net.fabricmc.loader.api.ModContainer; import net.kyori.adventure.key.Key; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.DoorBlock; import net.minecraft.command.argument.BlockArgumentParser; import net.minecraft.registry.Registries; import net.minecraft.state.property.BooleanProperty; @@ -35,7 +40,13 @@ import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent; import org.geysermc.geyser.api.util.CreativeCategory; +import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity; +import org.geysermc.pack.converter.converter.model.ModelStitcher; +import org.geysermc.pack.converter.util.DefaultLogListener; +import org.geysermc.pack.converter.util.LogListener; import org.intellij.lang.annotations.Subst; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import team.unnamed.creative.model.Model; import team.unnamed.creative.model.ModelTexture; @@ -49,6 +60,7 @@ import java.util.function.BiConsumer; import java.util.stream.Stream; +import static lol.sylvie.bedframe.util.BedframeConstants.GSON; import static lol.sylvie.bedframe.util.BedframeConstants.LOGGER; import static lol.sylvie.bedframe.util.PathHelper.createDirectoryOrThrow; @@ -147,10 +159,19 @@ private BoxComponent voxelShapeToBoxComponent(VoxelShape shape) { float sizeY = (float) box.getLengthY() * 16; float sizeZ = (float) box.getLengthZ() * 16; - Vec3d origin = box.getMinPos(); - Vector3f originNormalized = new Vec3d(origin.getX(), origin.getY(), origin.getZ()).toVector3f(); + Vector3f origin = box.getMinPos().toVector3f(); + return new BoxComponent(origin.x() - 8, origin.y(), origin.z() - 8, sizeX, sizeY, sizeZ); + } - return new BoxComponent(originNormalized.x() - 8, originNormalized.y(), originNormalized.z() - 8, sizeX, sizeY, sizeZ); + private Model resolveModel(Identifier identifier) { + // This is unstable (https://unnamed.team/docs/creative/latest/serialization/minecraft) + try { + JsonObject model = ResourceHelper.readJsonResource(identifier.getNamespace(), "models/" + identifier.getPath() + ".json"); + return ModelSerializer.INSTANCE.deserializeFromJson(model, Key.key(identifier.toString())); + } catch (RuntimeException e) { + LOGGER.warn("Couldn't resolve model {}", identifier); + return null; + } } // Referenced https://github.com/GeyserMC/Hydraulic/blob/master/shared/src/main/java/org/geysermc/hydraulic/block/BlockPackModule.java#L54 @@ -158,6 +179,9 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { Path textureDir = createDirectoryOrThrow(packRoot.resolve("textures")); createDirectoryOrThrow(textureDir.resolve("blocks")); + Path modelsDir = createDirectoryOrThrow(packRoot.resolve("models")); + Path blockModelsDir = createDirectoryOrThrow(modelsDir.resolve("blocks")); + JsonObject terrainTextureObject = new JsonObject(); terrainTextureObject.addProperty("resource_pack_name", "Bedframe"); terrainTextureObject.addProperty("texture_name", "atlas.terrain"); @@ -190,43 +214,43 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { PolymerBlockModel[] polymerBlockModels = ((BlockResourceCreatorAccessor) (Object) creator).getModels().get(polymerBlockState); PolymerBlockModel modelEntry = polymerBlockModels[0]; // TODO: java selects one by weight, does bedrock support this? + if (modelEntry == null) { + LOGGER.warn("No model specified for blockstate {}", state); + continue; + } + // Rotation TransformationComponent rotationComponent = new TransformationComponent((360 - modelEntry.x()) % 360, (360 - modelEntry.y()) % 360, 0); stateComponentBuilder.transformation(rotationComponent); // Geometry - // TODO: More geometry types - Identifier blockModelId = modelEntry.model(); // i hate the java compiler - Key key = Key.key(blockModelId.toString()); - JsonObject blockModel = ResourceHelper.readJsonResource(blockModelId.getNamespace(), "models/" + blockModelId.getPath() + ".json"); + String renderMethod = state.isOpaque() ? "opaque" : "blend"; // TODO: Hydraulic also faces this problem; Figure out when to use alpha_test + Identifier blockModelId = modelEntry.model(); + Model blockModel = resolveModel(blockModelId); if (blockModel == null) { LOGGER.warn("Couldn't load model for blockstate {}", state); continue; } - // This is unstable (https://unnamed.team/docs/creative/latest/serialization/minecraft) - Model model = ModelSerializer.INSTANCE.deserializeFromJson(blockModel, Key.key(blockModelId.toString())); - - Key modelParent = model.parent(); - String renderMethod = "opaque"; - if (modelParent == null) { - // TODO: MODEL CONVERSION - } else { - // TODO: actually handle non-vanilla parents - boolean cross = modelParent.toString().equals("minecraft:block/cross"); + Key modelParentKey = blockModel.parent(); + if (modelParentKey != null) { + // Vanilla parent + boolean cross = modelParentKey.toString().equals("minecraft:block/cross"); String geometryIdentifier = cross ? "minecraft:geometry.cross" : "minecraft:geometry.full_block"; if (cross) renderMethod = "alpha_test_single_sided"; GeometryComponent geometryComponent = GeometryComponent.builder().identifier(geometryIdentifier).build(); stateComponentBuilder.geometry(geometryComponent); - } + // Textures + ModelTextures textures = blockModel.textures(); + Map textureMap = textures.variables(); + List> faceMap = parentFaceMap.get(modelParentKey.value()); + if (faceMap == null) { + LOGGER.error("No texture map found for parent {} of blockstate {}", modelParentKey, state); + continue; + } - // Textures - ModelTextures textures = model.textures(); - Map textureMap = textures.variables(); - List> faceMap = parentFaceMap.get(modelParent.value()); - if (faceMap != null) { for (Pair face : faceMap) { String javaFaceName = face.getLeft(); String bedrockFaceName = face.getRight(); @@ -246,18 +270,54 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { .renderMethod(renderMethod) .texture(textureName) .faceDimming(true) - .ambientOcclusion(true) + .ambientOcclusion(blockModel.ambientOcclusion()) .build()); ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); } } else { - LOGGER.error("no parent found; fixing as we speak lmfao"); - continue; - } + // Custom model + ModelStitcher.Provider provider = key -> resolveModel(Identifier.of(key.asString())); + blockModel = new ModelStitcher(provider, blockModel).stitch(); // This resolves parent models (?) + + Pair nameAndModel = JavaGeometryConverter.convert(blockModel); + if (nameAndModel == null) { + LOGGER.error("Couldn't convert model for blockstate {}", state); + continue; + } + String geometryId = nameAndModel.getLeft(); + writeJsonToFile(nameAndModel.getRight(), blockModelsDir.resolve(geometryId + ".geo.json").toFile()); + for (Map.Entry entry : blockModel.textures().variables().entrySet()) { + String key = entry.getKey(); + ModelTexture texture = entry.getValue(); + String textureName = texture.key().asString(); + Identifier textureIdentifier = Identifier.of(textureName); + String texturePath = "textures/" + textureIdentifier.getPath(); + String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); + + JsonObject thisTexture = new JsonObject(); + thisTexture.addProperty("textures", bedrockPath); + textureDataObject.add(textureName, thisTexture); + + System.out.println(key + " -> " + textureName + " -> "+ bedrockPath); + stateComponentBuilder.materialInstance(key, MaterialInstance.builder() + .renderMethod(renderMethod) + .texture(textureName) + .faceDimming(true) + .ambientOcclusion(blockModel.ambientOcclusion()) + .build()); + + ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); + } + + GeometryComponent geometryComponent = GeometryComponent.builder().identifier(geometryId).build(); + stateComponentBuilder.geometry(geometryComponent); + } - stateComponentBuilder.collisionBox(voxelShapeToBoxComponent(realBlock.getDefaultState().getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); + stateComponentBuilder.collisionBox(voxelShapeToBoxComponent(state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); + stateComponentBuilder.selectionBox(voxelShapeToBoxComponent(state.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); + stateComponentBuilder.lightEmission(state.getLuminance()); CustomBlockComponents stateComponents = stateComponentBuilder.build(); if (state.getProperties().isEmpty()) { diff --git a/src/main/java/lol/sylvie/bedframe/util/BedframeConstants.java b/src/main/java/lol/sylvie/bedframe/util/BedframeConstants.java index efdd3ba..414c66b 100644 --- a/src/main/java/lol/sylvie/bedframe/util/BedframeConstants.java +++ b/src/main/java/lol/sylvie/bedframe/util/BedframeConstants.java @@ -5,6 +5,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.metadata.ModMetadata; import net.minecraft.util.Identifier; +import org.geysermc.pack.converter.util.LogListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 426a1b350603418db87ad69c66e874337ca06959 Mon Sep 17 00:00:00 2001 From: sylvxa Date: Sun, 8 Jun 2025 13:04:12 -0500 Subject: [PATCH 4/9] add uv sizing --- .../geyser/model/JavaGeometryConverter.java | 37 ++++--- .../geyser/translator/BlockTranslator.java | 64 ++++++------- .../lol/sylvie/testmod/block/ModBlocks.java | 8 ++ .../impl/TexturedFlowerPotExampleBlock.java | 11 +++ .../lol/sylvie/testmod/item/ModItems.java | 1 + .../blockstates/example_flower_pot.json | 7 ++ .../items/example_flower_pot.json | 6 ++ .../models/block/example_flower_pot.json | 90 ++++++++++++++++++ .../textures/block/example_flower_pot.png | Bin 0 -> 233 bytes 9 files changed, 177 insertions(+), 47 deletions(-) create mode 100644 src/testmod/java/lol/sylvie/testmod/block/impl/TexturedFlowerPotExampleBlock.java create mode 100644 src/testmod/resources/assets/bedframe-testmod/blockstates/example_flower_pot.json create mode 100644 src/testmod/resources/assets/bedframe-testmod/items/example_flower_pot.json create mode 100644 src/testmod/resources/assets/bedframe-testmod/models/block/example_flower_pot.json create mode 100644 src/testmod/resources/assets/bedframe-testmod/textures/block/example_flower_pot.png diff --git a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java index 826274a..25d904b 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java @@ -19,12 +19,12 @@ import team.unnamed.creative.texture.TextureUV; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import static lol.sylvie.bedframe.util.BedframeConstants.LOGGER; - /* * Concepts here were inspired by: * tomalbrc's fork: https://github.com/tomalbrc/bedframe/blob/main/src/main/java/lol/sylvie/bedframe/geyser/translator/JavaToBedrockGeometryTranslator.java @@ -39,41 +39,47 @@ private static float[] javaPosToBedrock(float[] java) { return new float[] { java[0] - 8.0f, java[1], java[2] - 8.0f }; } - private static void applyFaceUv(Uv uv, CubeFace cubeFace, float[] uvValue, String texture) { + private static void applyFaceUv(Uv uv, CubeFace cubeFace, float[] uvValue, float[] uvSize, String texture) { switch (cubeFace) { case UP -> { Up up = new Up(); up.uv(uvValue); + up.uvSize(uvSize); up.materialInstance(texture); uv.up(up); } case DOWN -> { Down down = new Down(); down.uv(uvValue); + down.uvSize(uvSize); down.materialInstance(texture); uv.down(down); } case NORTH -> { North north = new North(); north.uv(uvValue); + north.uvSize(uvSize); north.materialInstance(texture); uv.north(north); } case SOUTH -> { South south = new South(); south.uv(uvValue); + south.uvSize(uvSize); south.materialInstance(texture); uv.south(south); } case EAST -> { East east = new East(); east.uv(uvValue); + east.uvSize(uvSize); east.materialInstance(texture); uv.east(east); } case WEST -> { West west = new West(); west.uv(uvValue); + west.uvSize(uvSize); west.materialInstance(texture); uv.west(west); } @@ -132,23 +138,32 @@ public static Pair convert(Model model) { Uv uv = new Uv(); if (!element.faces().isEmpty()) { for (Map.Entry faceEntry : element.faces().entrySet()) { + CubeFace direction = faceEntry.getKey(); ElementFace face = faceEntry.getValue(); - TextureUV textureUV = face.uv0(); - if (textureUV == null) textureUV = TextureUV.uv(0, 0, 16, 16); - float[] uvValue = new float[] { textureUV.from().x(), textureUV.from().y() }; - // TODO: see if uv size is necessary - applyFaceUv(uv, faceEntry.getKey(), uvValue, face.texture().replace("#", "")); + TextureUV textureUV = face.uv0(); + if (textureUV == null) + textureUV = TextureUV.uv(0, 0, 1, 1); + else textureUV = TextureUV.uv(textureUV.from().multiply(16f), textureUV.to().multiply(16f)); + + float[] uvValue; + float[] uvSize; + if (direction.axis() == Axis3D.Y) { + uvValue = new float[] { textureUV.to().x(), textureUV.to().y() }; + uvSize = new float[] { (textureUV.from().x() - uvValue[0]), (textureUV.from().y() - uvValue[1]) }; + } else { + uvValue = new float[] { textureUV.from().x(), textureUV.from().y() }; + uvSize = new float[] { (textureUV.to().x() - uvValue[0]), (textureUV.to().y() - uvValue[1]) }; + } + + applyFaceUv(uv, direction, uvValue, uvSize, face.texture().replace("#", "")); } } else { - float[] fallbackValue = new float[] { 0, 0 }; for (CubeFace face : CubeFace.values()) { - applyFaceUv(uv, face, fallbackValue, face.name()); + applyFaceUv(uv, face, new float[] { 0, 0 }, new float[] { 16, 16 }, face.name()); } } - // Textures - cube.uv(uv); bone.cubes(List.of(cube)); bone.textureMeshes(null); diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index 67601a4..573ea78 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -232,6 +232,7 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { continue; } + HashMap materials = new HashMap<>(); Key modelParentKey = blockModel.parent(); if (modelParentKey != null) { // Vanilla parent @@ -251,29 +252,13 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { continue; } + for (Pair face : faceMap) { String javaFaceName = face.getLeft(); String bedrockFaceName = face.getRight(); if (!textureMap.containsKey(javaFaceName)) continue; - ModelTexture textureObject = textureMap.get(javaFaceName); - String textureName = textureObject.key().asString(); - Identifier textureIdentifier = Identifier.of(textureName); - String texturePath = "textures/" + textureIdentifier.getPath(); - String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); - - JsonObject thisTexture = new JsonObject(); - thisTexture.addProperty("textures", bedrockPath); - textureDataObject.add(textureName, thisTexture); - - stateComponentBuilder.materialInstance(bedrockFaceName, MaterialInstance.builder() - .renderMethod(renderMethod) - .texture(textureName) - .faceDimming(true) - .ambientOcclusion(blockModel.ambientOcclusion()) - .build()); - - ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); + materials.put(bedrockFaceName, textureMap.get(javaFaceName)); } } else { // Custom model @@ -291,30 +276,37 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { for (Map.Entry entry : blockModel.textures().variables().entrySet()) { String key = entry.getKey(); ModelTexture texture = entry.getValue(); - String textureName = texture.key().asString(); - Identifier textureIdentifier = Identifier.of(textureName); - String texturePath = "textures/" + textureIdentifier.getPath(); - String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); - - JsonObject thisTexture = new JsonObject(); - thisTexture.addProperty("textures", bedrockPath); - textureDataObject.add(textureName, thisTexture); - - System.out.println(key + " -> " + textureName + " -> "+ bedrockPath); - stateComponentBuilder.materialInstance(key, MaterialInstance.builder() - .renderMethod(renderMethod) - .texture(textureName) - .faceDimming(true) - .ambientOcclusion(blockModel.ambientOcclusion()) - .build()); - - ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); + materials.put(key, texture); } GeometryComponent geometryComponent = GeometryComponent.builder().identifier(geometryId).build(); stateComponentBuilder.geometry(geometryComponent); } + // Textures/materials + for (Map.Entry entry : materials.entrySet()) { + ModelTexture texture = entry.getValue(); + String textureName = texture.key().asString(); + Identifier textureIdentifier = Identifier.of(textureName); + + String texturePath = "textures/" + textureIdentifier.getPath(); + String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); + + JsonObject thisTexture = new JsonObject(); + thisTexture.addProperty("textures", bedrockPath); + textureDataObject.add(textureName, thisTexture); + + stateComponentBuilder.materialInstance(entry.getKey(), MaterialInstance.builder() + .renderMethod(renderMethod) + .texture(textureName) + .faceDimming(true) + .ambientOcclusion(blockModel.ambientOcclusion()) + .build()); + + ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); + } + + // Collision stateComponentBuilder.collisionBox(voxelShapeToBoxComponent(state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); stateComponentBuilder.selectionBox(voxelShapeToBoxComponent(state.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); stateComponentBuilder.lightEmission(state.getLuminance()); diff --git a/src/testmod/java/lol/sylvie/testmod/block/ModBlocks.java b/src/testmod/java/lol/sylvie/testmod/block/ModBlocks.java index 04c7442..6d98148 100644 --- a/src/testmod/java/lol/sylvie/testmod/block/ModBlocks.java +++ b/src/testmod/java/lol/sylvie/testmod/block/ModBlocks.java @@ -3,6 +3,7 @@ import eu.pb4.polymer.core.api.item.PolymerBlockItem; import lol.sylvie.testmod.Testmod; import lol.sylvie.testmod.block.impl.TexturedExampleBlock; +import lol.sylvie.testmod.block.impl.TexturedFlowerPotExampleBlock; import lol.sylvie.testmod.block.impl.TexturedFlowerExampleBlock; import lol.sylvie.testmod.block.impl.TexturedLogExampleBlock; import net.minecraft.block.AbstractBlock; @@ -40,6 +41,13 @@ public class ModBlocks { Items.WITHER_ROSE ); + public static final Block EXAMPLE_FLOWER_POT = register( + "example_flower_pot", + TexturedFlowerPotExampleBlock::new, + AbstractBlock.Settings.create().sounds(BlockSoundGroup.FLOWERBED).breakInstantly().nonOpaque(), + Items.FLOWER_POT + ); + private static Block register(String name, Function blockFactory, AbstractBlock.Settings settings, Item polymerItem) { RegistryKey blockKey = keyOfBlock(name); Block block = blockFactory.apply(settings.registryKey(blockKey)); diff --git a/src/testmod/java/lol/sylvie/testmod/block/impl/TexturedFlowerPotExampleBlock.java b/src/testmod/java/lol/sylvie/testmod/block/impl/TexturedFlowerPotExampleBlock.java new file mode 100644 index 0000000..8b80b9b --- /dev/null +++ b/src/testmod/java/lol/sylvie/testmod/block/impl/TexturedFlowerPotExampleBlock.java @@ -0,0 +1,11 @@ +package lol.sylvie.testmod.block.impl; + +import eu.pb4.polymer.blocks.api.BlockModelType; +import lol.sylvie.testmod.Testmod; +import net.minecraft.util.Identifier; + +public class TexturedFlowerPotExampleBlock extends SimplePolymerTexturedBlock { + public TexturedFlowerPotExampleBlock(Settings settings) { + super(settings, BlockModelType.TRANSPARENT_BLOCK, Identifier.of(Testmod.MOD_ID, "block/example_flower_pot")); + } +} diff --git a/src/testmod/java/lol/sylvie/testmod/item/ModItems.java b/src/testmod/java/lol/sylvie/testmod/item/ModItems.java index 540e4ef..a61dfb5 100644 --- a/src/testmod/java/lol/sylvie/testmod/item/ModItems.java +++ b/src/testmod/java/lol/sylvie/testmod/item/ModItems.java @@ -57,6 +57,7 @@ public static void initialize() { itemGroup.add(ModBlocks.EXAMPLE_BLOCK.asItem()); itemGroup.add(ModBlocks.EXAMPLE_LOG.asItem()); itemGroup.add(ModBlocks.EXAMPLE_FLOWER.asItem()); + itemGroup.add(ModBlocks.EXAMPLE_FLOWER_POT.asItem()); }); } } \ No newline at end of file diff --git a/src/testmod/resources/assets/bedframe-testmod/blockstates/example_flower_pot.json b/src/testmod/resources/assets/bedframe-testmod/blockstates/example_flower_pot.json new file mode 100644 index 0000000..fbf555c --- /dev/null +++ b/src/testmod/resources/assets/bedframe-testmod/blockstates/example_flower_pot.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "bedframe-testmod:block/example_flower_pot" + } + } +} \ No newline at end of file diff --git a/src/testmod/resources/assets/bedframe-testmod/items/example_flower_pot.json b/src/testmod/resources/assets/bedframe-testmod/items/example_flower_pot.json new file mode 100644 index 0000000..4b806e2 --- /dev/null +++ b/src/testmod/resources/assets/bedframe-testmod/items/example_flower_pot.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "bedframe-testmod:block/example_flower_pot" + } +} \ No newline at end of file diff --git a/src/testmod/resources/assets/bedframe-testmod/models/block/example_flower_pot.json b/src/testmod/resources/assets/bedframe-testmod/models/block/example_flower_pot.json new file mode 100644 index 0000000..b8a8fa4 --- /dev/null +++ b/src/testmod/resources/assets/bedframe-testmod/models/block/example_flower_pot.json @@ -0,0 +1,90 @@ +{ + "credit": "Made with Blockbench", + "ambientocclusion": false, + "texture_size": [32, 32], + "textures": { + "3": "bedframe-testmod:block/example_flower_pot" + }, + "elements": [ + { + "from": [5, 0, 5], + "to": [6, 6, 11], + "faces": { + "north": {"uv": [4, 6, 4.5, 9], "texture": "#3"}, + "east": {"uv": [5.5, 0, 8.5, 3], "texture": "#3"}, + "south": {"uv": [4.5, 6, 5, 9], "texture": "#3"}, + "west": {"uv": [5.5, 0, 8.5, 3], "texture": "#3"}, + "up": {"uv": [5.5, 9, 5, 6], "texture": "#3"}, + "down": {"uv": [5.5, 6, 5, 9], "texture": "#3", "cullface": "down"} + } + }, + { + "from": [10, 0, 5], + "to": [11, 6, 11], + "faces": { + "north": {"uv": [4.5, 6, 5, 9], "texture": "#3"}, + "east": {"uv": [5.5, 0, 8.5, 3], "texture": "#3"}, + "south": {"uv": [4, 6, 4.5, 9], "texture": "#3"}, + "west": {"uv": [5.5, 0, 8.5, 3], "texture": "#3"}, + "up": {"uv": [6, 9, 5.5, 6], "texture": "#3"}, + "down": {"uv": [6, 6, 5.5, 9], "texture": "#3", "cullface": "down"} + } + }, + { + "from": [6, 0, 5], + "to": [10, 6, 6], + "faces": { + "north": {"uv": [5.5, 3, 7.5, 6], "texture": "#3"}, + "south": {"uv": [5.5, 3, 7.5, 6], "texture": "#3"}, + "up": {"uv": [8, 6.5, 6, 6], "texture": "#3"}, + "down": {"uv": [8, 6.5, 6, 7], "texture": "#3", "cullface": "down"} + } + }, + { + "from": [6, 0, 10], + "to": [10, 6, 11], + "faces": { + "north": {"uv": [5.5, 3, 7.5, 6], "texture": "#3"}, + "south": {"uv": [5.5, 3, 7.5, 6], "texture": "#3"}, + "up": {"uv": [8, 7, 6, 6.5], "texture": "#3"}, + "down": {"uv": [8, 6, 6, 6.5], "texture": "#3", "cullface": "down"} + } + }, + { + "from": [6, 0, 6], + "to": [10, 4, 10], + "faces": { + "up": {"uv": [2, 8, 0, 6], "texture": "#3"}, + "down": {"uv": [4, 6, 2, 8], "texture": "#3", "cullface": "down"} + } + }, + { + "from": [2.6, 4, 8], + "to": [13.4, 16, 8], + "shade": false, + "rotation": {"angle": 45, "axis": "y", "origin": [8, 8, 8], "rescale": true}, + "faces": { + "north": {"uv": [0, 0, 5.5, 6], "texture": "#3"}, + "south": {"uv": [0, 0, 5.5, 6], "texture": "#3"} + } + }, + { + "from": [8, 4, 2.6], + "to": [8, 16, 13.4], + "shade": false, + "rotation": {"angle": 45, "axis": "y", "origin": [8, 8, 8], "rescale": true}, + "faces": { + "east": {"uv": [0, 0, 5.5, 6], "texture": "#3"}, + "west": {"uv": [0, 0, 5.5, 6], "texture": "#3"} + } + } + ], + "groups": [ + { + "name": "flower_pot_cross", + "origin": [8, 8, 8], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6] + } + ] +} \ No newline at end of file diff --git a/src/testmod/resources/assets/bedframe-testmod/textures/block/example_flower_pot.png b/src/testmod/resources/assets/bedframe-testmod/textures/block/example_flower_pot.png new file mode 100644 index 0000000000000000000000000000000000000000..addf7dea83ad7f6af8e1077d47dd22a8f5e6e036 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}i#=T&Ln2z= zPC3ZipupqGUD(lg=;-9+5<9hzkA&{sW#VtUps>hAd-^`%#tZ+Z#q*drD7RhOH{blS zPHZSJEm~VR-d@$TbKjC;^9a9%z|>9*EMB@k|J7J=iHHQb@?+* zW}g2+>F@4-{s)9VZ546l|1))u)&u>JhMm9e&;L{=w5vQJWABHfZ;E%7KiDSKdB6Yi gUgoYv8eNOt)ciJmoF{oM1?XG`Pgg&ebxsLQ0IEP-SO5S3 literal 0 HcmV?d00001 From 48dd55656b712c2cfa0e489d9fd95150eee523df Mon Sep 17 00:00:00 2001 From: sylvxa Date: Sun, 8 Jun 2025 13:24:36 -0500 Subject: [PATCH 5/9] fix particles --- .../geyser/translator/BlockTranslator.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index 573ea78..5609bf2 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -1,23 +1,17 @@ package lol.sylvie.bedframe.geyser.translator; import com.google.gson.JsonObject; -import com.mojang.logging.LogListeners; import eu.pb4.polymer.blocks.api.BlockResourceCreator; import eu.pb4.polymer.blocks.api.PolymerBlockModel; import eu.pb4.polymer.blocks.api.PolymerTexturedBlock; -import lol.sylvie.bedframe.geyser.PackGenerator; import lol.sylvie.bedframe.geyser.Translator; import lol.sylvie.bedframe.geyser.model.JavaGeometryConverter; import lol.sylvie.bedframe.mixin.BlockResourceCreatorAccessor; import lol.sylvie.bedframe.mixin.PolymerBlockResourceUtilsAccessor; -import lol.sylvie.bedframe.util.BedframeConstants; -import lol.sylvie.bedframe.util.JsonHelper; import lol.sylvie.bedframe.util.ResourceHelper; -import net.fabricmc.loader.api.ModContainer; import net.kyori.adventure.key.Key; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.block.DoorBlock; import net.minecraft.command.argument.BlockArgumentParser; import net.minecraft.registry.Registries; import net.minecraft.state.property.BooleanProperty; @@ -28,7 +22,6 @@ import net.minecraft.util.Pair; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; -import net.minecraft.util.math.Vec3d; import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.EmptyBlockView; import org.geysermc.geyser.api.block.custom.CustomBlockData; @@ -42,16 +35,10 @@ import org.geysermc.geyser.api.util.CreativeCategory; import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity; import org.geysermc.pack.converter.converter.model.ModelStitcher; -import org.geysermc.pack.converter.util.DefaultLogListener; -import org.geysermc.pack.converter.util.LogListener; -import org.intellij.lang.annotations.Subst; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import team.unnamed.creative.model.Model; import team.unnamed.creative.model.ModelTexture; import team.unnamed.creative.model.ModelTextures; -import team.unnamed.creative.serialize.minecraft.font.FontSerializer; import team.unnamed.creative.serialize.minecraft.model.ModelSerializer; import xyz.nucleoid.packettweaker.PacketContext; @@ -60,7 +47,6 @@ import java.util.function.BiConsumer; import java.util.stream.Stream; -import static lol.sylvie.bedframe.util.BedframeConstants.GSON; import static lol.sylvie.bedframe.util.BedframeConstants.LOGGER; import static lol.sylvie.bedframe.util.PathHelper.createDirectoryOrThrow; @@ -252,7 +238,6 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { continue; } - for (Pair face : faceMap) { String javaFaceName = face.getLeft(); String bedrockFaceName = face.getRight(); @@ -284,6 +269,13 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { } // Textures/materials + if (materials.isEmpty()) { + LOGGER.error("Couldn't generate materials for blockstate {}", state); + continue; + } + + // Particles + materials.put("*", materials.values().iterator().next()); for (Map.Entry entry : materials.entrySet()) { ModelTexture texture = entry.getValue(); String textureName = texture.key().asString(); From 4b99e791fe096148e87bc8e41ea3ae1264c2e3c0 Mon Sep 17 00:00:00 2001 From: sylvxa Date: Sun, 8 Jun 2025 19:31:48 -0500 Subject: [PATCH 6/9] bugfixes --- .../geyser/translator/BlockTranslator.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index 5609bf2..ffff348 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -152,7 +152,9 @@ private BoxComponent voxelShapeToBoxComponent(VoxelShape shape) { private Model resolveModel(Identifier identifier) { // This is unstable (https://unnamed.team/docs/creative/latest/serialization/minecraft) try { - JsonObject model = ResourceHelper.readJsonResource(identifier.getNamespace(), "models/" + identifier.getPath() + ".json"); + String modelPath = identifier.getPath(); + if (!(modelPath.startsWith("item/") || modelPath.startsWith("block/"))) modelPath = "block/" + modelPath; + JsonObject model = ResourceHelper.readJsonResource(identifier.getNamespace(), "models/" + modelPath + ".json"); return ModelSerializer.INSTANCE.deserializeFromJson(model, Key.key(identifier.toString())); } catch (RuntimeException e) { LOGGER.warn("Couldn't resolve model {}", identifier); @@ -198,13 +200,14 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { BlockState polymerBlockState = block.getPolymerBlockState(state, PacketContext.get()); BlockResourceCreator creator = PolymerBlockResourceUtilsAccessor.getCREATOR(); PolymerBlockModel[] polymerBlockModels = ((BlockResourceCreatorAccessor) (Object) creator).getModels().get(polymerBlockState); - PolymerBlockModel modelEntry = polymerBlockModels[0]; // TODO: java selects one by weight, does bedrock support this? - if (modelEntry == null) { + if (polymerBlockModels == null || polymerBlockModels.length == 0) { LOGGER.warn("No model specified for blockstate {}", state); continue; } + PolymerBlockModel modelEntry = polymerBlockModels[0]; // TODO: java selects one by weight, does bedrock support this? + // Rotation TransformationComponent rotationComponent = new TransformationComponent((360 - modelEntry.x()) % 360, (360 - modelEntry.y()) % 360, 0); stateComponentBuilder.transformation(rotationComponent); @@ -220,7 +223,7 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { HashMap materials = new HashMap<>(); Key modelParentKey = blockModel.parent(); - if (modelParentKey != null) { + if (modelParentKey != null && parentFaceMap.containsKey(modelParentKey.value())) { // Vanilla parent boolean cross = modelParentKey.toString().equals("minecraft:block/cross"); String geometryIdentifier = cross ? "minecraft:geometry.cross" : "minecraft:geometry.full_block"; @@ -233,16 +236,11 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { ModelTextures textures = blockModel.textures(); Map textureMap = textures.variables(); List> faceMap = parentFaceMap.get(modelParentKey.value()); - if (faceMap == null) { - LOGGER.error("No texture map found for parent {} of blockstate {}", modelParentKey, state); - continue; - } for (Pair face : faceMap) { String javaFaceName = face.getLeft(); String bedrockFaceName = face.getRight(); if (!textureMap.containsKey(javaFaceName)) continue; - materials.put(bedrockFaceName, textureMap.get(javaFaceName)); } } else { @@ -275,9 +273,20 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { } // Particles - materials.put("*", materials.values().iterator().next()); + if (!materials.containsKey("*")) + materials.put("*", materials.values().iterator().next()); for (Map.Entry entry : materials.entrySet()) { ModelTexture texture = entry.getValue(); + + //String reference = texture.reference(); + //if (reference != null && materials.containsKey(reference)) + // texture = materials.get(reference); + + if (texture.key() == null) { + LOGGER.warn("Texture for block {} on side {} is missing", identifier, entry.getKey()); + continue; + } + String textureName = texture.key().asString(); Identifier textureIdentifier = Identifier.of(textureName); From 185ae711dfb5e10364a6769969bcc63a9a072212 Mon Sep 17 00:00:00 2001 From: sylvxa Date: Sun, 8 Jun 2025 22:25:21 -0500 Subject: [PATCH 7/9] sound, resolve texture references, JavaBlockState --- .../sylvie/bedframe/BedframeInitializer.java | 8 ++ .../geyser/model/JavaGeometryConverter.java | 56 ++++---- .../geyser/translator/BlockTranslator.java | 125 +++++++++++++++--- .../mixin/PolymerBlockUtilsMixin.java | 23 ++++ .../sylvie/bedframe/util/ResourceHelper.java | 9 ++ src/main/resources/bedframe.mixins.json | 1 + 6 files changed, 176 insertions(+), 46 deletions(-) create mode 100644 src/main/java/lol/sylvie/bedframe/mixin/PolymerBlockUtilsMixin.java diff --git a/src/main/java/lol/sylvie/bedframe/BedframeInitializer.java b/src/main/java/lol/sylvie/bedframe/BedframeInitializer.java index 2d75394..ab5cdae 100644 --- a/src/main/java/lol/sylvie/bedframe/BedframeInitializer.java +++ b/src/main/java/lol/sylvie/bedframe/BedframeInitializer.java @@ -1,12 +1,16 @@ package lol.sylvie.bedframe; +import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; +import eu.pb4.polymer.resourcepack.api.ResourcePackBuilder; import lol.sylvie.bedframe.geyser.TranslationManager; +import lol.sylvie.bedframe.util.ResourceHelper; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.metadata.Person; import java.nio.file.Path; +import java.util.function.Consumer; import static lol.sylvie.bedframe.util.BedframeConstants.*; @@ -22,5 +26,9 @@ public void onInitialize() { TranslationManager manager = new TranslationManager(); manager.registerHooks(); }); + + PolymerResourcePackUtils.RESOURCE_PACK_AFTER_INITIAL_CREATION_EVENT.register(resourcePackBuilder -> { + ResourceHelper.PACK_BUILDER = resourcePackBuilder; + }); } } \ No newline at end of file diff --git a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java index 25d904b..a860b31 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java @@ -18,10 +18,7 @@ import team.unnamed.creative.model.Model; import team.unnamed.creative.texture.TextureUV; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; import static lol.sylvie.bedframe.util.BedframeConstants.LOGGER; @@ -136,38 +133,39 @@ public static Pair convert(Model model) { // UV Uv uv = new Uv(); - if (!element.faces().isEmpty()) { - for (Map.Entry faceEntry : element.faces().entrySet()) { - CubeFace direction = faceEntry.getKey(); - ElementFace face = faceEntry.getValue(); - - TextureUV textureUV = face.uv0(); - if (textureUV == null) - textureUV = TextureUV.uv(0, 0, 1, 1); - else textureUV = TextureUV.uv(textureUV.from().multiply(16f), textureUV.to().multiply(16f)); - - float[] uvValue; - float[] uvSize; - if (direction.axis() == Axis3D.Y) { - uvValue = new float[] { textureUV.to().x(), textureUV.to().y() }; - uvSize = new float[] { (textureUV.from().x() - uvValue[0]), (textureUV.from().y() - uvValue[1]) }; - } else { - uvValue = new float[] { textureUV.from().x(), textureUV.from().y() }; - uvSize = new float[] { (textureUV.to().x() - uvValue[0]), (textureUV.to().y() - uvValue[1]) }; - } - - applyFaceUv(uv, direction, uvValue, uvSize, face.texture().replace("#", "")); - } - } else { + Map faceMap = element.faces(); + if (faceMap.isEmpty()) { + faceMap = new HashMap<>(); for (CubeFace face : CubeFace.values()) { - applyFaceUv(uv, face, new float[] { 0, 0 }, new float[] { 16, 16 }, face.name()); + faceMap.put(face, ElementFace.face().texture(face.name()).build()); } } + for (Map.Entry faceEntry : faceMap.entrySet()) { + CubeFace direction = faceEntry.getKey(); + ElementFace face = faceEntry.getValue(); + + TextureUV textureUV = face.uv0(); + if (textureUV == null) + textureUV = TextureUV.uv(0, 0, 16f, 16f); + else textureUV = TextureUV.uv(textureUV.from().multiply(16f), textureUV.to().multiply(16f)); + + float[] uvValue; + float[] uvSize; + if (direction.axis() == Axis3D.Y) { + uvValue = new float[] { textureUV.to().x(), textureUV.to().y() }; + uvSize = new float[] { (textureUV.from().x() - uvValue[0]), (textureUV.from().y() - uvValue[1]) }; + } else { + uvValue = new float[] { textureUV.from().x(), textureUV.from().y() }; + uvSize = new float[] { (textureUV.to().x() - uvValue[0]), (textureUV.to().y() - uvValue[1]) }; + } + + applyFaceUv(uv, direction, uvValue, uvSize, face.texture().replace("#", "")); + } + cube.uv(uv); bone.cubes(List.of(cube)); bone.textureMeshes(null); - bones.add(bone); nthElement++; diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index ffff348..337535d 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -4,6 +4,7 @@ import eu.pb4.polymer.blocks.api.BlockResourceCreator; import eu.pb4.polymer.blocks.api.PolymerBlockModel; import eu.pb4.polymer.blocks.api.PolymerTexturedBlock; +import eu.pb4.polymer.core.api.block.PolymerBlock; import lol.sylvie.bedframe.geyser.Translator; import lol.sylvie.bedframe.geyser.model.JavaGeometryConverter; import lol.sylvie.bedframe.mixin.BlockResourceCreatorAccessor; @@ -12,11 +13,14 @@ import net.kyori.adventure.key.Key; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.piston.PistonBehavior; import net.minecraft.command.argument.BlockArgumentParser; import net.minecraft.registry.Registries; +import net.minecraft.sound.BlockSoundGroup; import net.minecraft.state.property.BooleanProperty; import net.minecraft.state.property.EnumProperty; import net.minecraft.state.property.IntProperty; +import net.minecraft.state.property.Properties; import net.minecraft.state.property.Property; import net.minecraft.util.Identifier; import net.minecraft.util.Pair; @@ -29,10 +33,13 @@ import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.api.block.custom.NonVanillaCustomBlockData; import org.geysermc.geyser.api.block.custom.component.*; +import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState; +import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBoundingBox; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent; import org.geysermc.geyser.api.util.CreativeCategory; +import org.geysermc.geyser.util.SoundUtils; import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity; import org.geysermc.pack.converter.converter.model.ModelStitcher; import org.joml.Vector3f; @@ -54,7 +61,6 @@ public class BlockTranslator extends Translator { // Maps parent models to a map containing the translations between Java sides and Bedrock sides private static final Map>> parentFaceMap = Map.of( "block/cube_all", List.of( - new Pair<>("particle", "*"), new Pair<>("all", "*") ), "block/cross", List.of( @@ -78,7 +84,7 @@ public class BlockTranslator extends Translator { new Pair<>("side", "south"), new Pair<>("side", "east"), new Pair<>("side", "west") - ), + )/*, "block/cube_column_horizontal", List.of( new Pair<>("side", "*"), new Pair<>("end", "up"), @@ -93,9 +99,10 @@ public class BlockTranslator extends Translator { new Pair<>("front", "north"), new Pair<>("top", "up"), new Pair<>("bottom", "down") - ) + )*/ ); + private static final ArrayList registeredBlocks = new ArrayList<>(); private final HashMap blocks = new HashMap<>(); public BlockTranslator() { @@ -162,6 +169,10 @@ private Model resolveModel(Identifier identifier) { } } + public static boolean isRegisteredBlock(PolymerBlock block) { + return registeredBlocks.contains(block); + } + // Referenced https://github.com/GeyserMC/Hydraulic/blob/master/shared/src/main/java/org/geysermc/hydraulic/block/BlockPackModule.java#L54 public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { Path textureDir = createDirectoryOrThrow(packRoot.resolve("textures")); @@ -174,6 +185,14 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { terrainTextureObject.addProperty("resource_pack_name", "Bedframe"); terrainTextureObject.addProperty("texture_name", "atlas.terrain"); + JsonObject blocksJson = new JsonObject(); + blocksJson.addProperty("format_version", "1.21.40"); + + JsonObject soundsJson = new JsonObject(); + JsonObject blockSoundsObject = new JsonObject(); + JsonObject interactiveSoundsObject = new JsonObject(); + + JsonObject interactiveSoundsWrapper = new JsonObject(); JsonObject textureDataObject = new JsonObject(); forEachBlock((identifier, block) -> { @@ -273,14 +292,20 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { } // Particles - if (!materials.containsKey("*")) - materials.put("*", materials.values().iterator().next()); + ModelTextures textures = blockModel.textures(); + materials.put("*", textures.particle() == null ? materials.values().iterator().next() : textures.particle()); + for (Map.Entry entry : materials.entrySet()) { ModelTexture texture = entry.getValue(); - //String reference = texture.reference(); - //if (reference != null && materials.containsKey(reference)) - // texture = materials.get(reference); + while (texture.key() == null) { + String reference = texture.reference(); + if (reference == null || !materials.containsKey(reference)) { + break; + } + + texture = materials.get(reference); + } if (texture.key() == null) { LOGGER.warn("Texture for block {} on side {} is missing", identifier, entry.getKey()); @@ -288,14 +313,18 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { } String textureName = texture.key().asString(); - Identifier textureIdentifier = Identifier.of(textureName); + if (!textureDataObject.has(textureName)) { + Identifier textureIdentifier = Identifier.of(textureName); - String texturePath = "textures/" + textureIdentifier.getPath(); - String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); + String texturePath = "textures/" + textureIdentifier.getPath(); + String bedrockPath = ResourceHelper.javaToBedrockTexture(texturePath); - JsonObject thisTexture = new JsonObject(); - thisTexture.addProperty("textures", bedrockPath); - textureDataObject.add(textureName, thisTexture); + JsonObject thisTexture = new JsonObject(); + thisTexture.addProperty("textures", bedrockPath); + textureDataObject.add(textureName, thisTexture); + + ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); + } stateComponentBuilder.materialInstance(entry.getKey(), MaterialInstance.builder() .renderMethod(renderMethod) @@ -303,8 +332,6 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { .faceDimming(true) .ambientOcclusion(blockModel.ambientOcclusion()) .build()); - - ResourceHelper.copyResource(textureIdentifier.getNamespace(), texturePath + ".png", packRoot.resolve(bedrockPath + ".png")); } // Collision @@ -335,10 +362,47 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { String stateCondition = String.join(" && ", conditions); permutations.add(new CustomBlockPermutation(stateComponents, stateCondition)); } + builder.permutations(permutations); + // Sounds + // blocks.json + String blockAsString = identifier.toString(); + JsonObject thisBlockObject = new JsonObject(); + thisBlockObject.addProperty("sound", blockAsString); + blocksJson.add(blockAsString, thisBlockObject); + + // sounds.json + BlockSoundGroup soundGroup = realBlock.getDefaultState().getSoundGroup(); + // base sounds (break, hit, place) + JsonObject baseSoundObject = new JsonObject(); + baseSoundObject.addProperty("pitch", soundGroup.getPitch()); + baseSoundObject.addProperty("volume", soundGroup.getVolume()); + + JsonObject soundEventsObject = new JsonObject(); + soundEventsObject.addProperty("break", SoundUtils.translatePlaySound(soundGroup.getBreakSound().id().toString())); + soundEventsObject.addProperty("hit", SoundUtils.translatePlaySound(soundGroup.getHitSound().id().toString())); + soundEventsObject.addProperty("place", SoundUtils.translatePlaySound(soundGroup.getPlaceSound().id().toString())); + baseSoundObject.add("events", soundEventsObject); + + blockSoundsObject.add(blockAsString, baseSoundObject); + // interactive sounds + JsonObject interactiveSoundObject = new JsonObject(); + interactiveSoundObject.addProperty("pitch", soundGroup.getPitch()); + interactiveSoundObject.addProperty("volume", soundGroup.getVolume()); + + JsonObject interactiveEventsObject = new JsonObject(); + interactiveEventsObject.addProperty("fall", SoundUtils.translatePlaySound(soundGroup.getFallSound().id().toString())); + interactiveEventsObject.addProperty("jump", SoundUtils.translatePlaySound(soundGroup.getStepSound().id().toString())); + interactiveEventsObject.addProperty("step", SoundUtils.translatePlaySound(soundGroup.getStepSound().id().toString())); + interactiveEventsObject.addProperty("land", SoundUtils.translatePlaySound(soundGroup.getFallSound().id().toString())); + interactiveSoundObject.add("events", interactiveEventsObject); + interactiveSoundsObject.add(blockAsString, interactiveSoundObject); + + // Registration NonVanillaCustomBlockData data = builder.build(); event.register(data); + registeredBlocks.add(block); // Registering the block states for (BlockState state : realBlock.getStateManager().getStates()) { @@ -358,12 +422,39 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { } CustomBlockState customBlockState = stateBuilder.build(); - event.registerOverride(BlockArgumentParser.stringifyBlockState(block.getPolymerBlockState(state, PacketContext.get())), customBlockState); + JavaBlockState.Builder javaBlockState = JavaBlockState.builder(); + javaBlockState.blockHardness(state.getHardness(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)); + + VoxelShape shape = state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + if (shape.isEmpty()) { + javaBlockState.collision(new JavaBoundingBox[0]); + } else { + Box box = shape.getBoundingBox(); + javaBlockState.collision(new JavaBoundingBox[]{ + new JavaBoundingBox(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ) + }); + } + + javaBlockState.javaId(Block.getRawIdFromState(state)); + javaBlockState.identifier(BlockArgumentParser.stringifyBlockState(state)); + javaBlockState.waterlogged(state.get(Properties.WATERLOGGED, false)); + if (realBlock.asItem() != null) javaBlockState.pickItem(Registries.ITEM.getId(realBlock.asItem()).toString()); + javaBlockState.canBreakWithHand(state.isToolRequired()); + + PistonBehavior pistonBehavior = state.getPistonBehavior(); + javaBlockState.pistonBehavior(pistonBehavior == PistonBehavior.IGNORE ? "NORMAL" : pistonBehavior.name()); + + event.registerOverride(javaBlockState.build(), customBlockState); } }); terrainTextureObject.add("texture_data", textureDataObject); + soundsJson.add("block_sounds", blockSoundsObject); + interactiveSoundsWrapper.add("block_sounds", interactiveSoundsObject); + soundsJson.add("interactive_sounds", interactiveSoundsWrapper); writeJsonToFile(terrainTextureObject, textureDir.resolve("terrain_texture.json").toFile()); + writeJsonToFile(blocksJson, packRoot.resolve("blocks.json").toFile()); + writeJsonToFile(soundsJson, packRoot.resolve("sounds.json").toFile()); markResourcesProvided(); } diff --git a/src/main/java/lol/sylvie/bedframe/mixin/PolymerBlockUtilsMixin.java b/src/main/java/lol/sylvie/bedframe/mixin/PolymerBlockUtilsMixin.java new file mode 100644 index 0000000..808016e --- /dev/null +++ b/src/main/java/lol/sylvie/bedframe/mixin/PolymerBlockUtilsMixin.java @@ -0,0 +1,23 @@ +package lol.sylvie.bedframe.mixin; + +import eu.pb4.polymer.core.api.block.PolymerBlock; +import eu.pb4.polymer.core.api.block.PolymerBlockUtils; +import lol.sylvie.bedframe.geyser.translator.BlockTranslator; +import lol.sylvie.bedframe.util.GeyserHelper; +import net.minecraft.block.BlockState; +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 xyz.nucleoid.packettweaker.PacketContext; + +@Mixin(PolymerBlockUtils.class) +public class PolymerBlockUtilsMixin { + // This mixin tells Polymer to send Bedrock clients + // the actual blocks rather than their Polymer representations + @Inject(method = "getBlockStateSafely(Leu/pb4/polymer/core/api/block/PolymerBlock;Lnet/minecraft/block/BlockState;ILxyz/nucleoid/packettweaker/PacketContext;)Lnet/minecraft/block/BlockState;", at = @At("RETURN"), cancellable = true) + private static void bedframe$tellPolymerToAbstain(PolymerBlock block, BlockState blockState, int maxDistance, PacketContext context, CallbackInfoReturnable cir) { + if (GeyserHelper.isBedrockPlayer(context.getPlayer()) && BlockTranslator.isRegisteredBlock(block)) + cir.setReturnValue(blockState); + } +} diff --git a/src/main/java/lol/sylvie/bedframe/util/ResourceHelper.java b/src/main/java/lol/sylvie/bedframe/util/ResourceHelper.java index a876ad2..9f5d8a1 100644 --- a/src/main/java/lol/sylvie/bedframe/util/ResourceHelper.java +++ b/src/main/java/lol/sylvie/bedframe/util/ResourceHelper.java @@ -1,8 +1,10 @@ package lol.sylvie.bedframe.util; import com.google.gson.JsonObject; +import eu.pb4.polymer.resourcepack.api.ResourcePackBuilder; import net.minecraft.util.Identifier; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -10,7 +12,14 @@ import java.nio.file.Path; public class ResourceHelper { + public static ResourcePackBuilder PACK_BUILDER = null; + public static InputStream getResource(String path) { + if (PACK_BUILDER != null) { + byte[] data = PACK_BUILDER.getData(path); + if (data != null) return new ByteArrayInputStream(data); + } + return Thread.currentThread().getContextClassLoader().getResourceAsStream(path); } diff --git a/src/main/resources/bedframe.mixins.json b/src/main/resources/bedframe.mixins.json index 18f4e6f..415a367 100644 --- a/src/main/resources/bedframe.mixins.json +++ b/src/main/resources/bedframe.mixins.json @@ -6,6 +6,7 @@ "BlockResourceCreatorAccessor", "CustomItemRegistryPopulatorMixin", "PolymerBlockResourceUtilsAccessor", + "PolymerBlockUtilsMixin", "PolymerItemMixin", "PolymerItemUtilsMixin" ], From df1408a15b579d2ad69b04213ad962f3e3e43eae Mon Sep 17 00:00:00 2001 From: sylvxa Date: Tue, 17 Jun 2025 13:54:00 -0500 Subject: [PATCH 8/9] hackily fix dependency errors --- build.gradle | 5 ++++- .../bedframe/geyser/model/JavaGeometryConverter.java | 1 - .../bedframe/geyser/translator/BlockTranslator.java | 11 +++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 57cf4c1..dee1fa2 100644 --- a/build.gradle +++ b/build.gradle @@ -81,7 +81,10 @@ dependencies { compileOnly("org.geysermc.geyser:api:${project.geyser_version}") compileOnly("org.geysermc.geyser:core:${project.geyser_version}") - api("org.geysermc.pack:converter:${project.pack_converter_version}") + include api("org.geysermc.pack:converter:${project.pack_converter_version}") + include api("org.geysermc.pack:pack-schema-api:${project.pack_converter_version}") + include api("team.unnamed:creative-api:1.8.2-SNAPSHOT") + include api("team.unnamed:creative-serializer-minecraft:1.8.2-SNAPSHOT") } processResources { diff --git a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java index a860b31..7adb499 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java @@ -7,7 +7,6 @@ import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Bones; import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Description; import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.Cubes; -import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.TextureMeshes; import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.Uv; import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.*; import team.unnamed.creative.base.Axis3D; diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index 337535d..8b9e1c1 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -215,6 +215,11 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { for (BlockState state : realBlock.getStateManager().getStates()) { CustomBlockComponents.Builder stateComponentBuilder = CustomBlockComponents.builder(); + // Hardness + float hardness = state.getHardness(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + LOGGER.info("{} -> {}", identifier, hardness); + stateComponentBuilder.destructibleByMining(hardness); + // Obtain model data from polymers internal api BlockState polymerBlockState = block.getPolymerBlockState(state, PacketContext.get()); BlockResourceCreator creator = PolymerBlockResourceUtilsAccessor.getCREATOR(); @@ -240,6 +245,7 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { continue; } + // Textures HashMap materials = new HashMap<>(); Key modelParentKey = blockModel.parent(); if (modelParentKey != null && parentFaceMap.containsKey(modelParentKey.value())) { @@ -251,7 +257,6 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { GeometryComponent geometryComponent = GeometryComponent.builder().identifier(geometryIdentifier).build(); stateComponentBuilder.geometry(geometryComponent); - // Textures ModelTextures textures = blockModel.textures(); Map textureMap = textures.variables(); List> faceMap = parentFaceMap.get(modelParentKey.value()); @@ -285,7 +290,6 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { stateComponentBuilder.geometry(geometryComponent); } - // Textures/materials if (materials.isEmpty()) { LOGGER.error("Couldn't generate materials for blockstate {}", state); continue; @@ -362,7 +366,6 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { String stateCondition = String.join(" && ", conditions); permutations.add(new CustomBlockPermutation(stateComponents, stateCondition)); } - builder.permutations(permutations); // Sounds @@ -389,7 +392,7 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { // interactive sounds JsonObject interactiveSoundObject = new JsonObject(); interactiveSoundObject.addProperty("pitch", soundGroup.getPitch()); - interactiveSoundObject.addProperty("volume", soundGroup.getVolume()); + interactiveSoundObject.addProperty("volume", soundGroup.getVolume() * .4); // The multiplier is arbitrary, its just too loud by default :( JsonObject interactiveEventsObject = new JsonObject(); interactiveEventsObject.addProperty("fall", SoundUtils.translatePlaySound(soundGroup.getFallSound().id().toString())); From c8b817c51f3b9beba641493f9e0b4f8d68f29d11 Mon Sep 17 00:00:00 2001 From: sylvxa Date: Wed, 2 Jul 2025 13:14:42 -0500 Subject: [PATCH 9/9] translation tweaks --- README.md | 27 ++++--- gradle.properties | 12 ++-- .../sylvie/bedframe/geyser/PackGenerator.java | 18 ++--- .../bedframe/geyser/TranslationManager.java | 1 + .../geyser/model/JavaGeometryConverter.java | 12 +++- .../geyser/translator/BlockTranslator.java | 71 +++++++++++++++---- .../geyser/translator/ItemTranslator.java | 2 + .../CustomBlockRegistryPopulatorMixin.java | 28 ++++++++ .../bedframe/util/TranslationHelper.java | 4 +- src/main/resources/bedframe.mixins.json | 1 + src/main/resources/fabric.mod.json | 2 +- .../data/bedframe-testmod/lang/en_us.json | 3 +- 12 files changed, 131 insertions(+), 50 deletions(-) create mode 100644 src/main/java/lol/sylvie/bedframe/mixin/CustomBlockRegistryPopulatorMixin.java diff --git a/README.md b/README.md index 6231d99..2d9c3b9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # Bedframe -## Premise +> [!WARNING] +> Bedframe is in early development, and you should *expect* problems. Batteries not included. See store for details. Translation layer that converts textured/custom server-side Polymer blocks and items to native Bedrock representations. +> [!NOTE] +> If you are looking for general modding support on bedrock, see if [Hydraulic](https://github.com/GeyserMC/Hydraulic) works for you. Their conversion code is better than mine. Do note that Hydraulic *does not* support Polymer mods at the moment, and I haven't tested if both mods work at the same time. + ## Requirements - [Geyser-Fabric](https://geysermc.org/download) @@ -13,22 +17,17 @@ You also probably want Polymer's Auto-Host feature, but Bedframe doesn't require ## Compatibility -- Works perfectly with [More Furnaces (Polymer)](https://modrinth.com/mod/morefurnaces)* -- Works perfectly with [Televator](https://modrinth.com/mod/televator) -- Will probably work with most simple Polymer Resource Pack mods - -*technically there is an item display for upgrades, but its hardly noticeable +Polymer mods that use display entities are not supported at the moment. (Geyser simply doesn't support it). So far I've tested the following: -## Limitations & Goals +- [Tom's Server Additions](https://modrinth.com/mods?q=Tom%27s+Server+Additions) (EXCLUDING Decorations and Furniture) +- [More Furnaces](https://modrinth.com/mod/morefurnaces) +- [Televator](https://modrinth.com/mod/televator) +- [Navigation Compasses](https://modrinth.com/mod/navigation-compasses) (might be under review) -- Custom models (a.k.a non-full-block or cross-block) do not work -- Entities are not touched -- Mods that use block displays don't work +Bedframe does not touch non-textured blocks (so mods like [Server-Side Waystones](https://modrinth.com/mod/sswaystones) will work as expected) -### Mods -- [Borukva-Food](https://github.com/MykhailoOpryshok/Borukva-Food/tree/master) is missing many models/textures (mostly crops and 3D tools) -- [Tom's Server Additions](https://modrinth.com/mods?q=Tom%27s+Server+Additions) is quite *iffy* +Feel free to [make an issue](https://github.com/sylvxa/bedframe/issues/new) for incompatible mods. ## License -This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own project \ No newline at end of file +This mod is available under the CC0 license. Feel free to learn from it and incorporate it in your own project \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index fc2a42c..c2aba36 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,8 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.5 -yarn_mappings=1.21.5+build.1 +minecraft_version=1.21.6 +yarn_mappings=1.21.6+build.1 loader_version=0.16.14 loom_version=1.10-SNAPSHOT @@ -15,16 +15,16 @@ maven_group=lol.sylvie archives_base_name=bedframe # Dependencies -fabric_version=0.124.2+1.21.5 +fabric_version=0.127.0+1.21.6 # Find updates at https://maven.nucleoid.xyz/eu/pb4/polymer-core/ -polymer_version=0.12.6+1.21.5 +polymer_version=0.13.3+1.21.6 # Find updates at https://maven.nucleoid.xyz/xyz/nucleoid/server-translations-api/ -server_translations_version=2.5.0+1.21.5-rc1 +server_translations_version=2.5.1+1.21.5 # Find updates at https://repo.opencollab.dev/#/maven-snapshots/org/geysermc/geyser/api -geyser_version=2.7.1-SNAPSHOT +geyser_version=2.8.0-SNAPSHOT # Find updates at https://repo.opencollab.dev/#/maven-snapshots/org/geysermc/pack/converter pack_converter_version=3.3.2-SNAPSHOT \ No newline at end of file diff --git a/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java b/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java index 965b139..8cdb35b 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/PackGenerator.java @@ -3,15 +3,16 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import lol.sylvie.bedframe.util.BedframeConstants; -import lol.sylvie.bedframe.util.PathHelper; -import lol.sylvie.bedframe.util.ResourceHelper; -import lol.sylvie.bedframe.util.ZipHelper; +import lol.sylvie.bedframe.util.*; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.Version; +import net.minecraft.text.Text; +import net.minecraft.util.Pair; import org.geysermc.pack.converter.util.NioDirectoryFileTreeReader; import team.unnamed.creative.ResourcePack; import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackReader; +import xyz.nucleoid.server.translations.api.language.TranslationAccess; +import xyz.nucleoid.server.translations.impl.ServerTranslations; import java.io.File; import java.io.FileWriter; @@ -99,24 +100,23 @@ public void generatePack(Path packPath, File outputFile, List transl PathHelper.createDirectoryOrThrow(textsDir); // TODO: I'm not sure if translations are even necessary - /*JsonArray languages = new JsonArray(); + JsonArray languages = new JsonArray(); ArrayList> allKeys = new ArrayList<>(); translators.forEach(t -> allKeys.addAll(t.getTranslations())); TranslationHelper.LANGUAGES.forEach((code) -> { - TranslationAccess access = ServerTranslations.INSTANCE.getLanguage(code).serverTranslations(); try (FileWriter writer = new FileWriter(textsDir.resolve(code + ".lang").toFile())) { for (Pair keyPair : allKeys) { - writer.write(keyPair.getLeft() + "=" + access.get(keyPair.getRight()) + "\n"); + writer.write(keyPair.getLeft() + "=" + Text.translatable(keyPair.getRight()).getString() + "\n"); } } catch (IOException e) { - bedframe.getLogger().error("Couldn't write language file"); + BedframeConstants.LOGGER.error("Couldn't write language file"); } languages.add(code); }); - writeJsonToFile(languages, textsDir.resolve("languages.json").toFile());*/ + writeJsonToFile(languages, textsDir.resolve("languages.json").toFile()); Optional icon = METADATA.getIconPath(512); Files.copy(ResourceHelper.getResource(icon.orElseThrow()), packPath.resolve("pack_icon.png")); diff --git a/src/main/java/lol/sylvie/bedframe/geyser/TranslationManager.java b/src/main/java/lol/sylvie/bedframe/geyser/TranslationManager.java index 9897c9c..609b05d 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/TranslationManager.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/TranslationManager.java @@ -59,6 +59,7 @@ public void registerHooks() { } event.register(ResourcePack.create(PackCodec.path(resourcePack))); + BedframeConstants.LOGGER.info("Registered resource pack"); } catch (IOException e) { BedframeConstants.LOGGER.error("Couldn't generate resource pack", e); } diff --git a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java index 7adb499..2cc5dd2 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/model/JavaGeometryConverter.java @@ -107,7 +107,10 @@ public static Pair convert(Model model) { // Transform Cubes cube = new Cubes(); cube.origin(javaPosToBedrock(javaFrom)); - cube.size(new float[] { javaTo[0] - javaFrom[0], javaTo[1] - javaFrom[1], javaTo[2] - javaFrom[2] }); + cube.size(new float[] { + javaTo[0] - javaFrom[0], + javaTo[1] - javaFrom[1], + javaTo[2] - javaFrom[2] }); cube.inflate(0f); // Rotation @@ -117,7 +120,7 @@ public static Pair convert(Model model) { bone.pivot(javaPosToBedrock(rotOrigin)); // We are given an angle and an axis, and we need to provide a vector - float rotValue = rotation.angle(); + float rotValue = (360 - rotation.angle()) % 360; Axis3D axis = rotation.axis(); float[] rotArray = new float[] { axis == Axis3D.X ? rotValue : 0f, @@ -174,7 +177,10 @@ public static Pair convert(Model model) { modelEntity.geometry(List.of(geometry)); String namespace = model.key().namespace(); - String geometryName = String.format(GEOMETRY_FORMAT, (namespace.equals(Key.MINECRAFT_NAMESPACE) ? "" : namespace + ".") + model.key().value().replace(":", ".").replace("/", ".")); + String[] pathSplit = model.key().value().split("/"); + String path = pathSplit[pathSplit.length - 1]; + String geometryName = String.format(GEOMETRY_FORMAT, (namespace.equals(Key.MINECRAFT_NAMESPACE) ? "" : namespace + ".") + + path.replace(":", ".")); Description description = new Description(); description.identifier(geometryName); diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java index 8b9e1c1..3b40fdd 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/BlockTranslator.java @@ -26,6 +26,8 @@ import net.minecraft.util.Pair; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.EmptyBlockView; import org.geysermc.geyser.api.block.custom.CustomBlockData; @@ -39,6 +41,7 @@ import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent; import org.geysermc.geyser.api.util.CreativeCategory; +import org.geysermc.geyser.util.MathUtils; import org.geysermc.geyser.util.SoundUtils; import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity; import org.geysermc.pack.converter.converter.model.ModelStitcher; @@ -84,7 +87,7 @@ public class BlockTranslator extends Translator { new Pair<>("side", "south"), new Pair<>("side", "east"), new Pair<>("side", "west") - )/*, + ), "block/cube_column_horizontal", List.of( new Pair<>("side", "*"), new Pair<>("end", "up"), @@ -99,7 +102,7 @@ public class BlockTranslator extends Translator { new Pair<>("front", "north"), new Pair<>("top", "up"), new Pair<>("bottom", "down") - )*/ + ) ); private static final ArrayList registeredBlocks = new ArrayList<>(); @@ -141,19 +144,51 @@ private void populateProperties(CustomBlockData.Builder builder, Collection { Block realBlock = Registries.BLOCK.get(identifier); // Block names - addTranslationKey("tile." + identifier.toString() + ".name", realBlock.getTranslationKey()); + addTranslationKey("block." + identifier.getNamespace() + "." + identifier.getPath(), realBlock.getTranslationKey()); NonVanillaCustomBlockData.Builder builder = NonVanillaCustomBlockData.builder() .name(identifier.getPath()) @@ -217,7 +252,6 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { // Hardness float hardness = state.getHardness(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); - LOGGER.info("{} -> {}", identifier, hardness); stateComponentBuilder.destructibleByMining(hardness); // Obtain model data from polymers internal api @@ -297,7 +331,10 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { // Particles ModelTextures textures = blockModel.textures(); - materials.put("*", textures.particle() == null ? materials.values().iterator().next() : textures.particle()); + if (!materials.containsKey("*")) { + ModelTexture texture = textures.particle() == null ? materials.values().iterator().next() : textures.particle(); + materials.put("*", texture); + } for (Map.Entry entry : materials.entrySet()) { ModelTexture texture = entry.getValue(); @@ -339,8 +376,12 @@ public void handle(GeyserDefineCustomBlocksEvent event, Path packRoot) { } // Collision - stateComponentBuilder.collisionBox(voxelShapeToBoxComponent(state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); - stateComponentBuilder.selectionBox(voxelShapeToBoxComponent(state.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN))); + VoxelShape collisionBox = state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + stateComponentBuilder.collisionBox(voxelShapeToBoxComponent(collisionBox)); + + VoxelShape outlineBox = state.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN); + stateComponentBuilder.selectionBox(voxelShapeToBoxComponent(outlineBox)); + stateComponentBuilder.lightEmission(state.getLuminance()); CustomBlockComponents stateComponents = stateComponentBuilder.build(); diff --git a/src/main/java/lol/sylvie/bedframe/geyser/translator/ItemTranslator.java b/src/main/java/lol/sylvie/bedframe/geyser/translator/ItemTranslator.java index 6313bf2..4f99e39 100644 --- a/src/main/java/lol/sylvie/bedframe/geyser/translator/ItemTranslator.java +++ b/src/main/java/lol/sylvie/bedframe/geyser/translator/ItemTranslator.java @@ -2,6 +2,8 @@ import com.google.gson.JsonObject; import eu.pb4.polymer.core.api.item.PolymerItem; +import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; +import eu.pb4.polymer.resourcepack.api.ResourcePackCreator; import lol.sylvie.bedframe.geyser.Translator; import lol.sylvie.bedframe.util.BedframeConstants; import lol.sylvie.bedframe.util.ResourceHelper; diff --git a/src/main/java/lol/sylvie/bedframe/mixin/CustomBlockRegistryPopulatorMixin.java b/src/main/java/lol/sylvie/bedframe/mixin/CustomBlockRegistryPopulatorMixin.java new file mode 100644 index 0000000..b187e6e --- /dev/null +++ b/src/main/java/lol/sylvie/bedframe/mixin/CustomBlockRegistryPopulatorMixin.java @@ -0,0 +1,28 @@ +package lol.sylvie.bedframe.mixin; + +import com.llamalad7.mixinextras.sugar.Local; +import org.cloudburstmc.nbt.NbtMap; +import org.cloudburstmc.nbt.NbtMapBuilder; +import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; +import org.geysermc.geyser.api.block.custom.component.MaterialInstance; +import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +import java.util.Map; + +@Mixin(value = CustomBlockRegistryPopulator.class, remap = false) +public class CustomBlockRegistryPopulatorMixin { + @ModifyVariable(method = "convertComponents", at = @At(value = "STORE", ordinal = 0)) + private static NbtMapBuilder addParticleComponent(NbtMapBuilder value, @Local(argsOnly = true) CustomBlockComponents components) { + Map materials = components.materialInstances(); + if (!materials.containsKey("*")) return value; + + value.putCompound("minecraft:destruction_particles", NbtMap.builder() + .putString("texture", materials.get("*").texture()) + .build()); + + return value; + } +} diff --git a/src/main/java/lol/sylvie/bedframe/util/TranslationHelper.java b/src/main/java/lol/sylvie/bedframe/util/TranslationHelper.java index aecd56c..120a18a 100644 --- a/src/main/java/lol/sylvie/bedframe/util/TranslationHelper.java +++ b/src/main/java/lol/sylvie/bedframe/util/TranslationHelper.java @@ -7,12 +7,14 @@ public class TranslationHelper { public static ArrayList LANGUAGES = new ArrayList<>(); static { + /* for (ServerLanguageDefinition language : ServerLanguageDefinition.getAllLanguages()) { String code = language.code(); String[] sides = code.split("_"); if (sides.length == 2) { LANGUAGES.add(sides[0] + "_" + sides[1].toUpperCase()); } else LANGUAGES.add(code); - } + }*/ + LANGUAGES.add("en_US"); } } diff --git a/src/main/resources/bedframe.mixins.json b/src/main/resources/bedframe.mixins.json index 415a367..e18d717 100644 --- a/src/main/resources/bedframe.mixins.json +++ b/src/main/resources/bedframe.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_21", "mixins": [ "BlockResourceCreatorAccessor", + "CustomBlockRegistryPopulatorMixin", "CustomItemRegistryPopulatorMixin", "PolymerBlockResourceUtilsAccessor", "PolymerBlockUtilsMixin", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 568171f..c1f26d3 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -25,7 +25,7 @@ ], "depends": { "fabricloader": ">=0.16.14", - "minecraft": "~1.21.5", + "minecraft": ["1.21.6", "1.21.7"], "java": ">=21", "fabric-api": "*", "geyser-fabric": "*", diff --git a/src/testmod/resources/data/bedframe-testmod/lang/en_us.json b/src/testmod/resources/data/bedframe-testmod/lang/en_us.json index 6c2278a..0bb63d1 100644 --- a/src/testmod/resources/data/bedframe-testmod/lang/en_us.json +++ b/src/testmod/resources/data/bedframe-testmod/lang/en_us.json @@ -4,5 +4,6 @@ "item.bedframe-testmod.vanilla_textured": "Vanilla-Textured Item", "block.bedframe-testmod.example_block": "Example Block", "block.bedframe-testmod.example_log": "Example Log", - "block.bedframe-testmod.example_flower": "Example Flower" + "block.bedframe-testmod.example_flower": "Example Flower", + "block.bedframe-testmod.example_flower_pot": "Example Flower Pot" } \ No newline at end of file