Preserved merged tag order

- Merged datapack tags keep first-seen value order with duplicates removed instead of re-sorting, so world preset tag order is preserved
- NeoForge remove lists are kept as the deduplicated union of both tags instead of being dropped
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-14 21:00:02 -04:00
1 parent 038833e614
commit 56ba2e51b7
2 files changed
+80 -16

No files matched your search

@@ -25,12 +25,13 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
final class ModdedDatapackTagMerge {
private static final String REPLACE = "replace";
private static final String VALUES = "values";
private static final String REMOVE = "remove";
private ModdedDatapackTagMerge() {
}
@@ -45,19 +46,27 @@ final class ModdedDatapackTagMerge {
static void merge(Path source, Path destination) throws IOException {
JSONObject incoming = read(source);
JSONObject existing = read(destination);
Map<String, Object> values = new TreeMap<>();
collect(existing, values, destination);
collect(incoming, values, source);
JSONArray merged = new JSONArray();
for (Object value : values.values()) {
merged.put(value);
}
JSONObject tag = new JSONObject();
tag.put(REPLACE, existing.optBoolean(REPLACE, false) || incoming.optBoolean(REPLACE, false));
tag.put(VALUES, merged);
tag.put(VALUES, union(existing, incoming, VALUES, source, destination));
if (existing.has(REMOVE) || incoming.has(REMOVE)) {
tag.put(REMOVE, union(existing, incoming, REMOVE, source, destination));
}
Files.writeString(destination, tag.toString(4), StandardCharsets.UTF_8);
}
private static JSONArray union(JSONObject existing, JSONObject incoming, String field, Path source, Path destination)
throws IOException {
Map<String, Object> entries = new LinkedHashMap<>();
collect(existing, field, entries, destination);
collect(incoming, field, entries, source);
JSONArray merged = new JSONArray();
for (Object entry : entries.values()) {
merged.put(entry);
}
return merged;
}
private static JSONObject read(Path tag) throws IOException {
try {
return new JSONObject(Files.readString(tag, StandardCharsets.UTF_8));
@@ -66,18 +75,18 @@ final class ModdedDatapackTagMerge {
}
}
private static void collect(JSONObject tag, Map<String, Object> values, Path origin) throws IOException {
JSONArray entries = tag.optJSONArray(VALUES);
if (entries == null) {
private static void collect(JSONObject tag, String field, Map<String, Object> entries, Path origin) throws IOException {
JSONArray values = tag.optJSONArray(field);
if (values == null) {
return;
}
for (int index = 0; index < entries.length(); index++) {
Object entry = entries.get(index);
for (int index = 0; index < values.length(); index++) {
Object entry = values.get(index);
String key = entry instanceof JSONObject object ? object.optString("id", "") : entry.toString();
if (key.isBlank()) {
throw new IOException("Iris cannot merge datapack tag " + origin + ": entry " + index + " has no id.");
throw new IOException("Iris cannot merge datapack tag " + origin + ": " + field + " entry " + index + " has no id.");
}
values.putIfAbsent(key, entry);
entries.putIfAbsent(key, entry);
}
}
}