From d8e5acf5150b832b0d2aec89891bde6f491ee485 Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Sat, 4 Mar 2023 19:01:02 +0100 Subject: [PATCH] 3.0.0 --- .gitignore | 4 +- README.md | 20 +- pom.xml | 15 +- .../mappingsgenerator/CursedMappings.java | 101 +++++++ .../mappingsgenerator/MappingsGenerator.java | 172 ++++++++--- .../mappingsgenerator/MappingsOptimizer.java | 284 +++++++++--------- .../{Main.java => util/ServerJarUtil.java} | 45 +-- .../mappingsgenerator/util/Version.java | 19 +- 8 files changed, 406 insertions(+), 254 deletions(-) create mode 100644 src/main/java/com/viaversion/mappingsgenerator/CursedMappings.java rename src/main/java/com/viaversion/mappingsgenerator/{Main.java => util/ServerJarUtil.java} (55%) diff --git a/.gitignore b/.gitignore index 0a4a61e..99de243 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,8 @@ -server.jar - ### Java files ### *.class # Package Files -#*.jar +*.jar *.war *.ear diff --git a/README.md b/README.md index 44d54c6..792e33e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,18 @@ -## Mappings +# Mappings -Generates mapping files for Via*. +Generates and compiles mapping files for Via*. Current mapping files can be found in the `mappings/` directory. -## Usage -`java -jar MappingsGenerator-2.0.jar `\ -So for example: `java -jar MappingsGenerator-2.0.jar server.jar 20w22a` +## Generating json mapping files for a Minecraft version -The mapping file will then be generated in the `mappings` directory. +Compile the project using `mvn clean build` and put the jar in some directory, ideally the project root. + +Then run the jar with: `java -jar MappingsGenerator.jar `, +e.g. `java -jar MappingsGenerator.jar server.jar 20w22a`. The mapping file will then be generated in the `mappings/` +directory. + +## Compiling json mapping files into compact nbt files ## License -The Java code is licensed under the GNU GPL v3 license. The files under `mappings/` are free to copy, use, and expand upon in whatever way you like. \ No newline at end of file + +The Java code is licensed under the GNU GPL v3 license. The files under `mappings/` are free to copy, use, and expand +upon in whatever way you like. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6db65c3..7eec1d4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.viaversion mappingsgenerator MappingsGenerator - 2.2 + 3.0.0 https://github.com/ViaVersion/Mappings 2020 @@ -92,7 +92,7 @@ - com.viaversion.mappingsgenerator.Main + com.viaversion.mappingsgenerator.MappingsGenerator @@ -110,7 +110,6 @@ - false it.unimi.dsi:fastutil @@ -142,6 +141,16 @@ + + + it.unimi.dsi.fastutil + com.viaversion.libs.fastutil + + + com.google.gson + com.viaversion.libs.libs.gson + + diff --git a/src/main/java/com/viaversion/mappingsgenerator/CursedMappings.java b/src/main/java/com/viaversion/mappingsgenerator/CursedMappings.java new file mode 100644 index 0000000..4f86859 --- /dev/null +++ b/src/main/java/com/viaversion/mappingsgenerator/CursedMappings.java @@ -0,0 +1,101 @@ +/* + * This file is part of ViaVersion Mappings - https://github.com/ViaVersion/Mappings + * Copyright (C) 2023 Nassim Jahnke + * Copyright (C) 2023 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.mappingsgenerator; + +import com.github.steveice10.opennbt.NBTIO; +import com.github.steveice10.opennbt.tag.builtin.ByteTag; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; +import com.github.steveice10.opennbt.tag.builtin.IntTag; +import com.google.gson.JsonObject; +import com.viaversion.mappingsgenerator.util.JsonConverter; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import java.io.File; +import java.io.IOException; +import org.jetbrains.annotations.Nullable; + +public final class CursedMappings { + + public static void optimizeAndSaveOhSoSpecial1_12AsNBT() throws IOException { + final JsonObject unmappedObject = MappingsLoader.load("mapping-1.12.json"); + final JsonObject mappedObject = MappingsLoader.load("mapping-1.13.json"); + final CompoundTag tag = new CompoundTag(); + tag.put("v", new IntTag(MappingsOptimizer.VERSION)); + MappingsOptimizer.handleUnknownFields(tag, unmappedObject); + cursedMappings(tag, unmappedObject, mappedObject, null, "blocks", "blockstates", "blockstates", 4084); + cursedMappings(tag, unmappedObject, mappedObject, null, "items", "items", "items", unmappedObject.getAsJsonObject("items").size()); + cursedMappings(tag, unmappedObject, mappedObject, null, "legacy_enchantments", "enchantments", "enchantments", 72); + MappingsOptimizer.mappings(tag, unmappedObject, mappedObject, null, true, false, "sounds"); + NBTIO.writeFile(tag, new File(MappingsOptimizer.OUTPUT_DIR, "mappings-1.12to1.13.nbt"), false, false); + } + + public static void optimizeAndSaveOhSoSpecial1_12AsNBTBackwards() throws IOException { + final JsonObject unmappedObject = MappingsLoader.load("mapping-1.13.json"); + final JsonObject mappedObject = MappingsLoader.load("mapping-1.12.json"); + final JsonObject diffObject = MappingsLoader.load("mappingdiff-1.13to1.12.json"); + final CompoundTag tag = new CompoundTag(); + tag.put("v", new IntTag(MappingsOptimizer.VERSION)); + MappingsOptimizer.handleUnknownFields(tag, unmappedObject); + cursedMappings(tag, unmappedObject, mappedObject, diffObject, "blockstates", "blocks", "blockstates", 8582); + cursedMappings(tag, unmappedObject, mappedObject, diffObject, "items", "items", "items", unmappedObject.getAsJsonArray("items").size()); + cursedMappings(tag, unmappedObject, mappedObject, diffObject, "enchantments", "legacy_enchantments", "enchantments", unmappedObject.getAsJsonArray("enchantments").size()); + MappingsOptimizer.names(tag, unmappedObject, diffObject, "items", "itemnames"); + MappingsOptimizer.fullNames(tag, diffObject, "entitynames", "entitynames"); + MappingsOptimizer.fullNames(tag, diffObject, "sounds", "soundnames"); + MappingsOptimizer.mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "sounds"); + NBTIO.writeFile(tag, new File(MappingsOptimizer.OUTPUT_BACKWARDS_DIR, "mappings-1.13to1.12.nbt"), false, false); + } + + private static void cursedMappings( + final CompoundTag tag, + final JsonObject unmappedObject, + final JsonObject mappedObject, + @Nullable final JsonObject diffObject, + final String unmappedKey, + final String mappedKey, + final String outputKey, + final int size + ) { + final JsonObject mappedIdentifiers = JsonConverter.toJsonObject(mappedObject.get(mappedKey)); + final Int2IntMap map = MappingsLoader.map( + JsonConverter.toJsonObject(unmappedObject.get(unmappedKey)), + mappedIdentifiers, + diffObject != null ? diffObject.getAsJsonObject(unmappedKey) : null, + true + ); + + final CompoundTag changedTag = new CompoundTag(); + final int[] unmapped = new int[map.size()]; + final int[] mapped = new int[map.size()]; + int i = 0; + for (final Int2IntMap.Entry entry : map.int2IntEntrySet()) { + unmapped[i] = entry.getIntKey(); + mapped[i] = entry.getIntValue(); + i++; + } + + changedTag.put("id", new ByteTag(MappingsOptimizer.CHANGES_ID)); + changedTag.put("nofill", new ByteTag((byte) 1)); + changedTag.put("size", new IntTag(size)); + changedTag.put("mappedSize", new IntTag(mappedIdentifiers.size())); + changedTag.put("at", new IntArrayTag(unmapped)); + changedTag.put("val", new IntArrayTag(mapped)); + tag.put(outputKey, changedTag); + } +} diff --git a/src/main/java/com/viaversion/mappingsgenerator/MappingsGenerator.java b/src/main/java/com/viaversion/mappingsgenerator/MappingsGenerator.java index 09c2682..d1dcb21 100644 --- a/src/main/java/com/viaversion/mappingsgenerator/MappingsGenerator.java +++ b/src/main/java/com/viaversion/mappingsgenerator/MappingsGenerator.java @@ -24,19 +24,65 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import com.viaversion.mappingsgenerator.util.ServerJarUtil; import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.net.URL; +import java.net.URLClassLoader; import java.nio.file.Files; import java.util.Map; import java.util.TreeMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * Mappings generator to collect certain json mappings from the server jar. + * + * @see MappingsOptimizer for the compacting process + */ public final class MappingsGenerator { - /** - * For in-IDE execution. - */ + private static final Logger LOGGER = LoggerFactory.getLogger(MappingsGenerator.class.getSimpleName()); + public static void main(final String[] args) throws Exception { + if (args.length != 2 && args.length != 3) { + throw new IllegalArgumentException("Required args: path to server jar, version"); + } + + MappingsGenerator.cleanup(); + + final String serverPath = args[0]; + final String version = args[1]; + final String versionToCompareFrom = args.length == 3 ? args[2] : null; + + final File serverFile = new File(serverPath); + if (!serverFile.exists()) { + LOGGER.error("Server file does not exist at {}", serverFile); + System.exit(1); + } + + LOGGER.info("Loading net.minecraft.data.Main class..."); + final ClassLoader loader = URLClassLoader.newInstance( + new URL[]{serverFile.toURI().toURL()}, + MappingsGenerator.class.getClassLoader() + ); + + final String[] serverArgs = {"--reports"}; + final Object serverMainConstructor = ServerJarUtil.loadMain(loader).getConstructor().newInstance(); + serverMainConstructor.getClass().getDeclaredMethod("main", String[].class).invoke(null, (Object) serverArgs); + + ServerJarUtil.waitForServerMain(); + + MappingsGenerator.collectMappings(version); + + if (versionToCompareFrom != null) { + LOGGER.info("Running mappings optimizer for versions {} -> {}...", versionToCompareFrom, version); + MappingsOptimizer.optimizeAndSaveAsNBT(versionToCompareFrom, version); + } + } + + /*public static void main(final String[] args) throws Exception { cleanup(); try { @@ -51,15 +97,25 @@ public final class MappingsGenerator { } collectMappings("23w08a"); - } + }*/ + /** + * Deletes the previous generated and logs directories. + */ public static void cleanup() { delete(new File("generated")); delete(new File("logs")); } + /** + * Recursively deletes a directory or file. + * + * @param file file or directory to delete + */ public static void delete(final File file) { - if (!file.exists()) return; + if (!file.exists()) { + return; + } if (file.isDirectory()) { for (final File f : file.listFiles()) { delete(f); @@ -69,18 +125,20 @@ public final class MappingsGenerator { file.delete(); } + /** + * Collects registry mappings for the given Minecraft version and saves them to a json file. + * + * @param version Minecraft version + */ public static void collectMappings(final String version) throws IOException { - System.out.println("Beginning mapping collection..."); - String content = new String(Files.readAllBytes(new File("generated/reports/blocks.json").toPath())); - + LOGGER.info("Beginning mapping collection..."); final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - JsonObject object = gson.fromJson(content, JsonObject.class); - - final JsonObject viaMappings = new JsonObject(); + final String blocksContent = new String(Files.readAllBytes(new File("generated/reports/blocks.json").toPath())); + final JsonObject blocksObject = gson.fromJson(blocksContent, JsonObject.class); // Blocks and blockstates final Map blockstatesById = new TreeMap<>(); - for (final Map.Entry blocksEntry : object.entrySet()) { + for (final Map.Entry blocksEntry : blocksObject.entrySet()) { final JsonObject block = blocksEntry.getValue().getAsJsonObject(); final JsonArray states = block.getAsJsonArray("states"); for (final JsonElement state : states) { @@ -90,27 +148,13 @@ public final class MappingsGenerator { throw new IllegalArgumentException("Duplicate blockstate id: " + id); } - final StringBuilder value = new StringBuilder(removeNamespace(blocksEntry.getKey())); - if (stateObject.has("properties")) { - value.append('['); - final JsonObject properties = stateObject.getAsJsonObject("properties"); - boolean first = true; - for (final Map.Entry propertyEntry : properties.entrySet()) { - if (first) { - first = false; - } else { - value.append(','); - } - value.append(propertyEntry.getKey()).append('=').append(propertyEntry.getValue().getAsJsonPrimitive().getAsString()); - } - value.append(']'); - } - blockstatesById.put(id, value.toString()); + blockstatesById.put(id, serializeBlockState(blocksEntry.getKey(), stateObject)); } } final JsonArray blockstates = new JsonArray(); final JsonArray blocks = new JsonArray(); + final JsonObject viaMappings = new JsonObject(); viaMappings.add("blockstates", blockstates); viaMappings.add("blocks", blocks); @@ -126,18 +170,17 @@ public final class MappingsGenerator { } } - content = new String(Files.readAllBytes(new File("generated/reports/registries.json").toPath())); - object = gson.fromJson(content, JsonObject.class); - - addArray(viaMappings, object, "minecraft:item", "items"); - addArray(viaMappings, object, "minecraft:sound_event", "sounds"); - addArray(viaMappings, object, "minecraft:particle_type", "particles"); - addArray(viaMappings, object, "minecraft:block_entity_type", "blockentities"); - addArray(viaMappings, object, "minecraft:command_argument_type", "argumenttypes"); - addArray(viaMappings, object, "minecraft:enchantment", "enchantments"); - addArray(viaMappings, object, "minecraft:entity_type", "entities"); - addArray(viaMappings, object, "minecraft:motive", "paintings"); - addArray(viaMappings, object, "minecraft:painting_variant", "paintings"); + final String registriesContent = new String(Files.readAllBytes(new File("generated/reports/registries.json").toPath())); + final JsonObject registries = gson.fromJson(registriesContent, JsonObject.class); + addArray(viaMappings, registries, "minecraft:item", "items"); + addArray(viaMappings, registries, "minecraft:sound_event", "sounds"); + addArray(viaMappings, registries, "minecraft:particle_type", "particles"); + addArray(viaMappings, registries, "minecraft:block_entity_type", "blockentities"); + addArray(viaMappings, registries, "minecraft:command_argument_type", "argumenttypes"); + addArray(viaMappings, registries, "minecraft:enchantment", "enchantments"); + addArray(viaMappings, registries, "minecraft:entity_type", "entities"); + addArray(viaMappings, registries, "minecraft:motive", "paintings"); + addArray(viaMappings, registries, "minecraft:painting_variant", "paintings"); // Save new File("mappings").mkdir(); @@ -146,16 +189,53 @@ public final class MappingsGenerator { } new File("logs").deleteOnExit(); - System.out.println("Done!"); + LOGGER.info("Mapping file has been written to mappings/mapping-{}.json", version); } + /** + * Returns a blockstate string for the given block and properties. + * + * @param block block identifier + * @param blockObject json object holding properties + * @return blockstate identifier + */ + private static String serializeBlockState(String block, final JsonObject blockObject) { + block = removeNamespace(block); + if (!blockObject.has("properties")) { + return block; + } + + final StringBuilder value = new StringBuilder(block); + value.append('['); + final JsonObject properties = blockObject.getAsJsonObject("properties"); + boolean first = true; + for (final Map.Entry propertyEntry : properties.entrySet()) { + if (first) { + first = false; + } else { + value.append(','); + } + value.append(propertyEntry.getKey()).append('=').append(propertyEntry.getValue().getAsJsonPrimitive().getAsString()); + } + value.append(']'); + return value.toString(); + } + + /** + * Adds array mappings from a registry to the mappings object. + * + * @param mappings mappings to add to + * @param registry registry to read from + * @param registryKey registry key to read from + * @param mappingsKey mappings key to write to + */ private static void addArray(final JsonObject mappings, final JsonObject registry, final String registryKey, final String mappingsKey) { if (!registry.has(registryKey)) { - System.out.println("Ignoring missing registry: " + registryKey); + LOGGER.debug("Ignoring missing registry: {}", registryKey); return; } - System.out.println("Collecting " + registryKey + "..."); + LOGGER.debug("Collecting {}...", registryKey); final JsonObject entries = registry.getAsJsonObject(registryKey).getAsJsonObject("entries"); final String[] keys = new String[entries.size()]; for (final Map.Entry entry : entries.entrySet()) { @@ -177,6 +257,12 @@ public final class MappingsGenerator { } } + /** + * Removes the Minecraft namespace from a potentially namespaced key. + * + * @param key key to remove the namespace from + * @return key without the Minecraft namespace + */ private static String removeNamespace(final String key) { if (key.startsWith("minecraft:")) { return key.substring("minecraft:".length()); diff --git a/src/main/java/com/viaversion/mappingsgenerator/MappingsOptimizer.java b/src/main/java/com/viaversion/mappingsgenerator/MappingsOptimizer.java index adac4bc..3541c24 100644 --- a/src/main/java/com/viaversion/mappingsgenerator/MappingsOptimizer.java +++ b/src/main/java/com/viaversion/mappingsgenerator/MappingsOptimizer.java @@ -29,9 +29,9 @@ import com.github.steveice10.opennbt.tag.builtin.Tag; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.viaversion.mappingsgenerator.MappingsLoader.MappingsResult; import com.viaversion.mappingsgenerator.util.JsonConverter; import com.viaversion.mappingsgenerator.util.Version; -import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.io.File; import java.io.IOException; @@ -45,19 +45,22 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Optimizes mapping files as nbt files with only the necessary data (mostly int to int mappings in form of int arrays). + */ public final class MappingsOptimizer { - private static final Logger LOGGER = LoggerFactory.getLogger(MappingsOptimizer.class.getSimpleName()); + public static final int VERSION = 1; + public static final byte DIRECT_ID = 0; + public static final byte SHIFTS_ID = 1; + public static final byte CHANGES_ID = 2; + public static final byte IDENTITY_ID = 3; + public static final File MAPPINGS_DIR = new File("mappings"); public static final File OUTPUT_DIR = new File("output"); public static final File OUTPUT_BACKWARDS_DIR = new File(OUTPUT_DIR, "backwards"); - public static final File MAPPINGS_DIR = new File("mappings"); + + private static final Logger LOGGER = LoggerFactory.getLogger(MappingsOptimizer.class.getSimpleName()); private static final Set STANDARD_FIELDS = Set.of("blockstates", "blocks", "items", "sounds", "blockentities", "enchantments", "paintings", "entities", "particles", "argumenttypes", "statistics", "tags"); - private static final Set STANDARD_DIFF_FIELDS = Set.of("itemnames", "entitynames"); - private static final int VERSION = 1; - private static final byte DIRECT_ID = 0; - private static final byte SHIFTS_ID = 1; - private static final byte CHANGES_ID = 2; - private static final byte IDENTITY_ID = 3; private static final String DIFF_FILE_FORMAT = "mappingdiff-%sto%s.json"; private static final String MAPPING_FILE_FORMAT = "mapping-%s.json"; private static final String OUTPUT_FILE_FORMAT = "mappings-%sto%s.nbt"; @@ -76,7 +79,7 @@ public final class MappingsOptimizer { final String from = args.length == 2 ? args[0] : "1.12"; final String to = args.length == 2 ? args[1] : "1.11"; - optimizeAndSaveAsNBT(from, to, OUTPUT_DIR); + optimizeAndSaveAsNBT(from, to); } /** @@ -85,7 +88,8 @@ public final class MappingsOptimizer { * @param from version to map from * @param to version to map to */ - private static void optimizeAndSaveAsNBT(final String from, final String to, final File outputDir) throws IOException { + public static void optimizeAndSaveAsNBT(final String from, final String to) throws IOException { + LOGGER.info("Compacting json mapping files for versions {} -> {}...", to, from); final JsonObject unmappedObject = MappingsLoader.load(MAPPING_FILE_FORMAT.formatted(from)); final JsonObject mappedObject = MappingsLoader.load(MAPPING_FILE_FORMAT.formatted(to)); final JsonObject diffObject = MappingsLoader.load(DIFF_FILE_FORMAT.formatted(from, to)); @@ -112,12 +116,15 @@ public final class MappingsOptimizer { mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "particles"); mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "argumenttypes"); mappings(tag, unmappedObject, mappedObject, diffObject, false, false, "statistics"); + if (diffObject != null) { names(tag, unmappedObject, diffObject, "items", "itemnames"); fullNames(tag, diffObject, "entitynames", "entitynames"); - if (outputDir == OUTPUT_BACKWARDS_DIR) { // EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + + if (Version.isBackwards(from, to)) { fullNames(tag, diffObject, "sounds", "soundnames"); } + if (diffObject.has("tags")) { final CompoundTag tagsTag = new CompoundTag(); tags(tagsTag, mappedObject, diffObject); @@ -125,6 +132,7 @@ public final class MappingsOptimizer { } } + final File outputDir = Version.isBackwards(from, to) ? OUTPUT_BACKWARDS_DIR : OUTPUT_DIR; NBTIO.writeFile(tag, new File(outputDir, OUTPUT_FILE_FORMAT.formatted(from, to)), false, false); // Save full identifiers to a separate file per version @@ -142,37 +150,13 @@ public final class MappingsOptimizer { } } - private static void optimizeAndSaveOhSoSpecial1_12AsNBT() throws IOException { - final JsonObject unmappedObject = MappingsLoader.load("mapping-1.12.json"); - final JsonObject mappedObject = MappingsLoader.load("mapping-1.13.json"); - final CompoundTag tag = new CompoundTag(); - tag.put("v", new IntTag(VERSION)); - handleUnknownFields(tag, unmappedObject); - cursedMappings(tag, unmappedObject, mappedObject, null, "blocks", "blockstates", "blockstates", 4084); - cursedMappings(tag, unmappedObject, mappedObject, null, "items", "items", "items", unmappedObject.getAsJsonObject("items").size()); - cursedMappings(tag, unmappedObject, mappedObject, null, "legacy_enchantments", "enchantments", "enchantments", 72); - mappings(tag, unmappedObject, mappedObject, null, true, false, "sounds"); - NBTIO.writeFile(tag, new File(OUTPUT_DIR, "mappings-1.12to1.13.nbt"), false, false); - } - - private static void optimizeAndSaveOhSoSpecial1_12AsNBTBackwards() throws IOException { - final JsonObject unmappedObject = MappingsLoader.load("mapping-1.13.json"); - final JsonObject mappedObject = MappingsLoader.load("mapping-1.12.json"); - final JsonObject diffObject = MappingsLoader.load("mappingdiff-1.13to1.12.json"); - final CompoundTag tag = new CompoundTag(); - tag.put("v", new IntTag(VERSION)); - handleUnknownFields(tag, unmappedObject); - cursedMappings(tag, unmappedObject, mappedObject, diffObject, "blockstates", "blocks", "blockstates", 8582); - cursedMappings(tag, unmappedObject, mappedObject, diffObject, "items", "items", "items", unmappedObject.getAsJsonArray("items").size()); - cursedMappings(tag, unmappedObject, mappedObject, diffObject, "enchantments", "legacy_enchantments", "enchantments", unmappedObject.getAsJsonArray("enchantments").size()); - names(tag, unmappedObject, diffObject, "items", "itemnames"); - fullNames(tag, diffObject, "entitynames", "entitynames"); - fullNames(tag, diffObject, "sounds", "soundnames"); - mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "sounds"); - NBTIO.writeFile(tag, new File(OUTPUT_BACKWARDS_DIR, "mappings-1.13to1.12.nbt"), false, false); - } - - private static void handleUnknownFields(final CompoundTag tag, final JsonObject unmappedObject) { + /** + * Checks for unknown fields in the unmapped object and writes them to the tag unchanged. + * + * @param tag tag to write to + * @param unmappedObject unmapped object to check all fields from + */ + public static void handleUnknownFields(final CompoundTag tag, final JsonObject unmappedObject) { for (final String key : unmappedObject.keySet()) { if (STANDARD_FIELDS.contains(key)) { continue; @@ -192,28 +176,26 @@ public final class MappingsOptimizer { for (final File file : MAPPINGS_DIR.listFiles()) { final String name = file.getName(); if (name.startsWith("mapping-")) { - versions.add(name.substring("mapping-".length(), name.length() - ".json".length())); + final String version = name.substring("mapping-".length(), name.length() - ".json".length()); + versions.add(version); } } versions.sort(Comparator.comparing(Version::new)); + for (int i = 0; i < versions.size() - 1; i++) { final String from = versions.get(i); final String to = versions.get(i + 1); LOGGER.info("============================="); - LOGGER.info("Running {} to {}", from, to); if (from.equals("1.12") && to.equals("1.13")) { - optimizeAndSaveOhSoSpecial1_12AsNBT(); - LOGGER.info("Running {} to {}", to, from); - optimizeAndSaveOhSoSpecial1_12AsNBTBackwards(); + CursedMappings.optimizeAndSaveOhSoSpecial1_12AsNBT(); + CursedMappings.optimizeAndSaveOhSoSpecial1_12AsNBTBackwards(); continue; } - optimizeAndSaveAsNBT(from, to, OUTPUT_DIR); - + optimizeAndSaveAsNBT(from, to); LOGGER.info("-----------------------------"); - LOGGER.info("Running {} to {}", to, from); - optimizeAndSaveAsNBT(to, from, OUTPUT_BACKWARDS_DIR); + optimizeAndSaveAsNBT(to, from); LOGGER.info(""); } } @@ -229,7 +211,7 @@ public final class MappingsOptimizer { * @param alwaysWriteIdentity whether to always write the identity mapping with size and mapped size, even if the two arrays are equal * @param key to read from and write to */ - private static void mappings( + public static void mappings( final CompoundTag tag, final JsonObject unmappedObject, final JsonObject mappedObject, @@ -250,48 +232,19 @@ public final class MappingsOptimizer { } final JsonObject diffIdentifiers = diffMappings != null ? diffMappings.getAsJsonObject(key) : null; - final MappingsLoader.MappingsResult result = MappingsLoader.map(unmappedIdentifiers, mappedIdentifiers, diffIdentifiers, warnOnMissing); + final MappingsResult result = MappingsLoader.map(unmappedIdentifiers, mappedIdentifiers, diffIdentifiers, warnOnMissing); serialize(result, tag, key, alwaysWriteIdentity); } - private static void cursedMappings( - final CompoundTag tag, - final JsonObject unmappedObject, - final JsonObject mappedObject, - @Nullable final JsonObject diffObject, - final String unmappedKey, - final String mappedKey, - final String outputKey, - final int size - ) { - final JsonObject mappedIdentifiers = JsonConverter.toJsonObject(mappedObject.get(mappedKey)); - final Int2IntMap map = MappingsLoader.map( - JsonConverter.toJsonObject(unmappedObject.get(unmappedKey)), - mappedIdentifiers, - diffObject != null ? diffObject.getAsJsonObject(unmappedKey) : null, - true - ); - - final CompoundTag changedTag = new CompoundTag(); - final int[] unmapped = new int[map.size()]; - final int[] mapped = new int[map.size()]; - int i = 0; - for (final Int2IntMap.Entry entry : map.int2IntEntrySet()) { - unmapped[i] = entry.getIntKey(); - mapped[i] = entry.getIntValue(); - i++; - } - - changedTag.put("id", new ByteTag(CHANGES_ID)); - changedTag.put("nofill", new ByteTag((byte) 1)); - changedTag.put("size", new IntTag(size)); - changedTag.put("mappedSize", new IntTag(mappedIdentifiers.size())); - changedTag.put("at", new IntArrayTag(unmapped)); - changedTag.put("val", new IntArrayTag(mapped)); - tag.put(outputKey, changedTag); - } - - private static void names( + /** + * Writes int->string mappings to the given tag. + * + * @param data tag to write to + * @param diffObject diff mappings object + * @param key key to read identifiers from + * @param namesKey key to read names from and to write to + */ + public static void names( final CompoundTag data, final JsonObject object, final JsonObject diffObject, @@ -313,7 +266,15 @@ public final class MappingsOptimizer { } } - private static void fullNames( + /** + * Writes string->string mappings to the given tag. + * + * @param data tag to write to + * @param diffObject diff mappings object + * @param key key to read from + * @param outputKey key to write to + */ + public static void fullNames( final CompoundTag data, final JsonObject diffObject, final String key, @@ -409,7 +370,7 @@ public final class MappingsOptimizer { * @param key key to write to * @param alwaysWriteIdentity whether to write identity mappings even if there are no changes */ - private static void serialize(final MappingsLoader.MappingsResult result, final CompoundTag parent, final String key, boolean alwaysWriteIdentity) { + private static void serialize(final MappingsResult result, final CompoundTag parent, final String key, final boolean alwaysWriteIdentity) { final int[] mappings = result.mappings(); final int numberOfChanges = mappings.length - result.identityMappings(); final boolean hasChanges = numberOfChanges != 0 || result.emptyMappings() != 0; @@ -432,60 +393,9 @@ public final class MappingsOptimizer { final int shiftFormatSize = approximateShiftFormatSize(result); final int plainFormatSize = mappings.length; if (changedFormatSize < plainFormatSize && changedFormatSize < shiftFormatSize) { - // Put two intarrays of only changed ids instead of adding an entry for every single identifier - LOGGER.debug("{}: Storing as changed and mapped arrays", key); - tag.put("id", new ByteTag(CHANGES_ID)); - tag.put("size", new IntTag(mappings.length)); - - final int[] unmapped = new int[numberOfChanges]; - final int[] mapped = new int[numberOfChanges]; - int index = 0; - for (int i = 0; i < mappings.length; i++) { - final int mappedId = mappings[i]; - if (mappedId != i) { - unmapped[index] = i; - mapped[index] = mappedId; - index++; - } - } - - if (index != numberOfChanges) { - throw new IllegalStateException("Index " + index + " does not equal number of changes " + numberOfChanges); - } - - tag.put("at", new IntArrayTag(unmapped)); - tag.put("val", new IntArrayTag(mapped)); + writeChangedFormat(tag, result, key, numberOfChanges); } else if (shiftFormatSize < changedFormatSize && shiftFormatSize < plainFormatSize) { - LOGGER.debug("{}: Storing as shifts", key); - tag.put("id", new ByteTag(SHIFTS_ID)); - tag.put("size", new IntTag(mappings.length)); - - final int[] shiftsAt = new int[result.shiftChanges()]; - final int[] shiftsTo = new int[result.shiftChanges()]; - - int index = 0; - // Check the first entry - if (mappings[0] != 0) { - shiftsAt[0] = 0; - shiftsTo[0] = mappings[0]; - index++; - } - - for (int id = 1; id < mappings.length; id++) { - final int mappedId = mappings[id]; - if (mappedId != mappings[id - 1] + 1) { - shiftsAt[index] = id; - shiftsTo[index] = mappedId; - index++; - } - } - - if (index != result.shiftChanges()) { - throw new IllegalStateException("Index " + index + " does not equal number of changes " + result.shiftChanges() + " for " + key); - } - - tag.put("at", new IntArrayTag(shiftsAt)); - tag.put("to", new IntArrayTag(shiftsTo)); + writeShiftFormat(tag, result, key); } else { LOGGER.debug("{}: Storing as direct values", key); tag.put("id", new ByteTag(DIRECT_ID)); @@ -493,12 +403,88 @@ public final class MappingsOptimizer { } } - private static int approximateChangedFormatSize(final MappingsLoader.MappingsResult result) { + /** + * Writes compact int to int mappings as changed values to the given tag. + * + * @param tag tag to write to + * @param result result with int to int mappings + * @param key key to write to + * @param numberOfChanges number of changed mappings + */ + private static void writeChangedFormat(final CompoundTag tag, final MappingsResult result, final String key, final int numberOfChanges) { + // Put two intarrays of only changed ids instead of adding an entry for every single identifier + LOGGER.debug("{}: Storing as changed and mapped arrays", key); + final int[] mappings = result.mappings(); + tag.put("id", new ByteTag(CHANGES_ID)); + tag.put("size", new IntTag(mappings.length)); + + final int[] unmapped = new int[numberOfChanges]; + final int[] mapped = new int[numberOfChanges]; + int index = 0; + for (int i = 0; i < mappings.length; i++) { + final int mappedId = mappings[i]; + if (mappedId != i) { + unmapped[index] = i; + mapped[index] = mappedId; + index++; + } + } + + if (index != numberOfChanges) { + throw new IllegalStateException("Index " + index + " does not equal number of changes " + numberOfChanges); + } + + tag.put("at", new IntArrayTag(unmapped)); + tag.put("val", new IntArrayTag(mapped)); + } + + /** + * Writes compact int to int mappings as shifted values to the given tag. + * + * @param tag tag to write to + * @param result result with int to int mappings + * @param key key to write to + */ + private static void writeShiftFormat(final CompoundTag tag, final MappingsResult result, final String key) { + LOGGER.debug("{}: Storing as shifts", key); + final int[] mappings = result.mappings(); + tag.put("id", new ByteTag(SHIFTS_ID)); + tag.put("size", new IntTag(mappings.length)); + + final int[] shiftsAt = new int[result.shiftChanges()]; + final int[] shiftsTo = new int[result.shiftChanges()]; + + int index = 0; + // Check the first entry + if (mappings[0] != 0) { + shiftsAt[0] = 0; + shiftsTo[0] = mappings[0]; + index++; + } + + for (int id = 1; id < mappings.length; id++) { + final int mappedId = mappings[id]; + if (mappedId != mappings[id - 1] + 1) { + shiftsAt[index] = id; + shiftsTo[index] = mappedId; + index++; + } + } + + if (index != result.shiftChanges()) { + throw new IllegalStateException("Index " + index + " does not equal number of changes " + result.shiftChanges() + " for " + key); + } + + tag.put("at", new IntArrayTag(shiftsAt)); + tag.put("to", new IntArrayTag(shiftsTo)); + } + + private static int approximateChangedFormatSize(final MappingsResult result) { // Length of two arrays + more approximate length for extra tags return (result.mappings().length - result.identityMappings()) * 2 + 10; } - private static int approximateShiftFormatSize(final MappingsLoader.MappingsResult result) { + private static int approximateShiftFormatSize(final MappingsResult result) { // One entry in two arrays each time the id is not shifted by 1 from the last id + more approximate length for extra tags return result.shiftChanges() * 2 + 10; } diff --git a/src/main/java/com/viaversion/mappingsgenerator/Main.java b/src/main/java/com/viaversion/mappingsgenerator/util/ServerJarUtil.java similarity index 55% rename from src/main/java/com/viaversion/mappingsgenerator/Main.java rename to src/main/java/com/viaversion/mappingsgenerator/util/ServerJarUtil.java index 9e2a6e2..d138ad2 100644 --- a/src/main/java/com/viaversion/mappingsgenerator/Main.java +++ b/src/main/java/com/viaversion/mappingsgenerator/util/ServerJarUtil.java @@ -16,45 +16,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.viaversion.mappingsgenerator; +package com.viaversion.mappingsgenerator.util; -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public final class Main { +public final class ServerJarUtil { - private static final String[] ARGS = {"--reports"}; - - public static void main(final String[] args) throws Exception { - if (args.length != 2) { - throw new IllegalArgumentException("Required args: path to server jar, version"); - } - - final String serverPath = args[0]; - final String version = args[1]; - MappingsGenerator.cleanup(); - - final File serverFile = new File(serverPath); - if (!serverFile.exists()) { - System.err.println("Server file does not exist at " + serverFile); - System.exit(1); - } - - System.out.println("Loading net.minecraft.data.Main class..."); - final ClassLoader loader = URLClassLoader.newInstance( - new URL[]{serverFile.toURI().toURL()}, - Main.class.getClassLoader() - ); - - final Object o = loadMain(loader).getConstructor().newInstance(); - final Object[] mainArgs = {ARGS}; - o.getClass().getDeclaredMethod("main", String[].class).invoke(null, mainArgs); - - waitForServerMain(); - - MappingsGenerator.collectMappings(version); - } + private static final Logger LOGGER = LoggerFactory.getLogger(ServerJarUtil.class.getSimpleName()); public static void waitForServerMain() throws InterruptedException { final Thread serverMain = threadByName("ServerMain"); @@ -66,7 +35,7 @@ public final class Main { while (serverMain.isAlive()) { Thread.sleep(50); if (i++ * 50 > 30_000) { - System.err.println("Something definitely went wrong"); + LOGGER.error("Something definitely went wrong (waited over 30 seconds for the server main to start)"); System.exit(1); } } @@ -81,7 +50,7 @@ public final class Main { return null; } - private static Class loadMain(final ClassLoader classLoader) throws ClassNotFoundException { + public static Class loadMain(final ClassLoader classLoader) throws ClassNotFoundException { System.setProperty("bundlerMainClass", "net.minecraft.data.Main"); try { return classLoader.loadClass("net.minecraft.bundler.Main"); diff --git a/src/main/java/com/viaversion/mappingsgenerator/util/Version.java b/src/main/java/com/viaversion/mappingsgenerator/util/Version.java index 49336d0..10c9465 100644 --- a/src/main/java/com/viaversion/mappingsgenerator/util/Version.java +++ b/src/main/java/com/viaversion/mappingsgenerator/util/Version.java @@ -45,6 +45,10 @@ public final class Version implements Comparable { tag = matcher.group("tag") == null ? "" : matcher.group("tag"); } + public static boolean isBackwards(final String from, final String to) { + return new Version(from).compareTo(new Version(to)) > 0; + } + /** * Compare two versions * @@ -67,10 +71,12 @@ public final class Version implements Comparable { } // Simple tag check - if (verA.tag.isEmpty() && !verB.tag.isEmpty()) + if (verA.tag.isEmpty() && !verB.tag.isEmpty()) { return 1; - if (!verA.tag.isEmpty() && verB.tag.isEmpty()) + } + if (!verA.tag.isEmpty() && verB.tag.isEmpty()) { return -1; + } return 0; } @@ -102,13 +108,4 @@ public final class Version implements Comparable { result = 31 * result + Arrays.hashCode(parts); return result; } - - /** - * Get the tag, eg. -ALPHA - * - * @return The version tag - */ - public String getTag() { - return tag; - } } \ No newline at end of file