diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/ForgeRegistryMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/ForgeRegistryMixin.java new file mode 100644 index 00000000..c1c2f55a --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/ForgeRegistryMixin.java @@ -0,0 +1,110 @@ +package com.nomiceu.nomilabs.mixin; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.registries.IForgeRegistryInternal; +import net.minecraftforge.registries.IForgeRegistryModifiable; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import com.google.common.collect.Maps; +import com.llamalad7.mixinextras.sugar.Local; +import com.nomiceu.nomilabs.NomiLabs; +import com.nomiceu.nomilabs.mixinhelper.RemappableForgeRegistry; +import com.nomiceu.nomilabs.mixinhelper.RemappableSnapshot; + +/** + * This mixin saves a new map in Forge Registries: Remapped.
+ * This is a map of remapped block ids to their new resource location, as specified by a remapper.
+ * Note that this is only applied when the new resource location cannot be registered at the old id, as in that case, no + * saving of ids is required, as id loading will already load the new resource location.
+ *

+ * This mixin also fixes blocked lists not syncing between Forge Registries. + */ +@Mixin(value = ForgeRegistry.class, remap = false) +public abstract class ForgeRegistryMixin> + implements IForgeRegistryInternal, IForgeRegistryModifiable, + RemappableForgeRegistry { + + @Shadow + abstract void block(int id); + + @Shadow + @Final + private Set blocked; + + @Unique + private final Map remapped = Maps.newHashMap(); + + @Inject(method = "processMissingEvent", + at = @At(value = "INVOKE", + target = "Lnet/minecraftforge/registries/ForgeRegistry;addAlias(Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/util/ResourceLocation;)V"), + require = 1, + locals = LocalCapture.CAPTURE_FAILEXCEPTION) + public void handleRemaps(ResourceLocation name, ForgeRegistry pool, + List> mappings, + Map missing, Map remaps, + Collection defaulted, Collection failed, + boolean injectNetworkDummies, CallbackInfo ci, + @Local RegistryEvent.MissingMappings.Mapping remap, + @Local(ordinal = 2) int realId) { + if (remap.id != realId) { + block(remap.id); + remapped.put(remap.id, remap.getTarget().getRegistryName()); + NomiLabs.LOGGER.warn( + "[Forge Registry] Remap could not assign Id {} for Object {}! If this is of type BLOCK, without Data Fixers, after initial load, blocks will no longer be remapped!", + remap.id, remap.getTarget().getRegistryName()); + } + } + + @Inject(method = "sync", at = @At("RETURN")) + void syncBlockedRemapped(ResourceLocation name, ForgeRegistry from, CallbackInfo ci) { + blocked.clear(); + + var remFrom = (RemappableForgeRegistry) from; + + remFrom.getBlocked().forEach(this::block); + + remapped.clear(); + remFrom.getRemapped().forEach(this::addRemapped); + } + + @Inject(method = "makeSnapshot", at = @At("RETURN")) + public void addRemappedToSnapshot(CallbackInfoReturnable cir) { + ForgeRegistry.Snapshot ret = cir.getReturnValue(); + ((RemappableSnapshot) ret).addAllRemapped(remapped); + } + + @Override + @Unique + public void addRemapped(int id, ResourceLocation key) { + remapped.put(id, key); + } + + @Override + @Unique + public Map getRemapped() { + return remapped; + } + + @Override + @Unique + public Set getBlocked() { + return blocked; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/GameDataMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/GameDataMixin.java new file mode 100644 index 00000000..a0868838 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/GameDataMixin.java @@ -0,0 +1,38 @@ +package com.nomiceu.nomilabs.mixin; + +import java.util.Map; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.GameData; +import net.minecraftforge.registries.RegistryManager; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import com.llamalad7.mixinextras.sugar.Local; +import com.nomiceu.nomilabs.mixinhelper.RemappableForgeRegistry; +import com.nomiceu.nomilabs.mixinhelper.RemappableSnapshot; + +/** + * This Mixin allows for the remapped and block lists to be loaded from the in-world-save. + */ +@Mixin(value = GameData.class, remap = false) +public class GameDataMixin { + + @Inject(method = "loadPersistentDataToStagingRegistry", + at = @At(value = "INVOKE", + target = "Lnet/minecraftforge/registries/ForgeRegistry;loadIds(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Lnet/minecraftforge/registries/ForgeRegistry;Lnet/minecraft/util/ResourceLocation;)V"), + require = 1, + locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private static void loadRemappedToRegistry(RegistryManager pool, RegistryManager to, + Map remaps, + Map missing, ResourceLocation name, + ForgeRegistry.Snapshot snap, Class regType, CallbackInfo ci, + @Local(ordinal = 1) ForgeRegistry newRegistry) { + ((RemappableSnapshot) snap).loadToRegistry((RemappableForgeRegistry) newRegistry); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/SnapshotMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/SnapshotMixin.java new file mode 100644 index 00000000..6fbaca01 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/SnapshotMixin.java @@ -0,0 +1,73 @@ +package com.nomiceu.nomilabs.mixin; + +import java.util.Map; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.registries.ForgeRegistry; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.google.common.collect.Maps; +import com.nomiceu.nomilabs.mixinhelper.RemappableForgeRegistry; +import com.nomiceu.nomilabs.mixinhelper.RemappableSnapshot; + +/** + * This mixin allows for saving of Forge Registry Remappings to snapshots. + */ +@Mixin(value = ForgeRegistry.Snapshot.class, remap = false) +public class SnapshotMixin implements RemappableSnapshot { + + @Unique + public Map remapped = Maps.newHashMap(); + + @Unique + private static final String REMAPPED_KEY = "remapped"; + + @Inject(method = "write", at = @At("RETURN")) + public void saveRemapped(CallbackInfoReturnable cir) { + NBTTagCompound data = cir.getReturnValue(); + NBTTagList remapList = new NBTTagList(); + remapped.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> { + NBTTagCompound tag = new NBTTagCompound(); + tag.setInteger("K", e.getKey()); + tag.setString("V", e.getValue().toString()); + remapList.appendTag(tag); + }); + data.setTag(REMAPPED_KEY, remapList); + } + + @Inject(method = "read", at = @At("RETURN")) + private static void readRemapped(NBTTagCompound nbt, CallbackInfoReturnable cir) { + ForgeRegistry.Snapshot ret = cir.getReturnValue(); + NBTTagList list = nbt.getTagList(REMAPPED_KEY, Constants.NBT.TAG_COMPOUND); + list.forEach(e -> { + NBTTagCompound comp = (NBTTagCompound) e; + ((RemappableSnapshot) ret).addRemapped(comp.getInteger("K"), new ResourceLocation(comp.getString("V"))); + }); + } + + @Override + @Unique + public void addRemapped(int id, ResourceLocation key) { + remapped.put(id, key); + } + + @Override + @Unique + public void addAllRemapped(Map map) { + remapped.putAll(map); + } + + @Override + @Unique + public void loadToRegistry(RemappableForgeRegistry reg) { + remapped.forEach(reg::addRemapped); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixinhelper/RemappableForgeRegistry.java b/src/main/java/com/nomiceu/nomilabs/mixinhelper/RemappableForgeRegistry.java new file mode 100644 index 00000000..da57aa92 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixinhelper/RemappableForgeRegistry.java @@ -0,0 +1,15 @@ +package com.nomiceu.nomilabs.mixinhelper; + +import java.util.Map; +import java.util.Set; + +import net.minecraft.util.ResourceLocation; + +public interface RemappableForgeRegistry { + + void addRemapped(int id, ResourceLocation key); + + Set getBlocked(); + + Map getRemapped(); +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixinhelper/RemappableSnapshot.java b/src/main/java/com/nomiceu/nomilabs/mixinhelper/RemappableSnapshot.java new file mode 100644 index 00000000..9a5bed10 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixinhelper/RemappableSnapshot.java @@ -0,0 +1,14 @@ +package com.nomiceu.nomilabs.mixinhelper; + +import java.util.Map; + +import net.minecraft.util.ResourceLocation; + +public interface RemappableSnapshot { + + void addRemapped(int id, ResourceLocation key); + + void addAllRemapped(Map map); + + void loadToRegistry(RemappableForgeRegistry reg); +} diff --git a/src/main/java/com/nomiceu/nomilabs/remap/LabsRemapHelper.java b/src/main/java/com/nomiceu/nomilabs/remap/LabsRemapHelper.java index 8ee50192..a7ab1127 100644 --- a/src/main/java/com/nomiceu/nomilabs/remap/LabsRemapHelper.java +++ b/src/main/java/com/nomiceu/nomilabs/remap/LabsRemapHelper.java @@ -100,6 +100,11 @@ private static NBTTagCompound rewriteBlocksInSection(NBTTagCompound chunkSection new NibbleArray(chunkSectionTag.getByteArray("Add")) : null; for (int i = 0; i < 4096; ++i) { int x = i & 0x0F, y = i >> 8 & 0x0F, z = i >> 4 & 0x0F; + + // This is based off BlockStateContainer's setDataFromNBT + // There, the block id is shifted by 4, and extended is shifted by 12 + // However, that is to allow the accommodation of 4 bits of metadata info + // Thus, here, the block id is not shifted, and extended is only shifted by 8. int id = extendedIds == null ? (blockIds[i] & 0xFF) : ((blockIds[i] & 0xFF) | (extendedIds.get(x, y, z) << 8)); var state = new BlockStateLike(id, (short) blockMetadata.get(x, y, z), diff --git a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java index fc0c5220..2ceef0cb 100644 --- a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java +++ b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java @@ -26,13 +26,13 @@ import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.registry.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistry; -import net.minecraftforge.registries.GameData; import org.apache.commons.lang3.StringUtils; import org.apache.groovy.util.Arrays; import com.nomiceu.nomilabs.LabsValues; import com.nomiceu.nomilabs.NomiLabs; +import com.nomiceu.nomilabs.mixinhelper.RemappableForgeRegistry; import com.nomiceu.nomilabs.remap.LabsRemapHelper; import com.nomiceu.nomilabs.remap.datafixer.fixes.BlockFixer; import com.nomiceu.nomilabs.remap.datafixer.fixes.ItemFixer; @@ -67,8 +67,11 @@ public class DataFixerHandler { private static String savedLabsVersion; - /* Must be split up so that idToBlockMap is the old one (so we can use not registered resource locations) */ - private static NBTTagList oldBlockRegistry; + /* + * Must be split up so that idToBlockMap has remapped info + * (Remapped Info from ForgeRegistry & Snapshot & Game Data Mixins) + * Remapped contains the old id mapped to the new resource location (as specified by LabsRemappers) + */ private static Map idToBlockMap; private static Map blockToIdMap; @@ -97,11 +100,9 @@ public static void onWorldLoad(SaveHandler save) { neededNewFixes = null; savedLabsVersion = null; fixAvailable = true; - - // Clear Block Helper Maps, the ids can be different for each save idToBlockMap = null; blockToIdMap = null; - oldBlockRegistry = null; + NomiLabs.LOGGER.info("Checking Data Fixers..."); getInfoFromSave(save); @@ -183,18 +184,9 @@ private static void getInfoFromSave(SaveHandler save) { continue; savedLabsVersion = compound.getString("ModVersion"); - break; + return; } } - - if (!fml.hasKey("Registries", Constants.NBT.TAG_COMPOUND)) return; - NBTTagCompound registries = fml.getCompoundTag("Registries"); - - if (!registries.hasKey(GameData.BLOCKS.toString(), Constants.NBT.TAG_COMPOUND)) return; - NBTTagCompound blocks = registries.getCompoundTag(GameData.BLOCKS.toString()); - - if (!blocks.hasKey("ids", Constants.NBT.TAG_LIST)) return; - oldBlockRegistry = blocks.getTagList("ids", Constants.NBT.TAG_COMPOUND); } catch (IOException e) { NomiLabs.LOGGER.fatal("Failed to read level.dat.", e); } @@ -229,24 +221,22 @@ private static void determineNeededFixesAndLog() { } /** - * The Id To Block Map is either loaded from the save's existing registry map, or is produced once needed, - * instead of in World Load or Post Init.
+ * The Id to Block Map also includes Remapped IDs of Blocks.
* This means they are loaded after block id mismatch fixes, so the ids are correct to the world. */ public static Map getIdToBlockMap() { if (idToBlockMap != null) return idToBlockMap; - if (oldBlockRegistry == null || oldBlockRegistry.isEmpty()) { - ForgeRegistry registry = (ForgeRegistry) ForgeRegistries.BLOCKS; - idToBlockMap = registry.getKeys().stream() - .collect(Collectors.toMap(registry::getID, Function.identity())); - NomiLabs.LOGGER.error( - "Block Registry Save in level.dat was not found. Defaulting to Current Registry, some Remaps may not work correctly!"); - } else { - idToBlockMap = oldBlockRegistry.tagList.stream() - .map((tag) -> (NBTTagCompound) tag) - .collect(Collectors.toMap((tag) -> tag.getInteger("V"), - (tag) -> new ResourceLocation(tag.getString("K")))); + ForgeRegistry registry = (ForgeRegistry) ForgeRegistries.BLOCKS; + idToBlockMap = registry.getKeys().stream() + .collect(Collectors.toMap(registry::getID, Function.identity())); + var remReg = (RemappableForgeRegistry) registry; + if (!remReg.getRemapped().isEmpty()) { + NomiLabs.LOGGER.debug("Map Before Adding Remapped IDs:"); + NomiLabs.LOGGER.debug(idToBlockMap); + NomiLabs.LOGGER.debug("Adding Block Remapped IDs:"); + NomiLabs.LOGGER.debug(remReg.getRemapped()); + idToBlockMap.putAll(remReg.getRemapped()); } NomiLabs.LOGGER.debug("Generated Id to Block Map!"); @@ -311,6 +301,5 @@ public static void close() { neededNewFixes = null; idToBlockMap = null; blockToIdMap = null; - oldBlockRegistry = null; } } diff --git a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java index 999b18b1..b46dafad 100644 --- a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java +++ b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java @@ -159,10 +159,6 @@ public static void init() { * // Note that this is not included in the fix list if the previous version is equal to the current overall fix * version. * - * (modList) -> true, // Inputs the previous modlist the world was loaded with (map of modid to modversion), - * return whether it is valid. - * // Note that the fix is only applied if the version AND the modlist is valid. - * * (stack) -> stack.rl.equals(new ResourceLocation("minecraft:apple")), // Input ItemStackLike, return a boolean * (true to fix, false to skip) * @@ -293,7 +289,7 @@ public static void init() { * * Example of an input: * BlockStateLike: - * rl: "minecraft:concrete" // Type: Resource Location. Not Null. Can not be registered. + * rl: "minecraft:concrete" // Type: Resource Location. Not Null. Note that Remappers are Applied before Fixes! * meta: 1 // Type: Short * * Example: @@ -309,10 +305,6 @@ public static void init() { * // Note that this is not included in the fix list if the previous * // version is equal to the current overall fix version. * - * (modList) -> true, // Inputs the previous modlist the world was loaded with (map of modid to modversion), - * // return whether it is valid. - * // Note that the fix is only applied if the version AND the modlist is valid. - * * false, // Whether the tile entity tag is needed. If yes, it is accessible via state.setTileEntityTag() and * // state.tileEntityTag. If this is false, that field will be null. * @@ -358,14 +350,19 @@ public static void init() { false, (version) -> version <= PRE_AE2_STUFF_REMAP || version == NEW, true, - (state) -> state.rl.getNamespace().equals(AE2_STUFF_MODID) && - state.rl.getPath().equals("encoder"), + (state) -> state.rl.getNamespace().equals(AE2_MODID) && + state.rl.getPath().equals("interface") && state.meta != 0, // Apply if meta is not + // 0, interface can only + // be meta of 0 // Always apply regardless of TE Tag null, (state) -> { - state.setRl(new ResourceLocation(AE2_MODID, "interface")); state.setMeta((short) 0); - if (state.tileEntityTag == null) return; + + if (state.tileEntityTag == null || + !state.tileEntityTag.getString("id") + .equals(new ResourceLocation(AE2_STUFF_MODID, "encoder").toString())) + return; state.tileEntityTag.setString("id", new ResourceLocation(AE2_MODID, "interface").toString()); @@ -454,11 +451,6 @@ public static void init() { * // Note that this is not included in the fix list if the previous version is equal to the * // current overall fix version. * - * (modList) -> true, - * // Inputs the previous modlist the world was loaded with (map of modid to modversion), return whether it is - * valid. - * // Note that the fix is only applied if the version AND the modlist is valid. - * * // Input NBT Tag Compound, return a boolean (true to fix, false to skip) * (compound) -> compound.hasKey("id", Constants.NBT.TAG_STRING) && * compound.getString("id").equals(new ResourceLocation("minecraft:chest").toString()) && diff --git a/src/main/resources/mixins.nomilabs.json b/src/main/resources/mixins.nomilabs.json index f1a97a6d..6b6b2910 100644 --- a/src/main/resources/mixins.nomilabs.json +++ b/src/main/resources/mixins.nomilabs.json @@ -8,8 +8,11 @@ "AccessibleFluidRegistry", "CommandDifficultyMixin", "FluidRegistryMixin", + "ForgeRegistryMixin", + "GameDataMixin", "ItemStackMixin", "NarratorMixin", + "SnapshotMixin", "WorldLoadHandler" ], "client": [