diff --git a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java index b98a8931..52a78055 100644 --- a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java +++ b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java @@ -10,8 +10,9 @@ import net.minecraft.client.gui.screens.worldselection.EditWorldScreen; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.util.ProgressListener; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; @@ -322,12 +323,42 @@ public class DataFixerAPI { private static void fixPlayerNbt(CompoundTag player, boolean[] changed, MigrationProfile data) { //Checking Inventory - ListTag inventory = player.getList("Inventory", 10); - fixInventory(inventory, changed, data, true); + ListTag inventory = player.getList("Inventory", Tag.TAG_COMPOUND); + fixItemArrayWithID(inventory, changed, data, true); //Checking EnderChest - ListTag enderitems = player.getList("EnderItems", 10); - fixInventory(enderitems, changed, data, true); + ListTag enderitems = player.getList("EnderItems", Tag.TAG_COMPOUND); + fixItemArrayWithID(enderitems, changed, data, true); + + //Checking ReceipBook + if (player.contains("recipeBook")) { + CompoundTag recipeBook = player.getCompound("recipeBook"); + changed[0] |= fixStringIDList(recipeBook, "recipes", data); + changed[0] |= fixStringIDList(recipeBook, "toBeDisplayed", data); + } + } + + static boolean fixStringIDList(CompoundTag root, String name, MigrationProfile data) { + boolean _changed = false; + if (root.contains(name)) { + ListTag items = root.getList(name, Tag.TAG_STRING); + ListTag newItems = new ListTag(); + + for (Tag tag : items) { + final StringTag str = (StringTag)tag; + final String replace = data.replaceStringFromIDs(str.getAsString()); + if (replace!=null) { + _changed = true; + newItems.add(StringTag.valueOf(replace)); + } else { + newItems.add(tag); + } + } + if (_changed) { + root.put(name, newItems); + } + } + return _changed; } private static void fixRegion(MigrationProfile data, State state, File file) { @@ -350,18 +381,18 @@ public class DataFixerAPI { //Checking TileEntities ListTag tileEntities = root.getCompound("Level") - .getList("TileEntities", 10); + .getList("TileEntities", Tag.TAG_COMPOUND); fixItemArrayWithID(tileEntities, changed, data, true); //Checking Entities - ListTag entities = root.getList("Entities", 10); + ListTag entities = root.getList("Entities", Tag.TAG_COMPOUND); fixItemArrayWithID(entities, changed, data, true); //Checking Block Palette ListTag sections = root.getCompound("Level") - .getList("Sections", 10); + .getList("Sections", Tag.TAG_COMPOUND); sections.forEach((tag) -> { - ListTag palette = ((CompoundTag) tag).getList("Palette", 10); + ListTag palette = ((CompoundTag) tag).getList("Palette", Tag.TAG_COMPOUND); palette.forEach((blockTag) -> { CompoundTag blockTagCompound = ((CompoundTag) blockTag); changed[0] |= data.replaceStringFromIDs(blockTagCompound, "Name"); @@ -396,29 +427,14 @@ public class DataFixerAPI { return patchConfTag; } - private static void fixInventory(ListTag inventory, boolean[] changed, MigrationProfile data, boolean recursive) { - inventory.forEach(item -> { - changed[0] |= data.replaceStringFromIDs((CompoundTag)item, "id"); - - if (((CompoundTag) item).contains("tag")) { - CompoundTag tag = (CompoundTag)((CompoundTag) item).get("tag"); - if (tag.contains("BlockEntityTag")){ - CompoundTag blockEntityTag = (CompoundTag)((CompoundTag) tag).get("BlockEntityTag"); - ListTag items = blockEntityTag.getList("Items", 10); - fixItemArrayWithID(items, changed, data, recursive); - } - } - }); - } - - private static void fixItemArrayWithID(ListTag items, boolean[] changed, MigrationProfile data, boolean recursive) { + static void fixItemArrayWithID(ListTag items, boolean[] changed, MigrationProfile data, boolean recursive) { items.forEach(inTag -> { fixID((CompoundTag) inTag, changed, data, recursive); }); } - private static void fixID(CompoundTag inTag, boolean[] changed, MigrationProfile data, boolean recursive) { + static void fixID(CompoundTag inTag, boolean[] changed, MigrationProfile data, boolean recursive) { final CompoundTag tag = inTag; changed[0] |= data.replaceStringFromIDs(tag, "id"); @@ -428,11 +444,20 @@ public class DataFixerAPI { } if (recursive && tag.contains("Items")) { - fixItemArrayWithID(tag.getList("Items", 10), changed, data, true); + fixItemArrayWithID(tag.getList("Items", Tag.TAG_COMPOUND), changed, data, true); } if (recursive && tag.contains("Inventory")) { - ListTag inventory = tag.getList("Inventory", 10); - fixInventory(inventory, changed, data, recursive); + ListTag inventory = tag.getList("Inventory", Tag.TAG_COMPOUND); + fixItemArrayWithID(inventory, changed, data, true); + } + if (tag.contains("tag")) { + CompoundTag entityTag = (CompoundTag)tag.get("tag"); + if (entityTag.contains("BlockEntityTag")){ + CompoundTag blockEntityTag = (CompoundTag)entityTag.get("BlockEntityTag"); + fixID(blockEntityTag, changed, data, recursive); + /*ListTag items = blockEntityTag.getList("Items", Tag.TAG_COMPOUND); + fixItemArrayWithID(items, changed, data, recursive);*/ + } } } diff --git a/src/main/java/ru/bclib/api/datafixer/MigrationProfile.java b/src/main/java/ru/bclib/api/datafixer/MigrationProfile.java index d58f793c..e0466207 100644 --- a/src/main/java/ru/bclib/api/datafixer/MigrationProfile.java +++ b/src/main/java/ru/bclib/api/datafixer/MigrationProfile.java @@ -1,6 +1,8 @@ package ru.bclib.api.datafixer; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; import org.jetbrains.annotations.NotNull; import ru.bclib.api.WorldDataAPI; @@ -17,6 +19,7 @@ public class MigrationProfile { final Map idReplacements; final List> levelPatchers; final List worldDataPatchers; + final Map> worldDataIDPaths; private final CompoundTag config; @@ -31,11 +34,16 @@ public class MigrationProfile { HashMap replacements = new HashMap(); List> levelPatches = new LinkedList<>(); List worldDataPatches = new LinkedList<>(); + HashMap> worldDataIDPaths = new HashMap<>(); for (String modID : mods) { + Patch.getALL() .stream() .filter(p -> p.modID.equals(modID)) .forEach(patch -> { + List paths = patch.getWorldDataIDPaths(); + if (paths!=null) worldDataIDPaths.put(modID, paths); + if (currentPatchLevel(modID) < patch.level) { replacements.putAll(patch.getIDReplacements()); if (patch.getLevelDatPatcher()!=null) @@ -49,7 +57,8 @@ public class MigrationProfile { } }); } - + + this.worldDataIDPaths = Collections.unmodifiableMap(worldDataIDPaths); this.idReplacements = Collections.unmodifiableMap(replacements); this.levelPatchers = Collections.unmodifiableList(levelPatches); this.worldDataPatchers = Collections.unmodifiableList(worldDataPatches); @@ -74,10 +83,15 @@ public class MigrationProfile { public boolean hasAnyFixes() { return idReplacements.size() > 0 || levelPatchers.size() > 0 || worldDataPatchers.size() > 0; } + + public String replaceStringFromIDs(@NotNull String val) { + final String replace = idReplacements.get(val); + return replace; + } public boolean replaceStringFromIDs(@NotNull CompoundTag tag, @NotNull String key) { if (!tag.contains(key)) return false; - + final String val = tag.getString(key); final String replace = idReplacements.get(val); @@ -89,6 +103,64 @@ public class MigrationProfile { return false; } + + private boolean replaceIDatPath(@NotNull ListTag list, @NotNull String[] parts, int level){ + boolean[] changed = {false}; + if (level == parts.length-1) { + DataFixerAPI.fixItemArrayWithID(list, changed, this, true); + } else { + list.forEach(inTag -> changed[0] |= replaceIDatPath((CompoundTag)inTag, parts, level+1)); + } + return changed[0]; + } + + private boolean replaceIDatPath(@NotNull CompoundTag tag, @NotNull String[] parts, int level){ + boolean changed = false; + for (int i=level; i0) { + final String key = parts[parts.length-1]; + final byte type = tag.getTagType(key); + if (type == Tag.TAG_LIST) { + final ListTag list = tag.getList(key, Tag.TAG_COMPOUND); + final boolean[] _changed = {false}; + if (list.size()==0) { + _changed[0] = DataFixerAPI.fixStringIDList(tag, key, this); + } else { + DataFixerAPI.fixItemArrayWithID(list, _changed, this, true); + } + return _changed[0]; + } else if (type == Tag.TAG_STRING) { + return replaceStringFromIDs(tag, key); + } else if (type == Tag.TAG_COMPOUND) { + final CompoundTag cTag = tag.getCompound(key); + boolean[] _changed = {false}; + DataFixerAPI.fixID(cTag, _changed, this, true); + return _changed[0]; + } + } + + + return false; + } + + public boolean replaceIDatPath(@NotNull CompoundTag root, @NotNull String path){ + String[] parts = path.split("\\."); + return replaceIDatPath(root, parts, 0); + } public boolean patchLevelDat(@NotNull CompoundTag level) throws PatchDidiFailException { boolean changed = false; @@ -106,5 +178,17 @@ public class MigrationProfile { WorldDataAPI.saveFile(patch.modID); } } + + for (Map.Entry> entry : worldDataIDPaths.entrySet()){ + CompoundTag root = WorldDataAPI.getRootTag(entry.getKey()); + boolean[] changed = {false}; + entry.getValue().forEach(path -> { + changed[0] |= replaceIDatPath(root, path); + }); + + if (changed[0]){ + WorldDataAPI.saveFile(entry.getKey()); + } + } } } diff --git a/src/main/java/ru/bclib/api/datafixer/Patch.java b/src/main/java/ru/bclib/api/datafixer/Patch.java index e687f3a6..24d4d83e 100644 --- a/src/main/java/ru/bclib/api/datafixer/Patch.java +++ b/src/main/java/ru/bclib/api/datafixer/Patch.java @@ -156,5 +156,40 @@ public abstract class Patch { static MigrationProfile createMigrationData(CompoundTag config) { return new MigrationProfile(config); } + + /** + * Returns a list of paths,where your mod may IDs in your {@link ru.bclib.api.WorldDataAPI}-File. + *

+ * {@link DataFixerAPI} will use information from the latest patch that returns a non-null-result. This list is used + * to automatically fix changed IDs from all active patches (see {@link Patch#getIDReplacements()} + *

+ * The end of the path can either be a {@link net.minecraft.nbt.StringTag}, a {@link net.minecraft.nbt.ListTag} or + * a {@link CompoundTag}. If the Path contains a non-leaf {@link net.minecraft.nbt.ListTag}, all members of that + * list will be processed. For example: + *

+	 *     - global +
+	 *              | - key (String)
+	 *              | - items (List) +
+	 *                               | - { id (String) }
+	 *                               | - { id (String) }
+	 * 
+ * The path global.items.id will fix all id-entries in the items-list, while the path + * global.key will only fix the key-entry. + *

+ * if the leaf-entry (= the last part of the path, which would be items in global.items) is a + * {@link CompoundTag}, the system will fix any id entry. If the {@link CompoundTag} contains an item + * or tag.BlockEntityTag entry, the system will recursivley continue with those. If an items + * or inventory-{@link net.minecraft.nbt.ListTag} was found, the system will continue recursivley with + * every item of that list. + *

+ * if the leaf-entry is a {@link net.minecraft.nbt.ListTag}, it is handle the same as a child items entry + * of a {@link CompoundTag}. + * + * @return {@code null} if nothing changes or a list of Paths in your {@link ru.bclib.api.WorldDataAPI}-File. + * Paths are dot-seperated (see {@link ru.bclib.api.WorldDataAPI#getCompoundTag(String, String)}). + */ + public List getWorldDataIDPaths() { + return null; + } }