Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
* <p>
* Handles block conversions
*/
public class McRegionToStationFlatteningChunkFix extends DataFix {
private final String name;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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")
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

Expand All @@ -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()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
import java.util.Map;
import java.util.function.Supplier;

/**
* Defines conversion rules for different parts of the chunk data
* <p>
* 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
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;

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<String> 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<String> ITEM_TO_OLD_ID = Util.make(new Object2IntOpenHashMap<>(512), map -> map.defaultReturnValue(0));
Expand All @@ -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
* <p>
* <em>Must be used after the putState method<em/>
* @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
* <p>
* <em>Must be used after the putState method<em/>
* @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 <T> Type of the dynamic
*/
public static <T> int lookupOldBlockId(Dynamic<T> dynamic) {
return BLOCK_TO_OLD_ID.getInt(dynamic.get("Name").asString(""));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
* <p>
* 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down