mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 14:00:26 +00:00
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:
1 parent
038833e614
commit
56ba2e51b7
2 files changed
+80
-16
No files matched your search
+25
-16
@@ -25,12 +25,13 @@ import java.io.IOException;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
final class ModdedDatapackTagMerge {
|
final class ModdedDatapackTagMerge {
|
||||||
private static final String REPLACE = "replace";
|
private static final String REPLACE = "replace";
|
||||||
private static final String VALUES = "values";
|
private static final String VALUES = "values";
|
||||||
|
private static final String REMOVE = "remove";
|
||||||
|
|
||||||
private ModdedDatapackTagMerge() {
|
private ModdedDatapackTagMerge() {
|
||||||
}
|
}
|
||||||
@@ -45,19 +46,27 @@ final class ModdedDatapackTagMerge {
|
|||||||
static void merge(Path source, Path destination) throws IOException {
|
static void merge(Path source, Path destination) throws IOException {
|
||||||
JSONObject incoming = read(source);
|
JSONObject incoming = read(source);
|
||||||
JSONObject existing = read(destination);
|
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();
|
JSONObject tag = new JSONObject();
|
||||||
tag.put(REPLACE, existing.optBoolean(REPLACE, false) || incoming.optBoolean(REPLACE, false));
|
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);
|
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 {
|
private static JSONObject read(Path tag) throws IOException {
|
||||||
try {
|
try {
|
||||||
return new JSONObject(Files.readString(tag, StandardCharsets.UTF_8));
|
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 {
|
private static void collect(JSONObject tag, String field, Map<String, Object> entries, Path origin) throws IOException {
|
||||||
JSONArray entries = tag.optJSONArray(VALUES);
|
JSONArray values = tag.optJSONArray(field);
|
||||||
if (entries == null) {
|
if (values == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int index = 0; index < entries.length(); index++) {
|
for (int index = 0; index < values.length(); index++) {
|
||||||
Object entry = entries.get(index);
|
Object entry = values.get(index);
|
||||||
String key = entry instanceof JSONObject object ? object.optString("id", "") : entry.toString();
|
String key = entry instanceof JSONObject object ? object.optString("id", "") : entry.toString();
|
||||||
if (key.isBlank()) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+55
@@ -19,6 +19,7 @@
|
|||||||
package art.arcane.iris.modded;
|
package art.arcane.iris.modded;
|
||||||
|
|
||||||
import art.arcane.volmlib.util.collection.KSet;
|
import art.arcane.volmlib.util.collection.KSet;
|
||||||
|
import art.arcane.volmlib.util.json.JSONArray;
|
||||||
import art.arcane.volmlib.util.json.JSONObject;
|
import art.arcane.volmlib.util.json.JSONObject;
|
||||||
import art.arcane.iris.pack.datapack.DataVersion;
|
import art.arcane.iris.pack.datapack.DataVersion;
|
||||||
import art.arcane.iris.pack.datapack.v1217.DataFixerV1217;
|
import art.arcane.iris.pack.datapack.v1217.DataFixerV1217;
|
||||||
@@ -291,6 +292,60 @@ public class ModdedForcedDatapackTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tagMergeKeepsFirstSeenOrderAndUnionsRemoveLists() throws IOException {
|
||||||
|
Path root = Files.createTempDirectory("iris-forced-pack-tag-order");
|
||||||
|
try {
|
||||||
|
Path tag = Path.of("data/minecraft/tags/worldgen/world_preset/normal.json");
|
||||||
|
Path source = root.resolve("source");
|
||||||
|
Path destination = root.resolve("destination");
|
||||||
|
Files.createDirectories(source.resolve(tag).getParent());
|
||||||
|
Files.createDirectories(destination.resolve(tag).getParent());
|
||||||
|
Files.writeString(destination.resolve(tag),
|
||||||
|
"{\"replace\":false,\"values\":[\"irisworldgen:z\",\"irisworldgen:a\"],\"remove\":[\"minecraft:large_biomes\"]}",
|
||||||
|
StandardCharsets.UTF_8);
|
||||||
|
Files.writeString(source.resolve(tag),
|
||||||
|
"{\"replace\":false,\"values\":[\"irisworldgen:m\",\"irisworldgen:a\"],\"remove\":[\"minecraft:amplified\",\"minecraft:large_biomes\"]}",
|
||||||
|
StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
ModdedForcedDatapack.mergeDirectory(source, destination);
|
||||||
|
|
||||||
|
JSONObject merged = new JSONObject(Files.readString(destination.resolve(tag), StandardCharsets.UTF_8));
|
||||||
|
assertEquals(List.of("irisworldgen:z", "irisworldgen:a", "irisworldgen:m"), strings(merged.getJSONArray("values")));
|
||||||
|
assertEquals(List.of("minecraft:large_biomes", "minecraft:amplified"), strings(merged.getJSONArray("remove")));
|
||||||
|
} finally {
|
||||||
|
deleteTree(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tagMergeWithoutRemoveListsWritesNone() throws IOException {
|
||||||
|
Path root = Files.createTempDirectory("iris-forced-pack-tag-no-remove");
|
||||||
|
try {
|
||||||
|
Path tag = Path.of("data/minecraft/tags/worldgen/biome/is_overworld.json");
|
||||||
|
for (String side : List.of("source", "destination")) {
|
||||||
|
Files.createDirectories(root.resolve(side).resolve(tag).getParent());
|
||||||
|
Files.writeString(root.resolve(side).resolve(tag),
|
||||||
|
"{\"replace\":false,\"values\":[\"iris:biomes/" + side + "\"]}", StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModdedForcedDatapack.mergeDirectory(root.resolve("source"), root.resolve("destination"));
|
||||||
|
|
||||||
|
JSONObject merged = new JSONObject(Files.readString(root.resolve("destination").resolve(tag), StandardCharsets.UTF_8));
|
||||||
|
assertFalse(merged.has("remove"));
|
||||||
|
} finally {
|
||||||
|
deleteTree(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> strings(JSONArray array) {
|
||||||
|
List<String> values = new ArrayList<>();
|
||||||
|
for (int index = 0; index < array.length(); index++) {
|
||||||
|
values.add(array.get(index).toString());
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void tagMergeKeepsStrictConflictsForNonTagFilesInTheSameMerge() throws IOException {
|
public void tagMergeKeepsStrictConflictsForNonTagFilesInTheSameMerge() throws IOException {
|
||||||
Path root = Files.createTempDirectory("iris-forced-pack-tag-strict");
|
Path root = Files.createTempDirectory("iris-forced-pack-tag-strict");
|
||||||
|
|||||||
Reference in new issue
Block a user