From 3051ddb6c023a3b5f9c74c27d5aa882a3a37146c Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Mon, 15 Nov 2021 21:49:02 +0100 Subject: [PATCH] Better Mappings builders Allows for mappings between json arrays and objects without a billion different constructors, also now uses the proper size for 1.18 chunks --- .../viaversion/api/data/IntArrayMappings.java | 58 ++++---- .../viaversion/api/data/MappingDataBase.java | 8 +- .../api/data/MappingDataLoader.java | 20 ++- .../viaversion/api/data/Mappings.java | 134 ++++++++++++++++++ .../data/MappingData.java | 6 +- .../data/MappingData.java | 3 +- .../packets/WorldPackets.java | 2 +- 7 files changed, 190 insertions(+), 41 deletions(-) diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/IntArrayMappings.java b/api/src/main/java/com/viaversion/viaversion/api/data/IntArrayMappings.java index c31c0b48b..2c8541384 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/IntArrayMappings.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/IntArrayMappings.java @@ -29,79 +29,78 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Arrays; public class IntArrayMappings implements Mappings { - protected final int[] oldToNew; + private final int[] oldToNew; + private final int mappedIds; - public IntArrayMappings(int[] oldToNew) { + private IntArrayMappings(int[] oldToNew, int mappedIds) { this.oldToNew = oldToNew; + this.mappedIds = mappedIds; } - /** - * Maps old identifiers to the new ones. - * If an old value cannot be found in the new mappings, the diffmapping will be checked for the given entry. - * - * @param size set size of the underlying short array - * @param oldMapping mappings to map from - * @param newMapping mappings to map to - * @param diffMapping extra mappings that will be used/scanned when an entry cannot be found - */ + public static Builder builder() { + return Mappings.builder(IntArrayMappings::new); + } + + @Deprecated/*(forRemoval = true)*/ + public IntArrayMappings(int[] oldToNew) { + this(oldToNew, -1); + } + + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(int size, JsonObject oldMapping, JsonObject newMapping, @Nullable JsonObject diffMapping) { oldToNew = new int[size]; Arrays.fill(oldToNew, -1); + this.mappedIds = newMapping.size(); MappingDataLoader.mapIdentifiers(oldToNew, oldMapping, newMapping, diffMapping); } + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(JsonObject oldMapping, JsonObject newMapping, @Nullable JsonObject diffMapping) { this(oldMapping.entrySet().size(), oldMapping, newMapping, diffMapping); } - /** - * Maps old identifiers to the new ones. - * - * @param size set size of the underlying short array - * @param oldMapping mappings to map from - * @param newMapping mappings to map to - */ + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(int size, JsonObject oldMapping, JsonObject newMapping) { oldToNew = new int[size]; Arrays.fill(oldToNew, -1); + mappedIds = -1; MappingDataLoader.mapIdentifiers(oldToNew, oldMapping, newMapping); } + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(JsonObject oldMapping, JsonObject newMapping) { this(oldMapping.entrySet().size(), oldMapping, newMapping); } - /** - * Maps old identifiers to the new ones. - * - * @param size set size of the underlying short array - * @param oldMapping mappings to map from - * @param newMapping mappings to map to - * @param diffMapping extra mappings that will be used/scanned when an entry cannot be found - * @param warnOnMissing should "No key for x" be printed if there is no matching identifier - */ + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(int size, JsonArray oldMapping, JsonArray newMapping, JsonObject diffMapping, boolean warnOnMissing) { oldToNew = new int[size]; Arrays.fill(oldToNew, -1); + mappedIds = -1; MappingDataLoader.mapIdentifiers(oldToNew, oldMapping, newMapping, diffMapping, warnOnMissing); } + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(int size, JsonArray oldMapping, JsonArray newMapping, boolean warnOnMissing) { this(size, oldMapping, newMapping, null, warnOnMissing); } + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(JsonArray oldMapping, JsonArray newMapping, boolean warnOnMissing) { this(oldMapping.size(), oldMapping, newMapping, warnOnMissing); } + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(int size, JsonArray oldMapping, JsonArray newMapping) { this(size, oldMapping, newMapping, true); } + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(JsonArray oldMapping, JsonArray newMapping, JsonObject diffMapping) { this(oldMapping.size(), oldMapping, newMapping, diffMapping, true); } + @Deprecated/*(forRemoval = true)*/ public IntArrayMappings(JsonArray oldMapping, JsonArray newMapping) { this(oldMapping.size(), oldMapping, newMapping, true); } @@ -121,6 +120,11 @@ public class IntArrayMappings implements Mappings { return oldToNew.length; } + @Override + public int mappedSize() { + return mappedIds; + } + public int[] getOldToNew() { return oldToNew; } diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java index 98a85e6d5..93f64612d 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java @@ -85,7 +85,7 @@ public class MappingDataBase implements MappingData { itemMappings = new Int2IntBiHashMap(); itemMappings.defaultReturnValue(-1); MappingDataLoader.mapIdentifiers(itemMappings, oldMappings.getAsJsonObject("items"), newMappings.getAsJsonObject("items"), - diffmapping != null ? diffmapping.getAsJsonObject("items") : null); + diffmapping != null ? diffmapping.getAsJsonObject("items") : null, true); } if (diffmapping != null && diffmapping.has("tags")) { @@ -195,14 +195,16 @@ public class MappingDataBase implements MappingData { if (!oldMappings.has(key) || !newMappings.has(key)) return null; JsonObject diff = diffMappings != null ? diffMappings.getAsJsonObject(key) : null; - return new IntArrayMappings(oldMappings.getAsJsonArray(key), newMappings.getAsJsonArray(key), diff); + return IntArrayMappings.builder().unmapped(oldMappings.getAsJsonArray(key)) + .mapped(newMappings.getAsJsonArray(key)).diffMappings(diff).build(); } protected @Nullable Mappings loadFromObject(JsonObject oldMappings, JsonObject newMappings, @Nullable JsonObject diffMappings, String key) { if (!oldMappings.has(key) || !newMappings.has(key)) return null; JsonObject diff = diffMappings != null ? diffMappings.getAsJsonObject(key) : null; - return new IntArrayMappings(oldMappings.getAsJsonObject(key), newMappings.getAsJsonObject(key), diff); + return IntArrayMappings.builder().unmapped(oldMappings.getAsJsonObject(key)) + .mapped(newMappings.getAsJsonObject(key)).diffMappings(diff).build(); } protected @Nullable JsonObject loadDiffFile() { diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java index 0b7012f24..ec7c8ce94 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java @@ -135,31 +135,36 @@ public class MappingDataLoader { } } - public static void mapIdentifiers(Int2IntBiMap output, JsonObject oldIdentifiers, JsonObject newIdentifiers, @Nullable JsonObject diffIdentifiers) { + public static void mapIdentifiers(Int2IntBiMap output, JsonObject oldIdentifiers, JsonObject newIdentifiers, @Nullable JsonObject diffIdentifiers, boolean warnOnMissing) { Object2IntMap newIdentifierMap = MappingDataLoader.indexedObjectToMap(newIdentifiers); for (Map.Entry entry : oldIdentifiers.entrySet()) { - int value = mapIdentifierEntry(entry, newIdentifierMap, diffIdentifiers); + int value = mapIdentifierEntry(entry, newIdentifierMap, diffIdentifiers, warnOnMissing); if (value != -1) { output.put(Integer.parseInt(entry.getKey()), value); } } } + @Deprecated/*(forRemoval = true)*/ public static void mapIdentifiers(int[] output, JsonObject oldIdentifiers, JsonObject newIdentifiers) { - MappingDataLoader.mapIdentifiers(output, oldIdentifiers, newIdentifiers, null); + mapIdentifiers(output, oldIdentifiers, newIdentifiers, null); } - public static void mapIdentifiers(int[] output, JsonObject oldIdentifiers, JsonObject newIdentifiers, @Nullable JsonObject diffIdentifiers) { + public static void mapIdentifiers(int[] output, JsonObject oldIdentifiers, JsonObject newIdentifiers, @Nullable JsonObject diffIdentifiers, boolean warnOnMissing) { Object2IntMap newIdentifierMap = MappingDataLoader.indexedObjectToMap(newIdentifiers); for (Map.Entry entry : oldIdentifiers.entrySet()) { - int value = mapIdentifierEntry(entry, newIdentifierMap, diffIdentifiers); + int value = mapIdentifierEntry(entry, newIdentifierMap, diffIdentifiers, warnOnMissing); if (value != -1) { output[Integer.parseInt(entry.getKey())] = value; } } } - private static int mapIdentifierEntry(Map.Entry entry, Object2IntMap newIdentifierMap, @Nullable JsonObject diffIdentifiers) { + public static void mapIdentifiers(int[] output, JsonObject oldIdentifiers, JsonObject newIdentifiers, @Nullable JsonObject diffIdentifiers) { + mapIdentifiers(output, oldIdentifiers, newIdentifiers, diffIdentifiers, true); + } + + private static int mapIdentifierEntry(Map.Entry entry, Object2IntMap newIdentifierMap, @Nullable JsonObject diffIdentifiers, boolean warnOnMissing) { int value = newIdentifierMap.getInt(entry.getValue().getAsString()); if (value == -1) { // Search in diff mappings @@ -170,7 +175,7 @@ public class MappingDataLoader { } } if (value == -1) { - if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) { + if (warnOnMissing && !Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) { Via.getPlatform().getLogger().warning("No key for " + entry.getValue() + " :( "); } return -1; @@ -179,6 +184,7 @@ public class MappingDataLoader { return value; } + @Deprecated/*(forRemoval = true)*/ public static void mapIdentifiers(int[] output, JsonArray oldIdentifiers, JsonArray newIdentifiers, boolean warnOnMissing) { mapIdentifiers(output, oldIdentifiers, newIdentifiers, null, warnOnMissing); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/Mappings.java b/api/src/main/java/com/viaversion/viaversion/api/data/Mappings.java index 510667148..b682df578 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/Mappings.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/Mappings.java @@ -22,6 +22,10 @@ */ package com.viaversion.viaversion.api.data; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + public interface Mappings { /** @@ -41,5 +45,135 @@ public interface Mappings { */ void setNewId(int id, int newId); + /** + * Returns amount of unmapped entries, being the size of the mapping. + * + * @return amount of unmapped entries + */ int size(); + + /** + * Returns the amount of new ids total, even if it does not have a direct mapping. + * Returns -1 if unknown. + * + * @return amount of new ids, or -1 if unknown + */ + int mappedSize(); + + static Builder builder(final MappingsSupplier supplier) { + return new Builder(supplier); + } + + @FunctionalInterface + interface MappingsSupplier { + + T supply(int[] mappings, int mappedIds); + } + + final class Builder { + + private final MappingsSupplier supplier; + private JsonElement unmapped; + private JsonElement mapped; + private JsonObject diffMappings; + private int mappedSize = -1; + private int size = -1; + private boolean warnOnMissing = true; + + private Builder(final MappingsSupplier supplier) { + this.supplier = supplier; + } + + /** + * Sets a custom entry size different to the size of the unmapped collection. + * + * @param size custom entry size + * @return self + */ + public Builder customEntrySize(final int size) { + this.size = size; + return this; + } + + /** + * Sets a custom entry mapped ids count different to the size of the mapped collection. + * + * @param size custom mapped id count + * @return self + */ + public Builder customMappedSize(final int size) { + this.mappedSize = size; + return this; + } + + /** + * Sets whether warnings should be logged for missing mapped ids. + * + * @param warnOnMissing whether warnings should be logged for missing mapped ids + * @return self + */ + public Builder warnOnMissing(final boolean warnOnMissing) { + this.warnOnMissing = warnOnMissing; + return this; + } + + public Builder unmapped(final JsonArray unmappedArray) { + this.unmapped = unmappedArray; + return this; + } + + public Builder unmapped(final JsonObject unmappedObject) { + this.unmapped = unmappedObject; + return this; + } + + public Builder mapped(final JsonArray mappedArray) { + this.mapped = mappedArray; + return this; + } + + public Builder mapped(final JsonObject mappedObject) { + this.mapped = mappedObject; + return this; + } + + public Builder diffMappings(final JsonObject diffMappings) { + this.diffMappings = diffMappings; + return this; + } + + public T build() { + final int size = this.size != -1 ? this.size : size(unmapped); + final int mappedSize = this.mappedSize != -1 ? this.mappedSize : size(mapped); + final int[] mappings = new int[size]; + + // Do conversion if one is an array and the other an object, otherwise directly map + if (unmapped.isJsonArray()) { + if (mapped.isJsonObject()) { + MappingDataLoader.mapIdentifiers(mappings, toJsonObject(unmapped.getAsJsonArray()), mapped.getAsJsonObject(), diffMappings, warnOnMissing); + } else { + MappingDataLoader.mapIdentifiers(mappings, unmapped.getAsJsonArray(), mapped.getAsJsonArray(), diffMappings, warnOnMissing); + } + } else if (mapped.isJsonArray()) { + MappingDataLoader.mapIdentifiers(mappings, unmapped.getAsJsonObject(), toJsonObject(mapped.getAsJsonArray()), diffMappings, warnOnMissing); + } else { + MappingDataLoader.mapIdentifiers(mappings, unmapped.getAsJsonObject(), mapped.getAsJsonObject(), diffMappings, warnOnMissing); + } + + return supplier.supply(mappings, mappedSize); + } + + private int size(final JsonElement element) { + return element.isJsonObject() ? element.getAsJsonObject().size() : element.getAsJsonArray().size(); + } + + private JsonObject toJsonObject(final JsonArray array) { + final JsonObject object = new JsonObject(); + for (int i = 0; i < array.size(); i++) { + final JsonElement element = array.get(i); + object.add(Integer.toString(i), element); + } + return object; + } + } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/data/MappingData.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/data/MappingData.java index 4bb4d72ba..171820870 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/data/MappingData.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/data/MappingData.java @@ -60,7 +60,8 @@ public class MappingData extends MappingDataBase { loadTags(fluidTags, newMappings.getAsJsonObject("fluid_tags")); loadEnchantments(oldEnchantmentsIds, oldMappings.getAsJsonObject("enchantments")); - enchantmentMappings = new IntArrayMappings(72, oldMappings.getAsJsonObject("enchantments"), newMappings.getAsJsonObject("enchantments")); + enchantmentMappings = IntArrayMappings.builder().customEntrySize(72) + .unmapped(oldMappings.getAsJsonObject("enchantments")).mapped(newMappings.getAsJsonObject("enchantments")).build(); // Map minecraft:snow[layers=1] of 1.12 to minecraft:snow[layers=2] in 1.13 if (Via.getConfig().isSnowCollisionFix()) { @@ -126,7 +127,8 @@ public class MappingData extends MappingDataBase { protected Mappings loadFromObject(JsonObject oldMappings, JsonObject newMappings, @Nullable JsonObject diffMappings, String key) { if (key.equals("blocks")) { // Need to use a custom size since there are larger gaps in ids - return new IntArrayMappings(4084, oldMappings.getAsJsonObject("blocks"), newMappings.getAsJsonObject("blockstates")); + return IntArrayMappings.builder().customEntrySize(4084) + .unmapped(oldMappings.getAsJsonObject("blocks")).mapped(newMappings.getAsJsonObject("blockstates")).build(); } else { return super.loadFromObject(oldMappings, newMappings, diffMappings, key); } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_15to1_14_4/data/MappingData.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_15to1_14_4/data/MappingData.java index ea69bdca6..e968c6348 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_15to1_14_4/data/MappingData.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_15to1_14_4/data/MappingData.java @@ -36,6 +36,7 @@ public class MappingData extends MappingDataBase { } // Ignore removed sounds - return new IntArrayMappings(oldMappings.getAsJsonArray(key), newMappings.getAsJsonArray(key), false); + return IntArrayMappings.builder().warnOnMissing(false) + .unmapped(oldMappings.getAsJsonArray(key)).mapped(newMappings.getAsJsonArray(key)).build(); } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_18to1_17_1/packets/WorldPackets.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_18to1_17_1/packets/WorldPackets.java index fd6559a8d..8d2c01432 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_18to1_17_1/packets/WorldPackets.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_18to1_17_1/packets/WorldPackets.java @@ -156,7 +156,7 @@ public final class WorldPackets { final Chunk chunk = new Chunk1_18(oldChunk.getX(), oldChunk.getZ(), oldChunk.getSections(), oldChunk.getHeightMap(), blockEntities); wrapper.write(new Chunk1_18Type(tracker.currentWorldSectionHeight(), - MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().size()), + MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()), MathUtil.ceilLog2(tracker.biomesSent())), chunk); final ChunkLightStorage lightStorage = wrapper.user().get(ChunkLightStorage.class);