Add method to generate stub diff files
Move diff files to extra directory
Fix tags serialization
General cleanup
This commit is contained in:
Nassim Jahnke 2023-03-05 12:42:12 +01:00
parent f5b03ac946
commit f7c010afab
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
39 changed files with 345 additions and 408 deletions

View File

@ -53,9 +53,9 @@ jobs:
- name: Run MappingsGenerator
run: java -jar MappingsGenerator.jar server.jar ${{ steps.next_release.outputs.content }} ${{ steps.last_release.outputs.content }}
- name: Pack mappings
run: java -cp MappingsGenerator.jar com.viaversion.mappingsgenerator.MappingsOptimizer ${{ steps.last_release.outputs.content }} ${{ steps.next_release.outputs.content }}
run: java -cp MappingsGenerator.jar com.viaversion.mappingsgenerator.MappingsOptimizer ${{ steps.last_release.outputs.content }} ${{ steps.next_release.outputs.content }} --generateDiffStubs
- name: Pack backwards mappings
run: java -cp MappingsGenerator.jar com.viaversion.mappingsgenerator.MappingsOptimizer ${{ steps.next_release.outputs.content }} ${{ steps.last_release.outputs.content }}
run: java -cp MappingsGenerator.jar com.viaversion.mappingsgenerator.MappingsOptimizer ${{ steps.next_release.outputs.content }} ${{ steps.last_release.outputs.content }} --generateDiffStubs
- name: Commit changes
run: |

View File

@ -14,21 +14,6 @@ java -jar MappingsGenerator.jar <path to server jar> <version>
The mapping file will then be generated in the `mappings/` directory.
### Directly compiling them to compact files
Optionally, you can pass a third argument to have it generate the compact nbt mappings file as
well:
```
java -jar MappingsGenerator.jar server.jar <version> <from version>
```
e.g. to generate the compact nbt mappings for 1.19.3->1.19.4:
```
java -jar MappingsGenerator.jar server.jar 1.19.4 1.19.3
```
## Compiling json mapping files into compact nbt files
If you want to generate the compact mapping files with already present json files, you can also trigger the optimizer on
@ -38,6 +23,9 @@ its own by starting the `MappingsOptimizer` class with the two arguments flipped
java -cp MappingsGenerator.jar com.viaversion.mappingsgenerator.MappingsOptimizer <from version> <to version>
```
Optionally, the `--generateDiffStubs` argument can be passed as a third argument to generate empty diff files for
missing mappings.
## Json format
The json files contain a number of Minecraft registries in form of json arrays, where the index corresponds to the id of
@ -47,10 +35,15 @@ Diff files for either ViaVersion or ViaBackwards then contain additional entries
of string→string or int→string mappings. These files need to be manually filled. If any such entries are required, the
optimizer will give a warning with the missing keys.
Json mapping files are found in the `mapping/` directory and are named `mapping-<version>.json`. Files containing
diff-mappings for added or removed identifiers between versions must be named `mapping-<from>to<to>.json` and put into
the `mapping/diff/` directory.
## Compact format
Compact files are always saved as [NBT](https://minecraft.fandom.com/wiki/NBT_format). ViaVersion uses its
own [OpenNBT](https://github.com/ViaVersion/OpenNBT) as its NBT reader/writer.
own [OpenNBT](https://github.com/ViaVersion/OpenNBT) as the NBT reader/writer. Compact files are found in the
`output/` directory and subdirectories.
### Identifier files

View File

@ -1,26 +0,0 @@
{
"sounds": {
"block.enchantment_table.use": "",
"entity.husk.ambient": "entity.zombie.ambient",
"entity.husk.death": "entity.zombie.death",
"entity.husk.hurt": "entity.zombie.hurt",
"entity.husk.step": "entity.zombie.step",
"entity.polar_bear.ambient": "entity.zombie.ambient",
"entity.polar_bear.baby_ambient": "entity.zombie.ambient",
"entity.polar_bear.death": "entity.zombie.death",
"entity.polar_bear.hurt": "entity.shulker.hurt",
"entity.polar_bear.step": "entity.horse.step",
"entity.polar_bear.warning": "entity.wolf.growl",
"entity.stray.ambient": "entity.skeleton.ambient",
"entity.stray.death": "entity.skeleton.death",
"entity.stray.hurt": "entity.skeleton.hurt",
"entity.stray.step": "entity.skeleton.step",
"entity.wither_skeleton.ambient": "entity.skeleton.ambient",
"entity.wither_skeleton.death": "entity.skeleton.death",
"entity.wither_skeleton.hurt": "entity.skeleton.hurt",
"entity.wither_skeleton.step": "entity.skeleton.step"
},
"entitynames": {
"polar_bear": "Polar Bear"
}
}

View File

@ -1,43 +0,0 @@
{
"sounds": {
"block.shulker_box.close": "block.wooden_trapdoor.close",
"block.shulker_box.open": "block.wooden_trapdoor.open",
"entity.elder_guardian.flop": "entity.guardian.flop",
"entity.evocation_fangs.attack": "entity.zombie.attack_iron_door",
"entity.evocation_illager.ambient": "entity.villager.ambient",
"entity.evocation_illager.cast_spell": "entity.irongolem.hurt",
"entity.evocation_illager.death": "entity.zombie_villager.death",
"entity.evocation_illager.hurt": "entity.villager.hurt",
"entity.evocation_illager.prepare_attack": "entity.elder_guardian.curse",
"entity.evocation_illager.prepare_summon": "block.portal.trigger",
"entity.evocation_illager.prepare_wololo": "entity.witch.ambient",
"entity.llama.ambient": "entity.horse.ambient",
"entity.llama.angry": "entity.horse.angry",
"entity.llama.chest": "entity.donkey.chest",
"entity.llama.death": "entity.donkey.death",
"entity.llama.eat": "entity.horse.eat",
"entity.llama.hurt": "entity.donkey.hurt",
"entity.llama.spit": "entity.shulker.shoot",
"entity.llama.step": "entity.horse.step",
"entity.llama.swag": "item.armor.equip_generic",
"entity.mule.chest": "entity.donkey.chest",
"entity.vex.ambient": "entity.horse.breathe",
"entity.vex.charge": "entity.elder_guardian.ambient",
"entity.vex.death": "entity.cat.death",
"entity.vex.hurt": "entity.cat.hurt",
"entity.vindication_illager.ambient": "entity.villager.ambient",
"entity.vindication_illager.death": "entity.villager.death",
"entity.vindication_illager.hurt": "entity.villager.hurt",
"item.armor.equip_elytra": "item.armor.equip_generic",
"item.bottle.empty": "item.bottle.fill",
"item.totem.use": "block.anvil.destroy"
},
"entitynames": {
"stray": "Stray",
"husk": "Husk",
"evocation_illager": "Evoker",
"vex": "Vex",
"vindication_illager": "Vindicator",
"liama": "Llama"
}
}

View File

@ -1,56 +0,0 @@
{
"sounds": {
"block.end_portal.spawn": "entity.lightning.thunder",
"block.note.bell": "block.note.harp",
"block.note.chime": "block.note.harp",
"block.note.flute": "block.note.harp",
"block.note.guitar": "block.note.harp",
"block.note.xylophone": "block.note.harp",
"entity.illusion_illager.ambient": "entity.evocation_illager.ambient",
"entity.illusion_illager.cast_spell": "entity.evocation_illager.cast_spell",
"entity.illusion_illager.death": "entity.evocation_illager.death",
"entity.illusion_illager.hurt": "entity.evocation_illager.hurt",
"entity.illusion_illager.mirror_move": "entity.endermen.teleport",
"entity.illusion_illager.prepare_blindness": "entity.evocation_illager.prepare_summon",
"entity.illusion_illager.prepare_mirror": "entity.evocation_illager.prepare_attack",
"entity.parrot.ambient": "entity.bat.ambient",
"entity.parrot.death": "entity.bat.death",
"entity.parrot.eat": "entity.generic.eat",
"entity.parrot.fly": "entity.bat.loop",
"entity.parrot.hurt": "entity.bat.hurt",
"entity.parrot.imitate.blaze": "entity.blaze.ambient",
"entity.parrot.imitate.creeper": "entity.creeper.primed",
"entity.parrot.imitate.elder_guardian": "entity.elder_guardian.ambient",
"entity.parrot.imitate.enderdragon": "entity.enderdragon.ambient",
"entity.parrot.imitate.enderman": "entity.endermen.ambient",
"entity.parrot.imitate.endermite": "entity.endermite.ambient",
"entity.parrot.imitate.evocation_illager": "entity.evocation_illager.ambient",
"entity.parrot.imitate.ghast": "entity.ghast.ambient",
"entity.parrot.imitate.husk": "entity.husk.ambient",
"entity.parrot.imitate.illusion_illager": "entity.evocation_illager.ambient",
"entity.parrot.imitate.magmacube": "entity.magmacube.squish",
"entity.parrot.imitate.polar_bear": "entity.polar_bear.ambient",
"entity.parrot.imitate.shulker": "entity.shulker.ambient",
"entity.parrot.imitate.silverfish": "entity.silverfish.ambient",
"entity.parrot.imitate.skeleton": "entity.skeleton.ambient",
"entity.parrot.imitate.slime": "entity.slime.squish",
"entity.parrot.imitate.spider": "entity.spider.ambient",
"entity.parrot.imitate.stray": "entity.stray.ambient",
"entity.parrot.imitate.vex": "entity.vex.ambient",
"entity.parrot.imitate.vindication_illager": "entity.vindication_illager.ambient",
"entity.parrot.imitate.witch": "entity.witch.ambient",
"entity.parrot.imitate.wither": "entity.wither.ambient",
"entity.parrot.imitate.wither_skeleton": "entity.wither_skeleton.ambient",
"entity.parrot.imitate.wolf": "entity.wolf.ambient",
"entity.parrot.imitate.zombie": "entity.zombie.ambient",
"entity.parrot.imitate.zombie_pigman": "entity.zombie_pig.ambient",
"entity.parrot.imitate.zombie_villager": "entity.zombie_villager.ambient",
"entity.parrot.step": "entity.chicken.step",
"entity.player.hurt_drown": "entity.player.hurt",
"entity.player.hurt_on_fire": "entity.player.hurt"
},
"entitynames": {
"parrot": "Parrot",
"illusion_illager": "Illusioner"
}
}

Binary file not shown.

View File

@ -7,7 +7,7 @@
<groupId>com.viaversion</groupId>
<artifactId>mappingsgenerator</artifactId>
<name>MappingsGenerator</name>
<version>3.0.3</version>
<version>3.1.0</version>
<url>https://github.com/ViaVersion/Mappings</url>
<inceptionYear>2020</inceptionYear>

View File

@ -18,84 +18,30 @@
*/
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);
final MappingsOptimizer optimizer = new MappingsOptimizer("1.12", "1.13");
optimizer.handleUnknownFields();
optimizer.cursedMappings("blocks", "blockstates", "blockstates", 4084);
optimizer.cursedMappings("items", "items", "items");
optimizer.cursedMappings("legacy_enchantments", "enchantments", "enchantments", 72);
optimizer.mappings(true, false, "sounds");
optimizer.write(MappingsOptimizer.OUTPUT_DIR);
}
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);
final MappingsOptimizer optimizer = new MappingsOptimizer("1.13", "1.12");
optimizer.handleUnknownFields();
optimizer.cursedMappings("blockstates", "blocks", "blockstates", 8582);
optimizer.cursedMappings("items", "items", "items");
optimizer.cursedMappings("enchantments", "legacy_enchantments", "enchantments");
optimizer.names("items", "itemnames");
optimizer.fullNames("entitynames", "entitynames");
optimizer.fullNames("sounds", "soundnames");
optimizer.mappings(true, false, "sounds");
optimizer.write(MappingsOptimizer.OUTPUT_BACKWARDS_DIR);
}
}

View File

@ -18,21 +18,83 @@
*/
package com.viaversion.mappingsgenerator;
import com.viaversion.mappingsgenerator.util.ServerJarUtil;
import com.viaversion.mappingsgenerator.util.Version;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ManualRunner {
private static final boolean ALL = false;
private static final Logger LOGGER = LoggerFactory.getLogger(ManualRunner.class.getSimpleName());
private static final boolean ALL = true;
public static void main(final String[] args) throws IOException {
if (ALL) {
MappingsOptimizer.runAll();
runAll();
return;
}
final String from = "1.19.3";
final String to = "1.19.4";
MappingsOptimizer.optimizeAndSaveAsNBT(from, to);
MappingsOptimizer.optimizeAndSaveAsNBT(to, from);
final String from = "1.19";
final String to = "1.18";
MappingsOptimizer mappingsOptimizer = new MappingsOptimizer(from, to);
mappingsOptimizer.writeDiffStubs();
mappingsOptimizer.optimizeAndWrite();
mappingsOptimizer = new MappingsOptimizer(to, from);
mappingsOptimizer.writeDiffStubs();
mappingsOptimizer.optimizeAndWrite();
}
/**
* Runs the optimizer for all mapping files present in the mappings/ directory.
*/
public static void runAll() throws IOException {
final List<String> versions = new ArrayList<>();
for (final File file : MappingsOptimizer.MAPPINGS_DIR.toFile().listFiles()) {
final String name = file.getName();
if (name.startsWith("mapping-")) {
final String version = name.substring("mapping-".length(), name.length() - ".json".length());
versions.add(version);
}
}
versions.sort(Version::compare);
for (int i = 0; i < versions.size() - 1; i++) {
final String from = versions.get(i);
final String to = versions.get(i + 1);
LOGGER.info("=============================");
if (from.equals("1.12") && to.equals("1.13")) {
CursedMappings.optimizeAndSaveOhSoSpecial1_12AsNBT();
CursedMappings.optimizeAndSaveOhSoSpecial1_12AsNBTBackwards();
continue;
}
new MappingsOptimizer(from, to).optimizeAndWrite();
LOGGER.info("-----------------------------");
new MappingsOptimizer(to, from).optimizeAndWrite();
LOGGER.info("");
}
}
private static void runMappingsGen() throws Exception {
MappingsGenerator.cleanup();
try {
// Server jar bundle since 21w39a
// Alternatively, java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports
System.setProperty("bundlerMainClass", "net.minecraft.data.Main");
Class.forName("net.minecraft.bundler.Main").getDeclaredMethod("main", String[].class).invoke(null, (Object) new String[]{"--reports"});
ServerJarUtil.waitForServerMain();
} catch (final ClassNotFoundException ignored) {
final Class<?> mainClass = Class.forName("net.minecraft.data.Main");
mainClass.getDeclaredMethod("main", String[].class).invoke(null, (Object) new String[]{"--reports"});
}
MappingsGenerator.collectMappings("23w08a");
}
}

View File

@ -43,10 +43,11 @@ import org.slf4j.LoggerFactory;
*/
public final class MappingsGenerator {
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
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) {
if (args.length != 2) {
LOGGER.error("Required args: path to server jar, version");
System.exit(1);
}
@ -55,7 +56,6 @@ public final class MappingsGenerator {
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()) {
@ -76,30 +76,8 @@ public final class MappingsGenerator {
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 {
// Server jar bundle since 21w39a
// Alternatively, java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports
System.setProperty("bundlerMainClass", "net.minecraft.data.Main");
Class.forName("net.minecraft.bundler.Main").getDeclaredMethod("main", String[].class).invoke(null, (Object) new String[]{"--reports"});
Main.waitForServerMain();
} catch (final ClassNotFoundException ignored) {
final Class<?> mainClass = Class.forName("net.minecraft.data.Main");
mainClass.getDeclaredMethod("main", String[].class).invoke(null, (Object) new String[]{"--reports"});
}
collectMappings("23w08a");
}*/
/**
* Deletes the previous generated and logs directories.
*/
@ -133,9 +111,8 @@ public final class MappingsGenerator {
*/
public static void collectMappings(final String version) throws IOException {
LOGGER.info("Beginning mapping collection...");
final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
final String blocksContent = new String(Files.readAllBytes(new File("generated/reports/blocks.json").toPath()));
final JsonObject blocksObject = gson.fromJson(blocksContent, JsonObject.class);
final JsonObject blocksObject = GSON.fromJson(blocksContent, JsonObject.class);
// Blocks and blockstates
final Map<Integer, String> blockstatesById = new TreeMap<>();
@ -172,7 +149,7 @@ public final class MappingsGenerator {
}
final String registriesContent = new String(Files.readAllBytes(new File("generated/reports/registries.json").toPath()));
final JsonObject registries = gson.fromJson(registriesContent, JsonObject.class);
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");
@ -186,7 +163,7 @@ public final class MappingsGenerator {
// Save
new File("mappings").mkdir();
try (final PrintWriter out = new PrintWriter("mappings/mapping-" + version + ".json")) {
out.print(gson.toJson(viaMappings));
out.print(GSON.toJson(viaMappings));
}
new File("logs").deleteOnExit();

View File

@ -27,9 +27,9 @@ import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@ -47,12 +47,12 @@ public final class MappingsLoader {
* @return the mappings file as a JsonObject, or null if it does not exist
*/
public static @Nullable JsonObject load(final String name) throws IOException {
final File file = new File(MappingsOptimizer.MAPPINGS_DIR, name);
if (!file.exists()) {
final Path path = MappingsOptimizer.MAPPINGS_DIR.resolve(name);
if (!Files.exists(path)) {
return null;
}
final String content = Files.readString(file.toPath());
final String content = Files.readString(path);
return GSON.fromJson(content, JsonObject.class);
}
@ -67,23 +67,22 @@ public final class MappingsLoader {
*/
public static MappingsResult map(final JsonArray unmappedIdentifiers, final JsonArray mappedIdentifiers, @Nullable final JsonObject diffIdentifiers, final boolean warnOnMissing) {
final int[] output = new int[unmappedIdentifiers.size()];
final Object2IntMap<String> newIdentifierMap = MappingsLoader.arrayToMap(mappedIdentifiers);
final Object2IntMap<String> mappedIdentifierMap = MappingsLoader.arrayToMap(mappedIdentifiers);
int emptyMappings = 0;
int identityMappings = 0;
int shiftChanges = 0;
for (int id = 0; id < unmappedIdentifiers.size(); id++) {
final JsonElement unmappedIdentifier = unmappedIdentifiers.get(id);
final int mappedId = mapEntry(id, unmappedIdentifier.getAsString(), newIdentifierMap, diffIdentifiers, warnOnMissing);
if (mappedId != -1) {
output[id] = mappedId;
if (mappedId == id) {
identityMappings++;
}
} else {
final int mappedId = mapEntry(id, unmappedIdentifier.getAsString(), mappedIdentifierMap, diffIdentifiers, warnOnMissing);
output[id] = mappedId;
if (mappedId == -1) {
emptyMappings++;
} else if (mappedId == id) {
identityMappings++;
}
// Check the first entry/if the shift changed
// Check the first entry/if the mapped id is equal to the expected sequential id
if (id == 0 && mappedId != 0
|| id != 0 && mappedId != output[id - 1] + 1) {
shiftChanges++;
@ -104,10 +103,10 @@ public final class MappingsLoader {
public static Int2IntMap map(final JsonObject unmappedIdentifiers, final JsonObject mappedIdentifiers, @Nullable final JsonObject diffIdentifiers, final boolean warnOnMissing) {
final Int2IntMap output = new Int2IntLinkedOpenHashMap();
output.defaultReturnValue(-1);
final Object2IntMap<String> newIdentifierMap = MappingsLoader.indexedObjectToMap(mappedIdentifiers);
final Object2IntMap<String> mappedIdentifierMap = MappingsLoader.indexedObjectToMap(mappedIdentifiers);
for (final Map.Entry<String, JsonElement> entry : unmappedIdentifiers.entrySet()) {
final int id = Integer.parseInt(entry.getKey());
final int mappedId = mapEntry(id, entry.getValue().getAsString(), newIdentifierMap, diffIdentifiers, warnOnMissing);
final int mappedId = mapEntry(id, entry.getValue().getAsString(), mappedIdentifierMap, diffIdentifiers, warnOnMissing);
output.put(id, mappedId);
}
return output;
@ -173,6 +172,69 @@ public final class MappingsLoader {
return mappedId;
}
/**
* Returns a diff object stub for the given unmapped and mapped objects with empty mappings to be filled.
*
* @param unmappedObject unmapped object
* @param mappedObject mapped object
* @param existingDiffObject existing diff object
* @return diff object stub, or null if no diff is needed
*/
public static @Nullable JsonObject getDiffObjectStub(final JsonObject unmappedObject, final JsonObject mappedObject, @Nullable final JsonObject existingDiffObject) {
final JsonObject diffObject = new JsonObject();
for (final Map.Entry<String, JsonElement> entry : unmappedObject.entrySet()) {
final String key = entry.getKey();
if (!entry.getValue().isJsonArray() || !mappedObject.has(key) || key.equals("blocks")) { // Special case!
continue;
}
final JsonArray unmappedIdentifiers = entry.getValue().getAsJsonArray();
final JsonArray mappedIdentifiers = mappedObject.getAsJsonArray(key);
final Object2IntMap<String> mappedIdentifierMap = MappingsLoader.arrayToMap(mappedIdentifiers);
final JsonObject diffIdentifiers = new JsonObject();
final JsonObject existingDiffIdentifiers = existingDiffObject != null && existingDiffObject.has(key) ? existingDiffObject.getAsJsonObject(key) : null;
for (int id = 0; id < unmappedIdentifiers.size(); id++) {
final String unmappedIdentifier = unmappedIdentifiers.get(id).getAsString();
final int mappedId = mapEntry(id, unmappedIdentifier, mappedIdentifierMap, existingDiffIdentifiers, false);
if (mappedId != -1) {
continue;
}
diffIdentifiers.addProperty(unmappedIdentifier, "");
}
if (!diffIdentifiers.isEmpty()) {
diffObject.add(key, diffIdentifiers);
}
}
if (diffObject.isEmpty()) {
return null;
}
if (existingDiffObject == null) {
return diffObject;
}
// Merge with existing diff object
for (final Map.Entry<String, JsonElement> entry : diffObject.entrySet()) {
final String key = entry.getKey();
final JsonObject value = entry.getValue().getAsJsonObject();
if (!existingDiffObject.has(key)) {
existingDiffObject.add(key, value);
continue;
}
final JsonObject diffIdentifiers = existingDiffObject.getAsJsonObject(key);
for (final Map.Entry<String, JsonElement> diffEntry : value.entrySet()) {
if (!diffIdentifiers.has(diffEntry.getKey())) {
diffIdentifiers.add(diffEntry.getKey(), diffEntry.getValue());
}
}
}
return existingDiffObject;
}
/**
* Returns a map of the object entries hashed by their id value.
*

View File

@ -32,15 +32,15 @@ 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;
import java.util.ArrayList;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -54,107 +54,151 @@ public final class MappingsOptimizer {
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 Path MAPPINGS_DIR = Path.of("mappings");
public static final Path OUTPUT_DIR = Path.of("output");
public static final Path OUTPUT_BACKWARDS_DIR = OUTPUT_DIR.resolve("backwards");
public static final String DIFF_FILE_FORMAT = "diff/mapping-%sto%s.json";
public static final String MAPPING_FILE_FORMAT = "mapping-%s.json";
public static final String OUTPUT_FILE_FORMAT = "mappings-%sto%s.nbt";
public static final String OUTPUT_IDENTIFIERS_FILE_FORMAT = "identifiers-%s.nbt";
private static final Logger LOGGER = LoggerFactory.getLogger(MappingsOptimizer.class.getSimpleName());
private static final Set<String> STANDARD_FIELDS = Set.of("blockstates", "blocks", "items", "sounds", "blockentities", "enchantments", "paintings", "entities", "particles", "argumenttypes", "statistics", "tags");
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";
private static final String OUTPUT_IDENTIFIERS_FILE_FORMAT = "identifiers-%s.nbt";
private static final Set<String> SAVED_IDENTIFIER_FILES = new HashSet<>();
private final Set<String> savedIdentifierFiles = new HashSet<>();
private final CompoundTag output;
private final String fromVersion;
private final String toVersion;
private final JsonObject unmappedObject;
private final JsonObject mappedObject;
private JsonObject diffObject;
public static void main(final String[] args) throws IOException {
if (args.length != 2) {
if (args.length < 2) {
LOGGER.error("Required args: from version, to version");
System.exit(1);
}
MAPPINGS_DIR.mkdirs();
OUTPUT_DIR.mkdirs();
Files.createDirectories(MAPPINGS_DIR);
Files.createDirectories(OUTPUT_DIR);
final Set<String> argsSet = new HashSet<>(Arrays.asList(Arrays.copyOfRange(args, 2, args.length)));
final String from = args[0];
final String to = args[1];
optimizeAndSaveAsNBT(from, to);
final MappingsOptimizer optimizer = new MappingsOptimizer(from, to);
if (argsSet.contains("--generateDiffStubs")) {
optimizer.writeDiffStubs();
}
optimizer.optimizeAndWrite();
}
/**
* Optimizes mapping files as nbt files with only the necessary data (int to int mappings in form of int arrays).
* Creates a new MappingsOptimizer instance.
*
* @param from version to map from
* @param to version to map to
* @see #optimizeAndWrite()
*/
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));
public MappingsOptimizer(final String from, final String to) throws IOException {
this.fromVersion = from;
this.toVersion = to;
output = new CompoundTag();
output.put("version", new IntTag(VERSION));
unmappedObject = MappingsLoader.load(MAPPING_FILE_FORMAT.formatted(from));
if (unmappedObject == null) {
throw new IllegalArgumentException("Mapping file for version " + from + " does not exist");
}
mappedObject = MappingsLoader.load(MAPPING_FILE_FORMAT.formatted(to));
if (mappedObject == null) {
throw new IllegalArgumentException("Mapping file for version " + to + " does not exist");
}
final CompoundTag tag = new CompoundTag();
tag.put("version", new IntTag(VERSION));
diffObject = MappingsLoader.load(DIFF_FILE_FORMAT.formatted(from, to));
}
handleUnknownFields(tag, unmappedObject);
/**
* Optimizes mapping files as nbt files with only the necessary data (int to int mappings in form of int arrays).
*/
public void optimizeAndWrite() throws IOException {
LOGGER.info("Compacting json mapping files for versions {} → {}...", fromVersion, toVersion);
mappings(tag, unmappedObject, mappedObject, diffObject, true, true, "blockstates");
mappings(tag, unmappedObject, mappedObject, diffObject, false, false, "blocks");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "items");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "sounds");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "blockentities");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "enchantments");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "paintings");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "entities");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "particles");
mappings(tag, unmappedObject, mappedObject, diffObject, true, false, "argumenttypes");
mappings(tag, unmappedObject, mappedObject, diffObject, false, false, "statistics");
handleUnknownFields();
mappings(true, true, "blockstates");
mappings(false, false, "blocks");
mappings(true, false, "items");
mappings(true, false, "sounds");
mappings(true, false, "blockentities");
mappings(true, false, "enchantments");
mappings(true, false, "paintings");
mappings(true, false, "entities");
mappings(true, false, "particles");
mappings(true, false, "argumenttypes");
mappings(false, false, "statistics");
if (diffObject != null) {
names(tag, unmappedObject, diffObject, "items", "itemnames");
fullNames(tag, diffObject, "entitynames", "entitynames");
names("items", "itemnames");
fullNames("entitynames", "entitynames");
if (Version.isBackwards(from, to)) {
fullNames(tag, diffObject, "sounds", "soundnames");
if (Version.isBackwards(fromVersion, toVersion)) {
fullNames("sounds", "soundnames");
}
if (diffObject.has("tags")) {
final CompoundTag tagsTag = new CompoundTag();
tags(tagsTag, mappedObject, diffObject);
tag.put("tags", tagsTag);
tags();
}
}
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);
write(Version.isBackwards(fromVersion, toVersion) ? OUTPUT_BACKWARDS_DIR : OUTPUT_DIR);
// Save full identifiers to a separate file per version
saveIdentifierFiles(from, unmappedObject);
saveIdentifierFiles(to, mappedObject);
saveIdentifierFiles(fromVersion, unmappedObject);
saveIdentifierFiles(toVersion, mappedObject);
}
private static void saveIdentifierFiles(final String version, final JsonObject object) throws IOException {
/**
* Writes a diff file with empty mappings for all fields that require manual mapping.
* The generated diff object will be used for further mappings generation on this instance.
*
* @return true if the diff stubs were written, false if they were not written because there were no changes
*/
public boolean writeDiffStubs() throws IOException {
final JsonObject diffObject = MappingsLoader.getDiffObjectStub(unmappedObject, mappedObject, this.diffObject);
if (diffObject != null) {
LOGGER.info("Writing diff stubs for versions {} → {}", fromVersion, toVersion);
Files.writeString(MAPPINGS_DIR.resolve(DIFF_FILE_FORMAT.formatted(fromVersion, toVersion)), MappingsGenerator.GSON.toJson(diffObject));
this.diffObject = diffObject;
return true;
}
return false;
}
/**
* Writes the current mappings output as an NBT file into the given directory.
*
* @param directory directory to write the output file to
*/
public void write(final Path directory) throws IOException {
write(output, directory.resolve(OUTPUT_FILE_FORMAT.formatted(fromVersion, toVersion)));
}
public void saveIdentifierFiles(final String version, final JsonObject object) throws IOException {
final CompoundTag identifiers = new CompoundTag();
storeIdentifiers(identifiers, object, "entities");
storeIdentifiers(identifiers, object, "particles");
storeIdentifiers(identifiers, object, "argumenttypes");
if (SAVED_IDENTIFIER_FILES.add(version)) {
NBTIO.writeFile(identifiers, new File(OUTPUT_DIR, OUTPUT_IDENTIFIERS_FILE_FORMAT.formatted(version)), false, false);
if (savedIdentifierFiles.add(version)) {
write(identifiers, OUTPUT_DIR.resolve(OUTPUT_IDENTIFIERS_FILE_FORMAT.formatted(version)));
}
}
/**
* 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) {
public void handleUnknownFields() {
for (final String key : unmappedObject.keySet()) {
if (STANDARD_FIELDS.contains(key)) {
continue;
@ -162,63 +206,20 @@ public final class MappingsOptimizer {
LOGGER.warn("NON-STANDARD FIELD: {} - writing it to the file without changes", key);
final Tag asTag = JsonConverter.toTag(unmappedObject.get(key));
tag.put(key, asTag);
}
}
/**
* Runs the optimizer for all mapping files present in the mappings/ directory.
*/
public static void runAll() throws IOException {
final List<String> versions = new ArrayList<>();
for (final File file : MAPPINGS_DIR.listFiles()) {
final String name = file.getName();
if (name.startsWith("mapping-")) {
final String version = name.substring("mapping-".length(), name.length() - ".json".length());
versions.add(version);
}
}
versions.sort(Version::compare);
for (int i = 0; i < versions.size() - 1; i++) {
final String from = versions.get(i);
final String to = versions.get(i + 1);
LOGGER.info("=============================");
if (from.equals("1.12") && to.equals("1.13")) {
CursedMappings.optimizeAndSaveOhSoSpecial1_12AsNBT();
CursedMappings.optimizeAndSaveOhSoSpecial1_12AsNBTBackwards();
continue;
}
optimizeAndSaveAsNBT(from, to);
LOGGER.info("-----------------------------");
optimizeAndSaveAsNBT(to, from);
LOGGER.info("");
output.put(key, asTag);
}
}
/**
* Reads mappings from the unmapped and mapped objects and writes them to the nbt tag.
*
* @param tag tag to write to
* @param unmappedObject unmapped mappings object
* @param mappedObject mapped mappings object
* @param diffMappings diff mappings object
* @param warnOnMissing whether to warn on missing mappings
* @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
*/
public static void mappings(
final CompoundTag tag,
final JsonObject unmappedObject,
final JsonObject mappedObject,
@Nullable final JsonObject diffMappings,
final boolean warnOnMissing,
final boolean alwaysWriteIdentity,
final String key
) {
if (!unmappedObject.has(key) || !mappedObject.has(key)) {
public void mappings(final boolean warnOnMissing, final boolean alwaysWriteIdentity, final String key) {
if (!unmappedObject.has(key) || !mappedObject.has(key)
|| !unmappedObject.get(key).isJsonArray() || !mappedObject.get(key).isJsonArray()) {
return;
}
@ -229,33 +230,64 @@ public final class MappingsOptimizer {
return;
}
final JsonObject diffIdentifiers = diffMappings != null ? diffMappings.getAsJsonObject(key) : null;
final JsonObject diffIdentifiers = diffObject != null ? diffObject.getAsJsonObject(key) : null;
final MappingsResult result = MappingsLoader.map(unmappedIdentifiers, mappedIdentifiers, diffIdentifiers, warnOnMissing);
serialize(result, tag, key, alwaysWriteIdentity);
serialize(result, output, key, alwaysWriteIdentity);
}
public void cursedMappings(final String unmappedKey, final String mappedKey, final String outputKey) {
final JsonElement element = unmappedObject.get(unmappedKey);
cursedMappings(unmappedKey, mappedKey, outputKey, element.isJsonArray() ? element.getAsJsonArray().size() : element.getAsJsonObject().size());
}
public void cursedMappings(
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));
output.put(outputKey, changedTag);
}
/**
* 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
* @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,
final String key,
final String namesKey) {
if (!object.has(key) || !diffObject.has(namesKey)) {
public void names(final String key, final String namesKey) {
if (!unmappedObject.has(key) || !diffObject.has(namesKey)) {
return;
}
final Object2IntMap<String> identifierMap = MappingsLoader.arrayToMap(object.getAsJsonArray(key));
final Object2IntMap<String> identifierMap = MappingsLoader.arrayToMap(unmappedObject.getAsJsonArray(key));
final JsonObject nameMappings = diffObject.getAsJsonObject(namesKey);
final CompoundTag tag = new CompoundTag();
data.put(namesKey, tag);
output.put(namesKey, tag);
for (final Map.Entry<String, JsonElement> entry : nameMappings.entrySet()) {
// Would be smaller as two arrays, but /shrug
@ -267,24 +299,17 @@ public final class MappingsOptimizer {
/**
* 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
* @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,
final String outputKey
) {
public void fullNames(final String key, final String outputKey) {
if (!diffObject.has(key)) {
return;
}
final JsonObject nameMappings = diffObject.getAsJsonObject(key);
final CompoundTag tag = new CompoundTag();
data.put(outputKey, tag);
output.put(outputKey, tag);
for (final Map.Entry<String, JsonElement> entry : nameMappings.entrySet()) {
tag.put(entry.getKey(), new StringTag(entry.getValue().getAsString()));
@ -293,18 +318,15 @@ public final class MappingsOptimizer {
/**
* Writes mapped tag ids to the given tag.
*
* @param data tag to write to
* @param mappedObject mapped mappings object
* @param diffObject diff mappings object
*/
private static void tags(final CompoundTag data, final JsonObject mappedObject, final JsonObject diffObject) {
public void tags() {
final JsonObject tagsObject = diffObject.getAsJsonObject("tags");
final CompoundTag tagsTag = new CompoundTag();
for (final Map.Entry<String, JsonElement> entry : tagsObject.entrySet()) {
final JsonObject object = entry.getValue().getAsJsonObject();
final CompoundTag tag = new CompoundTag();
final String type = entry.getKey();
data.put(type, tag);
tagsTag.put(type, tag);
final String typeKey = switch (type) {
case "block" -> "blocks";
@ -333,6 +355,10 @@ public final class MappingsOptimizer {
tag.put(tagName, new IntArrayTag(tagIds));
}
}
if (!tagsTag.isEmpty()) {
output.put("tags", tagsTag);
}
}
/**
@ -477,6 +503,10 @@ public final class MappingsOptimizer {
tag.put("to", new IntArrayTag(shiftsTo));
}
public static void write(final CompoundTag tag, final Path path) throws IOException {
NBTIO.writeFile(tag, path.toFile(), false, false);
}
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;

View File

@ -18,7 +18,6 @@
*/
package com.viaversion.mappingsgenerator.extra;
import com.github.steveice10.opennbt.NBTIO;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
@ -26,7 +25,6 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.viaversion.mappingsgenerator.MappingsLoader;
import com.viaversion.mappingsgenerator.MappingsOptimizer;
import java.io.File;
import java.io.IOException;
public final class BlockStates1_13 {
@ -39,6 +37,6 @@ public final class BlockStates1_13 {
list.add(new StringTag(element.getAsString()));
}
tag.put("blockstates", list);
NBTIO.writeFile(tag, new File(MappingsOptimizer.OUTPUT_DIR, "blockstates-1.13.nbt"), false, false);
MappingsOptimizer.write(tag, MappingsOptimizer.OUTPUT_DIR.resolve("blockstates-1.13.nbt"));
}
}

View File

@ -18,7 +18,6 @@
*/
package com.viaversion.mappingsgenerator.extra;
import com.github.steveice10.opennbt.NBTIO;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
import com.google.gson.JsonArray;
@ -29,7 +28,6 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.File;
import java.io.IOException;
public final class MotionBlocking1_14 {
@ -69,6 +67,6 @@ public final class MotionBlocking1_14 {
final CompoundTag tag = new CompoundTag();
tag.put("motionBlocking", new IntArrayTag(motionBlockingIds));
tag.put("nonFullBlocks", new IntArrayTag(nonFullBlocks.toArray(new int[0])));
NBTIO.writeFile(tag, new File(MappingsOptimizer.OUTPUT_DIR, "extra/heightmap-1.14.nbt"), false, false);
MappingsOptimizer.write(tag, MappingsOptimizer.OUTPUT_DIR.resolve("extra/heightmap-1.14.nbt"));
}
}

View File

@ -46,11 +46,7 @@ public final class Version implements Comparable<Version> {
}
public static boolean isBackwards(final String from, final String to) {
try {
return new Version(from).compareTo(new Version(to)) > 0;
} catch (final IllegalArgumentException ignored) {
return from.compareTo(to) > 0;
}
return compare(from, to) > 0;
}
public static int compare(final String from, final String to) {