diff --git a/src/test/java/net/modificationstation/sltest/datafixer/DataFixerListener.java b/src/test/java/net/modificationstation/sltest/datafixer/DataFixerListener.java index de17ef907..1409112d7 100644 --- a/src/test/java/net/modificationstation/sltest/datafixer/DataFixerListener.java +++ b/src/test/java/net/modificationstation/sltest/datafixer/DataFixerListener.java @@ -8,8 +8,7 @@ import java.lang.invoke.MethodHandles; -import static net.modificationstation.stationapi.api.vanillafix.datafixer.schema.StationFlatteningItemStackSchema.putItem; -import static net.modificationstation.stationapi.api.vanillafix.datafixer.schema.StationFlatteningItemStackSchema.putState; +import static net.modificationstation.stationapi.api.vanillafix.datafixer.schema.StationFlatteningItemStackSchema.*; public class DataFixerListener { static { @@ -23,6 +22,7 @@ private static void registerFixer(DataFixerRegisterEvent event) { putState(99, "sltest:farlands_block", Util.make(new NbtCompound(), tag -> tag.putString("facing", "north"))); putState(100, "sltest:freezer"); putState(101, "sltest:altar"); + putStateMetaRule(17, 2, "minecraft:wool", 10); putItem(360, "sltest:test_item"); putItem(361, "sltest:test_pickaxe"); putItem(362, "sltest:nbt_item"); diff --git a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datadamager/damage/StationFlatteningToMcRegionChunkDamage.java b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datadamager/damage/StationFlatteningToMcRegionChunkDamage.java index 21381f150..25411814b 100644 --- a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datadamager/damage/StationFlatteningToMcRegionChunkDamage.java +++ b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datadamager/damage/StationFlatteningToMcRegionChunkDamage.java @@ -23,6 +23,9 @@ import static net.modificationstation.stationapi.impl.world.FlattenedWorldManager.SECTIONS; +/** + * Reverses Station API's block conversion into identifiers and reverts blocks into numeric IDs + */ public class StationFlatteningToMcRegionChunkDamage extends DataFix { private final static int CHUNK_SIZE = 16 * 128 * 16; private final static byte[] DEFAULT_BLOCK_LIGHT = new byte[CHUNK_SIZE >> 1]; diff --git a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/fix/McRegionToStationFlatteningChunkFix.java b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/fix/McRegionToStationFlatteningChunkFix.java index 9600999e7..cddd124f4 100644 --- a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/fix/McRegionToStationFlatteningChunkFix.java +++ b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/fix/McRegionToStationFlatteningChunkFix.java @@ -15,6 +15,7 @@ import net.modificationstation.stationapi.api.util.collection.PackedIntegerArray; import net.modificationstation.stationapi.api.util.math.MathHelper; import net.modificationstation.stationapi.api.vanillafix.datafixer.schema.StationFlatteningItemStackSchema; +import net.modificationstation.stationapi.api.vanillafix.util.MetaDependentIdConversion; import java.nio.ByteBuffer; import java.util.Arrays; @@ -24,6 +25,11 @@ import static net.modificationstation.stationapi.impl.world.FlattenedWorldManager.SECTIONS; +/** + * Datafixer used for converting McRegion worlds (Vanilla Beta 1.7.3 world format) + *

+ * Handles block conversions + */ public class McRegionToStationFlatteningChunkFix extends DataFix { private final String name; @@ -75,7 +81,7 @@ public Level(Dynamic dynamic) { public Dynamic transform() { Dynamic self = this.level; - // create sections with blocks + // Create sections with blocks Section[] sections = new Section[8]; for (int i = 0; i < 32768; i++) { int worldY = i & 0b1111111; @@ -84,17 +90,24 @@ public Dynamic transform() { int z = i >> 7 & 0b1111; int x = i >> 11; int block = Byte.toUnsignedInt(blocks.get(i)); - int data = this.data.get(x, worldY, z); - if (block > 0 || data > 0) { + int metadata = this.data.get(x, worldY, z); + if (block > 0 || metadata > 0) { if (sections[sectionY] == null) sections[sectionY] = new Section(self.createMap(Map.of(self.createString("y"), self.createByte((byte) sectionY)))); Section section = sections[sectionY]; - section.setBlock(x, y, z, StationFlatteningItemStackSchema.lookupState(block)); // do not convert just yet. we need same references for faster key comparison - section.setData(x, y, z, data); + // Preparation for conversion. References are maintained for faster key comparison + // See "transform" method at the bottom of this file for actual conversion + section.setBlock(x, y, z, StationFlatteningItemStackSchema.lookupState(block, metadata)); + // Replace old metadata with new one if specified + int newMetadata = StationFlatteningItemStackSchema.lookupMetadata(block, metadata); + if (newMetadata == MetaDependentIdConversion.UNSPECIFIED_META) { + newMetadata = metadata; + } + section.setMetadata(x, y, z, newMetadata); } } - // add lighting in created sections + // Add lighting in created sections for (Section section : sections) { if (section != null) { int sectionY = section.y; @@ -110,13 +123,13 @@ public Dynamic transform() { } } - // expand height map - byte[] height_map = new byte[512]; - for (int i = 0; i < height_map.length >> 1; i++) height_map[i << 1] = this.height_map.get(i); + // Expand height map + byte[] heightMap = new byte[512]; + for (int i = 0; i < heightMap.length >> 1; i++) heightMap[i << 1] = this.height_map.get(i); return self .set(SECTIONS, self.createList(Arrays.stream(sections).filter(Objects::nonNull).map(Section::transform))) - .set("height_map", self.createByteList(ByteBuffer.wrap(height_map))) + .set("height_map", self.createByteList(ByteBuffer.wrap(heightMap))) .remove("BlockLight") .remove("Blocks") .remove("Data") @@ -155,7 +168,7 @@ static final class Section { public Section(Dynamic section) { this.section = section; y = section.get("y").asInt(0); - Dynamic air = StationFlatteningItemStackSchema.lookupState(0); // same applies + Dynamic air = StationFlatteningItemStackSchema.lookupState(0, 0); // same applies seenStates.add(air); paletteData.add(air); paletteMap.add(air); @@ -170,7 +183,7 @@ public void setBlock(int x, int y, int z, Dynamic state) { states[(y << 4 | z) << 4 | x] = addTo(paletteMap, state); } - public void setData(int x, int y, int z, int data) { + public void setMetadata(int x, int y, int z, int data) { this.data.setValue(x << 8 | y << 4 | z, data); } @@ -180,7 +193,8 @@ public void setBlockLight(int x, int y, int z, int blockLight) { public Dynamic transform() { Dynamic self = this.section; - Dynamic palette = self.createList(paletteData.stream().map(dynamic -> dynamic.convert(self.getOps()))); // instead, convert when used + // Convert previously prepared values + Dynamic palette = self.createList(paletteData.stream().map(dynamic -> dynamic.convert(self.getOps()))); PackedIntegerArray array = new PackedIntegerArray(Math.max(4, MathHelper.ceilLog2(paletteData.size())), states.length); for (int i = 0; i < states.length; i++) array.set(i, states[i]); Dynamic data = self.createLongList(Arrays.stream(array.getData())); diff --git a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/McRegionSchemaB1_7_3.java b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/McRegionSchemaB1_7_3.java index cbc1025a9..61bd21d09 100644 --- a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/McRegionSchemaB1_7_3.java +++ b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/McRegionSchemaB1_7_3.java @@ -9,6 +9,11 @@ import java.util.Map; import java.util.function.Supplier; +/** + * Defines conversion rules for different parts of the chunk data + *

+ * Includes registries for Vanilla entities and block entities + */ public class McRegionSchemaB1_7_3 extends Schema { public McRegionSchemaB1_7_3(int versionKey, Schema parent) { super(versionKey, parent); diff --git a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/StationFlatteningItemStackSchema.java b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/StationFlatteningItemStackSchema.java index d355aba37..b1770fbdf 100644 --- a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/StationFlatteningItemStackSchema.java +++ b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/datafixer/schema/StationFlatteningItemStackSchema.java @@ -7,8 +7,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.nbt.NbtCompound; import net.modificationstation.stationapi.api.datafixer.TypeReferences; -import net.modificationstation.stationapi.api.nbt.NbtOps; import net.modificationstation.stationapi.api.util.Util; +import net.modificationstation.stationapi.api.vanillafix.util.MetaDependentIdConversion; import java.util.Map; import java.util.function.Supplier; @@ -16,7 +16,7 @@ import static net.modificationstation.stationapi.impl.vanillafix.datafixer.VanillaDataFixerImpl.STATION_ID; public class StationFlatteningItemStackSchema extends Schema { - private static final Dynamic[] OLD_ID_TO_BLOCKSTATE = new Dynamic[256]; + private static final MetaDependentIdConversion[] OLD_ID_TO_BLOCKSTATE = new MetaDependentIdConversion[256]; private static final Object2IntOpenHashMap BLOCK_TO_OLD_ID = Util.make(new Object2IntOpenHashMap<>(256), map -> map.defaultReturnValue(0)); private static final String[] OLD_ID_TO_ITEM = new String[32000]; private static final Object2IntOpenHashMap ITEM_TO_OLD_ID = Util.make(new Object2IntOpenHashMap<>(512), map -> map.defaultReturnValue(0)); @@ -28,34 +28,94 @@ public static void putState(int oldId, String id, NbtCompound properties) { })); } + /** + * Assigns a numeric block ID to its new identifier + * @param oldId Numeric block ID to be converted + * @param id New identifier (including the namespace) + */ public static void putState(int oldId, String id) { putState(oldId, Util.make(new NbtCompound(), tag -> tag.putString("Name", id))); } + /** + * Assigns a numeric block ID to an NBT compound with the new identifier and block state rules + * @param oldId Numeric block ID to be converted + * @param tag Tag with the identifier and block state rules + */ public static void putState(int oldId, NbtCompound tag) { String id = tag.getString("Name"); BLOCK_TO_OLD_ID.put(id, oldId); - Dynamic dynamic = new Dynamic<>(NbtOps.INSTANCE, tag); - OLD_ID_TO_BLOCKSTATE[oldId] = dynamic; + OLD_ID_TO_BLOCKSTATE[oldId] = new MetaDependentIdConversion(tag); putItem(oldId, id); } - public static Dynamic lookupState(int stateId) { - Dynamic dynamic = null; + /** + * Creates a metadata specific conversion rule for a block + *

+ * Must be used after the putState method + * @param oldId Numeric block ID to add the rule to + * @param meta Old metadata + * @param id New identifier (including the namespace) + * @param outputMeta New metadata + */ + public static void putStateMetaRule(int oldId, int meta, String id, int outputMeta) { + putStateMetaRule(oldId, meta, Util.make(new NbtCompound(), tag -> tag.putString("Name", id)), outputMeta); + } + + /** + * Creates a metadata specific conversion rule for a block + *

+ * Must be used after the putState method + * @param oldId Numeric block ID to add the rule to + * @param meta Old metadata + * @param tag Tag with the identifier and block state rules + * @param outputMeta New metadata + */ + public static void putStateMetaRule(int oldId, int meta, NbtCompound tag, int outputMeta) { + MetaDependentIdConversion rule; + if (oldId >= 0 && oldId < OLD_ID_TO_BLOCKSTATE.length) { + rule = OLD_ID_TO_BLOCKSTATE[oldId]; + if (rule != null) { + rule.addMetaDependentTag(meta, tag, outputMeta); + OLD_ID_TO_BLOCKSTATE[oldId] = rule; + } + } + } + + /** + * Takes a numeric block ID and returns a block entry for the datafixer + * @param stateId Numeric ID of the block + * @param metadata Metadata of the block + * @return Dynamic which contains the new ID + */ + public static Dynamic lookupState(int stateId, int metadata) { + MetaDependentIdConversion rule = null; if (stateId >= 0 && stateId < OLD_ID_TO_BLOCKSTATE.length) { - dynamic = OLD_ID_TO_BLOCKSTATE[stateId]; + rule = OLD_ID_TO_BLOCKSTATE[stateId]; } - return dynamic == null ? OLD_ID_TO_BLOCKSTATE[0] : dynamic; + return rule == null ? OLD_ID_TO_BLOCKSTATE[0].getDefaultTag() : rule.getTagForMeta(metadata); } - public static String lookupBlockId(int id) { - if (id < 0 || id >= OLD_ID_TO_BLOCKSTATE.length) { - return "minecraft:air"; + /** + * Replaces an old metadata with a new one for the given block + * @param stateId Numeric ID of the block + * @param metadata Old metadata + * @return New metadata or -1 if not specified + */ + public static int lookupMetadata(int stateId, int metadata) { + MetaDependentIdConversion rule = null; + if (stateId >= 0 && stateId < OLD_ID_TO_BLOCKSTATE.length) { + rule = OLD_ID_TO_BLOCKSTATE[stateId]; } - Dynamic dynamic = OLD_ID_TO_BLOCKSTATE[id]; - return dynamic == null ? "minecraft:air" : dynamic.get("Name").asString(""); + return rule == null ? MetaDependentIdConversion.UNSPECIFIED_META : rule.getOutputMeta(metadata); } + /** + * Reversed direction converter which turns an identifier into a numeric ID + * @param dynamic Dynamic with an identifier inside + * @return numeric ID + * @param Type of the dynamic + */ public static int lookupOldBlockId(Dynamic dynamic) { return BLOCK_TO_OLD_ID.getInt(dynamic.get("Name").asString("")); } diff --git a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/util/MetaDependentIdConversion.java b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/util/MetaDependentIdConversion.java new file mode 100644 index 000000000..4a84345f5 --- /dev/null +++ b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/api/vanillafix/util/MetaDependentIdConversion.java @@ -0,0 +1,70 @@ +package net.modificationstation.stationapi.api.vanillafix.util; + +import com.mojang.serialization.Dynamic; +import lombok.Getter; +import net.minecraft.nbt.NbtCompound; +import net.modificationstation.stationapi.api.nbt.NbtOps; + +/** + * Provides advanced conversion rules which depend on metadata + *

+ * Contains a default tag for unspecified metadata rules + */ +public class MetaDependentIdConversion { + public static final int UNSPECIFIED_META = -1; + + @Getter + private final Dynamic defaultTag; + private final Dynamic[] metaDependentTags = new Dynamic[16]; + private final Integer[] outputMetas = new Integer[16]; + + /** + * @param defaultTag Tag to be used for unspecified metadata rules + */ + public MetaDependentIdConversion(NbtCompound defaultTag) { + this.defaultTag = toDynamic(defaultTag); + } + + /** + * Adds a meta rule + * @param meta Metadata of the rule + * @param metaDependentTag Rule to be added + * @param outputMeta New metadata of the converted block + */ + public void addMetaDependentTag(int meta, NbtCompound metaDependentTag, int outputMeta) { + if (meta < metaDependentTags.length) { + metaDependentTags[meta] = toDynamic(metaDependentTag); + outputMetas[meta] = outputMeta; + } + } + + /** + * Provides a conversion rule for a given metadata value + * @param meta Metadata to check the rule for + * @return Specific rule or default if unspecified + */ + public Dynamic getTagForMeta(int meta) { + Dynamic tag = metaDependentTags[meta]; + if (tag == null) { + return defaultTag; + } + return tag; + } + + /** + * Replaces an old metadata value with a new one + * @param meta Old metadata to be replaced + * @return New metadata or -1 if not specified + */ + public int getOutputMeta(int meta) { + Integer outputMeta = null; + if (meta < outputMetas.length) { + outputMeta = outputMetas[meta]; + } + return outputMeta == null ? UNSPECIFIED_META : outputMeta; + } + + private Dynamic toDynamic(NbtCompound tag) { + return new Dynamic<>(NbtOps.INSTANCE, tag); + } +} diff --git a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/datafixer/VanillaDataFixerImpl.java b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/datafixer/VanillaDataFixerImpl.java index 099e8e807..09bb408f3 100644 --- a/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/datafixer/VanillaDataFixerImpl.java +++ b/station-vanilla-fix-v0/src/main/java/net/modificationstation/stationapi/impl/vanillafix/datafixer/VanillaDataFixerImpl.java @@ -52,9 +52,12 @@ public final class VanillaDataFixerImpl { private static void registerFixer(DataFixerRegisterEvent event) { DataFixers.registerFixer(NAMESPACE, executor -> { DataFixerBuilder builder = new DataFixerBuilder(CURRENT_VERSION); + // This schema provides the conversion rules for everything found inside chunks: + // Entities, block entities, players, and items Schema schema19132 = builder.addSchema(19132, McRegionSchemaB1_7_3::new); Schema schema69420 = builder.addSchema(69420, StationFlatteningItemStackSchema::new); builder.addFixer(new McRegionToStationFlatteningItemStackFix(schema69420, "McRegionToStationFlatteningItemStackFix")); + // This schema gets used for converting blocks from the standard Beta 1.7.3 world format Schema schema69421 = builder.addSchema(69421, StationFlatteningChunkSchema::new); builder.addFixer(new McRegionToStationFlatteningChunkFix(schema69421, "McRegionToStationFlatteningChunkFix")); return builder.build().fixer();