diff --git a/src/main/java/com/gtnewhorizons/postea/Postea.java b/src/main/java/com/gtnewhorizons/postea/Postea.java index 42de5e7..50d3877 100644 --- a/src/main/java/com/gtnewhorizons/postea/Postea.java +++ b/src/main/java/com/gtnewhorizons/postea/Postea.java @@ -27,7 +27,7 @@ public class Postea { @Mod.EventHandler public void onIdMappingsChanged(FMLModIdMappingEvent event) { - TransformerRegistry.onIdMappingsChanged(); + TransformerRegistry.onIdMappingsChanged(event); } @Mod.EventHandler diff --git a/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java b/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java index 47b3169..18ed85e 100644 --- a/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java +++ b/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java @@ -1,6 +1,7 @@ package com.gtnewhorizons.postea.api; import java.util.function.BiFunction; +import java.util.function.Function; import javax.annotation.Nonnull; @@ -17,12 +18,63 @@ public class BlockReplacementManager { /** * Adds a custom transformer for a given id. * + * @deprecated Use {@link #addTransformationHandler(String, Function)} + * + * @apiNote When the transformer function passed to this function returns a null, value Postea will assume that + * the transformer has failed to identify and run the handler on any other handler function registered + * to the given ID. + *
+ * If you need to perform a simple transformations consider using the following methods instead: + * + * + * @implNote For performance reasons, Postea assumes that the BlockConversionInfo that was passed to the handler + * hasn't been modified if you return a null value. This isn't enforced though since returning a null + * value after doing some modifications has a couple niche uses. eg: having separate handlers for + * different migrations of a single thing that can be sunset at different times down the line. + * + * @param originalId The id of the block to transform. + * @param transformer The transformer to apply. + */ + @Deprecated + @SuppressWarnings("unused") + public static void addBlockReplacement(@Nonnull String originalId, + @Nonnull BiFunction transformer) { + addTransformationHandler(originalId, info -> transformer.apply(info, info.world) != null); + } + + /** + * Adds a custom transformer for a given id. + * + * @apiNote When the transformer function passed to this function returns a null, value Postea will assume that + * the transformer has failed to identify and run the handler on any other handler function registered + * to the given ID. + *
+ * If you need to perform a simple transformations consider using the following methods instead: + * + * + * @implNote For performance reasons, Postea assumes that the BlockConversionInfo that was passed to the handler + * hasn't been modified if your handler return false. This isn't enforced though since returning false + * after doing some modifications has a couple niche uses. eg: having separate handlers for different + * migrations of a single thing that can be sunset at different times down the line. + * * @param originalId The id of the block to transform. * @param transformer The transformer to apply. */ @SuppressWarnings("unused") - public static void addBlockReplacement(@Nonnull String originalId, - @Nonnull BiFunction transformer) { + public static void addTransformationHandler(@Nonnull String originalId, + @Nonnull Function transformer) { if (originalId == null) throw new IllegalArgumentException("original id is null"); if (transformer == null) throw new IllegalArgumentException("transformer is null"); TransformerRegistry.addBlockReplacement(originalId, transformer); @@ -31,7 +83,7 @@ public class BlockReplacementManager { /** * Adds a mapping to remap stacks and block instances of a specific removed block to the given block whilst, whilst * maintaining its meta value. - *

+ *
* IMPORTANT: This only works if the world does not have an existing id set for the target block. If you need * to transform a missing block into something that was previously given an id (e.g.: Transforming a removed block * into dirt), use {@link #addBlockReplacement(String, Block)} with both meta parameters set to @@ -49,7 +101,7 @@ public class BlockReplacementManager { /** * Suppresses any missing mapping warning for the given id. - * + * * @param originalId The id of the block or item to suppress the warning for. */ @SuppressWarnings("unused") @@ -97,6 +149,36 @@ public class BlockReplacementManager { skipStackRemap); } + // block+meta -> block + + /** + * Adds a mapping to remap all stacks and block instances of a given block and damage/meta value to another block, + * whilst maintaining its meta value. This also adds a matching item stack replacement. + * + * @param originalId The id of the block to remap. + * @param originalMeta The meta of the block to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. + * @param block The block to remap to. + */ + @SuppressWarnings("unused") + public static void addBlockReplacement(@Nonnull String originalId, int originalMeta, @Nonnull Block block) { + addBlockReplacement(originalId, originalMeta, block, OreDictionary.WILDCARD_VALUE, false); + } + + /** + * Adds a mapping to remap all stacks and block instances of a given block and damage/meta value to another block,, + * whilst maintaining its meta value. + * + * @param originalId The id of the block to remap. + * @param originalMeta The meta of the block to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. + * @param block The block to remap to. + * @param skipStackRemap Set to true to skip auto-adding an item stack remapper. + */ + @SuppressWarnings("unused") + public static void addBlockReplacement(@Nonnull String originalId, int originalMeta, @Nonnull Block block, + boolean skipStackRemap) { + addBlockReplacement(originalId, originalMeta, block, OreDictionary.WILDCARD_VALUE, skipStackRemap); + } + // block -> block+meta /** diff --git a/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java b/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java index c971944..39ddc93 100644 --- a/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java +++ b/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java @@ -3,6 +3,7 @@ package com.gtnewhorizons.postea.api; import java.util.function.Function; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import net.minecraft.init.Items; import net.minecraft.item.Item; @@ -17,15 +18,159 @@ public class ItemStackReplacementManager { // Public API for converting ItemStacks. + // spotless:off + /** + * Fetches the original registration name of a given item id. Use this to get the actual name of an item when migrating + * an item that no longer exists. + * + * @apiNote This only works for items that have been registered for Postea transformations, and should not be used + * outside a Postea transformer function. + *

{@code
+     *          public static class Example {
+     *              public static void postLoad() {
+     *                  ItemStackReplacementManager.addTransformationHandler("removedmod:item1", Example::transform);
+     *                  ItemStackReplacementManager.addTransformationHandler("removedmod:item2", Example::transform);
+     *                  ItemStackReplacementManager.addTransformationHandler("removedmod:item3", Example::transform);
+     *              }
+     *
+     *              public static boolean transform(NBTTagCompound tag) {
+     *                  // consider using the other addItemReplacement functions without a function
+     *                  // They create very fast transformers that run in constant time
+     *                  // regardless of how many transformations you register to a single id
+     *                  short meta = tag.getShort("Damage");
+     *                  switch (ItemStackReplacementManager.getOriginalItemName(tag)) {
+     *                      int meta = 0;
+     *                      case "removedmod:item1":
+     *                          if (meta != 0) return false;
+     *                          meta = 1;
+     *                          break;
+     *                      case "removedmod:item2":
+     *                          meta = 2;
+     *                          break;
+     *                  }
+     *                  IDExtenderCompat.setItemStackID(tag, Items.getIdFromItem(MyItems.item1));
+     *                  tag.setShort("Damage", meta);
+     *                  return true;
+     *              }
+     *          }
+     *          }
+     *
+     * @param tag The NBT tag that was passed to your handler.
+     * @return The original namespaced name of the item being transformed.
+     */
+    // spotless:on
+    @SuppressWarnings("unused")
+    public static @Nullable String getOriginalItemName(NBTTagCompound tag) {
+        return TransformerRegistry.getMappedItemName(IDExtenderCompat.getItemStackID(tag));
+    }
+
+    // spotless:off
+    /**
+     * Fetches the original registration name of a given item id. Use this to get the actual name of an item when
+     * migrating an item that no longer exists.
+     *
+     * @apiNote This only works for items that have been registered for Postea transformations, and should not be used
+     *          outside a Postea transformer function.
+     *          
{@code
+     *          public static class Example {
+     *              public static void postLoad() {
+     *                  ItemStackReplacementManager.addTransformationHandler("removedmod:item1", Example::transform);
+     *                  ItemStackReplacementManager.addTransformationHandler("removedmod:item2", Example::transform);
+     *                  ItemStackReplacementManager.addTransformationHandler("removedmod:item3", Example::transform);
+     *              }
+     *
+     *              public static boolean transform(NBTTagCompound tag) {
+     *                  // consider using the other addItemReplacement functions without a function
+     *                  // They create very fast transformers that run in constant time
+     *                  // regardless of how many transformations you register to a single id
+     *                  short meta = tag.getShort("Damage");
+     *                  int id = IDExtenderCompat.getItemStackID(tag);
+     *                  switch (ItemStackReplacementManager.getOriginalItemName(id)) {
+     *                      int meta = 0;
+     *                      case "removedmod:item1":
+     *                          if (meta != 0) return false;
+     *                          meta = 1;
+     *                          break;
+     *                      case "removedmod:item2":
+     *                          meta = 2;
+     *                          break;
+     *                  }
+     *                  IDExtenderCompat.setItemStackID(tag, Items.getIdFromItem(MyItems.item1));
+     *                  tag.setShort("Damage", meta);
+     *                  return true;
+     *              }
+     *          }
+     *          }
+     *
+     * @param itemId The numeric id of the item that was passed to your transformation handler.
+     * @return The original namespaced name of the item being transformed.
+     */
+    // spotless:on
+    @SuppressWarnings("unused")
+    public static @Nullable String getOriginalItemName(int itemId) {
+        return TransformerRegistry.getMappedItemName(itemId);
+    }
+
     /**
      * Adds a custom transformer for a given id.
      *
+     * @deprecated Use {@link #addTransformationHandler(String, Function)} instead.
+     *
+     * @apiNote When the transformer function passed to this function returns a null, value Postea will assume that
+     *          the transformer has failed to identify and run the handler on any other handler function registered
+     *          to the given ID.
+     *          

+ * If you need to perform a simple transformations consider using the following methods instead: + *

    + *
  • item -> item (with same meta): {@link #addItemReplacement(String, Item)}
  • + *
  • item -> item with specific meta: {@link #addItemReplacement(String, Item, int)}
  • + *
  • item with meta -> item (with same meta): {@link #addItemReplacement(String, int, Item)}
  • + *
  • item with meta -> item with specific meta: {@link #addItemReplacement(String, int, Item, int)}
  • + *
+ * + * @implNote For performance reasons, Postea assumes that the tag that was passed to the handler hasn't been + * modified + * if you return a null value. This isn't enforced though since returning a null value after doing some + * modifications has a couple niche uses. eg: having separate handlers for different migrations of a + * single thing that can be sunset at different times down the line. + * + * @param originalId The id of the item to transform. + * @param transformer The transformer to apply. + */ + @Deprecated + @SuppressWarnings("unused") + public static void addItemReplacement(@Nonnull String originalId, + @Nonnull Function transformer) { + addTransformationHandler(originalId, tag -> transformer.apply(tag) != null); + } + + /** + * Adds a custom transformer for a given id. + * + * @apiNote When the transformer function passed to this function returns a null, value Postea will assume that + * the transformer has failed to identify and run the handler on any other handler function registered + * to the given ID. + *

+ * If you need to perform a simple transformations consider using the following methods instead: + *

    + *
  • item -> item (with same meta): {@link #addItemReplacement(String, Item)}
  • + *
  • item -> item with specific meta: {@link #addItemReplacement(String, Item, int)}
  • + *
  • item with meta -> item (with same meta): {@link #addItemReplacement(String, int, Item)}
  • + *
  • item with meta -> item with specific meta: {@link #addItemReplacement(String, int, Item, int)}
  • + *
+ * + * @implNote For performance reasons, Postea assumes that the tag that was passed to the handler hasn't been + * modified + * if you return a null value. This isn't enforced though since returning a null value after doing some + * modifications has a couple niche uses. eg: having separate handlers for different migrations of a + * single thing that can be sunset at different times down the line. + * * @param originalId The id of the item to transform. * @param transformer The transformer to apply. */ @SuppressWarnings("unused") - public static void addItemReplacement(@Nonnull String originalId, - @Nonnull Function transformer) { + public static void addTransformationHandler(@Nonnull String originalId, + @Nonnull Function transformer) { if (originalId == null) throw new IllegalArgumentException("original id is null"); if (transformer == null) throw new IllegalArgumentException("transformer is null"); TransformerRegistry.addItemReplacement(originalId, transformer); @@ -51,7 +196,7 @@ public class ItemStackReplacementManager { /** * Suppresses any missing mapping warning for the given id. - * + * * @param originalId The id of the block or item to suppress the warning for. */ @SuppressWarnings("unused") @@ -69,6 +214,9 @@ public class ItemStackReplacementManager { * mappings, * (e.g.: Transforming a removed item into a stick) * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param item The item to remap to. */ @@ -84,6 +232,9 @@ public class ItemStackReplacementManager { * mappings, * (e.g.: Transforming a removed item into a stick) * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param item The item to remap to. * @param skipBlockRemap Set to true to skip auto-adding a block remapper if the item happens to be an ItemBlock. @@ -104,6 +255,9 @@ public class ItemStackReplacementManager { * Adds a mapping to remap stacks of a given item to another item with a given damage/meta value. This also adds a * matching block replacement if this item happens to be an ItemBlock. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param item The item to remap to. * @param newMeta The meta of the item to remap to. OreDictionary.WILDCARD_VALUE maintains the existing value. @@ -116,6 +270,9 @@ public class ItemStackReplacementManager { /** * Adds a mapping to remap stacks of a given item to another item with a given damage/meta value. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param item The item to remap to. * @param newMeta The meta of the item to remap to. OreDictionary.WILDCARD_VALUE maintains the existing @@ -134,6 +291,9 @@ public class ItemStackReplacementManager { * Adds a mapping to remap stacks of a given item to another item with a given damage/meta value. This also adds a * matching block replacement if this item happens to be an ItemBlock. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param stack The item to remap to. */ @@ -150,6 +310,9 @@ public class ItemStackReplacementManager { /** * Adds a mapping to remap stacks of a given item to another item with a given damage/meta value. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param stack A stack of the item to replace to. * @param skipBlockRemap Set to true to skip auto-adding a block remapper if the item happens to be an ItemBlock. @@ -165,12 +328,51 @@ public class ItemStackReplacementManager { skipBlockRemap); } + // item+meta -> item + + /** + * Adds a mapping to remap all stacks of a given item and damage/meta value to another item, whilst maintaining its + * damage/meta value. This also adds a matching block replacement if this item happens to be an ItemBlock. + * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * + * @param originalId The id of the item to remap. + * @param originalMeta The meta of the item to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. + * @param item The item to remap to. + */ + @SuppressWarnings("unused") + public static void addItemReplacement(@Nonnull String originalId, int originalMeta, @Nonnull Item item) { + addItemReplacement(originalId, originalMeta, item, OreDictionary.WILDCARD_VALUE, false); + } + + /** + * Adds a mapping to remap all stacks of a given item and damage/meta value to another item, whilst maintaining its + * damage/meta value. + * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * + * @param originalId The id of the item to remap. + * @param originalMeta The meta of the item to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. + * @param item The item to remap to. + * @param skipBlockRemap Set to true to skip auto-adding a item remapper if the . + */ + @SuppressWarnings("unused") + public static void addItemReplacement(@Nonnull String originalId, int originalMeta, @Nonnull Item item, + boolean skipBlockRemap) { + addItemReplacement(originalId, originalMeta, item, OreDictionary.WILDCARD_VALUE, skipBlockRemap); + } + // item+meta -> stack /** * Adds a mapping to remap all stacks of a given item and damage/meta value to another item with a * given damage/meta value. This also adds a matching block replacement if this item happens to be an ItemBlock. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param originalMeta The meta of the item to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. * @param stack A stack of the item to replace to. @@ -184,6 +386,9 @@ public class ItemStackReplacementManager { * Adds a mapping to remap all stacks of a given item and damage/meta value to another item with a * given damage/meta value. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param originalMeta The meta of the item to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. * @param stack A stack of the item to replace to. @@ -201,6 +406,9 @@ public class ItemStackReplacementManager { * Adds a mapping to remap all stacks of a given item and damage/meta value to another item with a * given damage/meta value. This also adds a matching block replacement if this item happens to be an ItemBlock. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param originalMeta The meta of the item to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. * @param item The item to remap to. @@ -216,6 +424,9 @@ public class ItemStackReplacementManager { * Adds a mapping to remap all stacks of a given item and damage/meta value to another item with a * given damage/meta value. * + * @implNote All transformations registered using this handler are executed in constant time regardless of how many + * transformations are registered to a single id. + * * @param originalId The id of the item to remap. * @param originalMeta The meta of the item to remap. OreDictionary.WILDCARD_VALUE acts as a wildcard. * @param item The item to remap to. @@ -224,7 +435,7 @@ public class ItemStackReplacementManager { * @param skipBlockRemap Set to true to skip auto-adding a item remapper if the . */ @SuppressWarnings("unused") - public static void addItemReplacement(String originalId, int originalMeta, Item item, int newMeta, + public static void addItemReplacement(@Nonnull String originalId, int originalMeta, @Nonnull Item item, int newMeta, boolean skipBlockRemap) { if (originalId == null) throw new IllegalArgumentException("original id is null"); if (item == null) throw new IllegalArgumentException("item is null"); diff --git a/src/main/java/com/gtnewhorizons/postea/mixins/early/MixinItemStack.java b/src/main/java/com/gtnewhorizons/postea/mixins/early/MixinItemStack.java index 7ca19d3..bf8a976 100644 --- a/src/main/java/com/gtnewhorizons/postea/mixins/early/MixinItemStack.java +++ b/src/main/java/com/gtnewhorizons/postea/mixins/early/MixinItemStack.java @@ -1,7 +1,5 @@ package com.gtnewhorizons.postea.mixins.early; -import static com.gtnewhorizons.postea.utility.ItemFixerUtility.fixItemStack; - import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -10,6 +8,8 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.gtnewhorizons.postea.utility.TransformerRegistry; + @Mixin(ItemStack.class) @SuppressWarnings("unused") public abstract class MixinItemStack { @@ -17,7 +17,7 @@ public abstract class MixinItemStack { @Inject(method = "readFromNBT(Lnet/minecraft/nbt/NBTTagCompound;)V", at = @At("HEAD")) @SuppressWarnings("unused") private void onReadFromNBTReturn(NBTTagCompound tag, CallbackInfo ci) { - fixItemStack(tag); + TransformerRegistry.transformItem(tag); } } diff --git a/src/main/java/com/gtnewhorizons/postea/utility/BlockConversionInfo.java b/src/main/java/com/gtnewhorizons/postea/utility/BlockConversionInfo.java index fecfd7c..2b27808 100644 --- a/src/main/java/com/gtnewhorizons/postea/utility/BlockConversionInfo.java +++ b/src/main/java/com/gtnewhorizons/postea/utility/BlockConversionInfo.java @@ -1,19 +1,24 @@ package com.gtnewhorizons.postea.utility; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; public final class BlockConversionInfo { - public int x; - public int y; - public int z; - + public final String blockName; public int blockID; public int metadata; + public final int x; + public final int y; + public final int z; + public final World world; - public World world; - public NBTTagCompound tileEntityNBT; - - public String blockName; + public BlockConversionInfo(String blockName, int blockID, int metadata, int x, int y, int z, World world) { + this.blockName = blockName; + this.blockID = blockID; + this.metadata = metadata; + this.x = x; + this.y = y; + this.z = z; + this.world = world; + } } diff --git a/src/main/java/com/gtnewhorizons/postea/utility/ChunkFixerUtility.java b/src/main/java/com/gtnewhorizons/postea/utility/ChunkFixerUtility.java index c65c92a..55dd554 100644 --- a/src/main/java/com/gtnewhorizons/postea/utility/ChunkFixerUtility.java +++ b/src/main/java/com/gtnewhorizons/postea/utility/ChunkFixerUtility.java @@ -1,13 +1,12 @@ package com.gtnewhorizons.postea.utility; import static com.gtnewhorizons.postea.utility.PosteaUtilities.getModListHash; +import static com.gtnewhorizons.postea.utility.TransformerRegistry.getBlockReplacement; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; -import net.minecraft.block.Block; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.World; @@ -20,7 +19,6 @@ import com.gtnewhorizons.postea.compat.Compat; import com.gtnewhorizons.postea.compat.SubChunkAccess; import akka.japi.Pair; -import cpw.mods.fml.common.registry.GameRegistry; public class ChunkFixerUtility { @@ -28,7 +26,6 @@ public class ChunkFixerUtility { public static final int POSTEA_UPDATE_CODE = getModListHash(); private static final int AIR_ID = 0; - private static final HashMap loadedBlocks = new HashMap<>(); public static void transformNormalBlocks(Chunk chunk, ExtendedBlockStorage ebs, World world) { @@ -47,36 +44,18 @@ public class ChunkFixerUtility { // Skip air. if (blockId == AIR_ID) continue; - // If this block has no registered Postea conversion, skip it. - if (TransformerRegistry.blockNotConvertible(blockId)) continue; - // Cache block names to improve performance, as findUniqueIdentifierFor is expensive. - Block block = Block.getBlockById(blockId); - String blockName = loadedBlocks.computeIfAbsent( - block, - b -> GameRegistry.findUniqueIdentifierFor(b) - .toString()); - - BlockConversionInfo blockConversionInfo = new BlockConversionInfo(); - blockConversionInfo.blockName = blockName; - blockConversionInfo.blockID = blockId; - blockConversionInfo.metadata = (byte) metadata; // Updated - - blockConversionInfo.world = world; - - blockConversionInfo.x = x + chunkXPos + 1; - blockConversionInfo.y = y + sectionY; - blockConversionInfo.z = z + chunkZPos + 1; - - BlockConversionInfo output = TransformerRegistry.getBlockReplacement(blockConversionInfo, world); + BlockConversionInfo output = getBlockReplacement( + blockId, + (byte) metadata, + world, + x + chunkXPos + 1, + y + sectionY, + z + chunkZPos + 1); if (output != null) { access.setBlockId(x, y, z, output.blockID); access.setMeta(x, y, z, output.metadata); - } else if (MissingMappingHandler.isDummyBlock(block)) { - // clear out the slot if this is an unhandled dummy item. - access.setBlockId(x, y, z, 0); - access.setMeta(x, y, z, 0); } } } diff --git a/src/main/java/com/gtnewhorizons/postea/utility/ItemFixerUtility.java b/src/main/java/com/gtnewhorizons/postea/utility/ItemFixerUtility.java deleted file mode 100644 index cd2f4ce..0000000 --- a/src/main/java/com/gtnewhorizons/postea/utility/ItemFixerUtility.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.gtnewhorizons.postea.utility; - -import java.util.Collection; -import java.util.function.Function; - -import net.minecraft.item.Item; -import net.minecraft.nbt.NBTTagCompound; - -import com.gtnewhorizons.postea.api.IDExtenderCompat; - -import cpw.mods.fml.common.registry.GameRegistry; - -public abstract class ItemFixerUtility { - - public static void fixItemStack(NBTTagCompound tag) { - - if (tag.hasNoTags()) return; - - if (tag.hasKey("id")) { - int id = IDExtenderCompat.getItemStackID(tag); - Item item = Item.getItemById(id); - if (item == null) return; - GameRegistry.UniqueIdentifier uuid = GameRegistry.findUniqueIdentifierFor(item); - String itemNameInternal = uuid.modId + ":" + uuid.name; - - // abort early if no handlers - Collection> handlers = TransformerRegistry - .getItemReplacement(itemNameInternal); - if (handlers.isEmpty()) return; - - // apply handlers - for (Function transformer : handlers) { - if (transformer.apply(tag) != null) { - return; - } - } - - // if no handler overwrote anything on this stack, and it's a dummy, delete the stack. - if (MissingMappingHandler.isDummyItem(item)) { - tag.removeTag("id"); - tag.removeTag("idExt"); - } - } - } -} diff --git a/src/main/java/com/gtnewhorizons/postea/utility/MissingMappingHandler.java b/src/main/java/com/gtnewhorizons/postea/utility/MissingMappingHandler.java index 3e23b0b..156d507 100644 --- a/src/main/java/com/gtnewhorizons/postea/utility/MissingMappingHandler.java +++ b/src/main/java/com/gtnewhorizons/postea/utility/MissingMappingHandler.java @@ -17,6 +17,7 @@ import com.gtnewhorizons.postea.compat.Compat; import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.registry.GameRegistry; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; public abstract class MissingMappingHandler { @@ -65,7 +66,10 @@ public abstract class MissingMappingHandler { private static final HashMap MISSING_ITEM_MAPPINGS = new HashMap<>(); private static final HashMap MISSING_BLOCK_MAPPINGS = new HashMap<>(); private static final HashMap DUMMY_TARGET_TYPES = new HashMap<>(); - private static final HashMap REVERSE_DUMMY_ID_LOOKUP = new HashMap<>(); + private static final HashMap DUMMY_ID_TO_ORIGINAL_ID_LOOKUP = new HashMap<>(); + private static final HashMap ORIGINAL_ID_TO_DUMMY_ID_LOOKUP = new HashMap<>(); + + private static final IntOpenHashSet DUMMY_IDS = new IntOpenHashSet(); private static final String DUMMY_ID_PREFIX_START = Postea.MODID + ":"; private static final String DUMMY_ID_PREFIX_END = "d."; @@ -96,7 +100,8 @@ public abstract class MissingMappingHandler { MISSING_ITEM_MAPPINGS.put(originalId, dummy); Compat.hideItemFromNEI(dummy); } - REVERSE_DUMMY_ID_LOOKUP.put(mapping.id, originalId); + DUMMY_ID_TO_ORIGINAL_ID_LOOKUP.put(mapping.id, originalId); + ORIGINAL_ID_TO_DUMMY_ID_LOOKUP.put(originalId, mapping.id); } // no longer needed free what ever used to be in there DUMMY_TARGET_TYPES.clear(); @@ -176,7 +181,11 @@ public abstract class MissingMappingHandler { } public static String getOriginalIDIfIDIsDummyID(String originalId) { - return REVERSE_DUMMY_ID_LOOKUP.getOrDefault(originalId, originalId); + return DUMMY_ID_TO_ORIGINAL_ID_LOOKUP.getOrDefault(originalId, originalId); + } + + public static String getDummyIdIfMissing(String originalId) { + return ORIGINAL_ID_TO_DUMMY_ID_LOOKUP.getOrDefault(originalId, originalId); } private static @Nullable String[] splitItemId(String itemId) { diff --git a/src/main/java/com/gtnewhorizons/postea/utility/SimpleTransformationMap.java b/src/main/java/com/gtnewhorizons/postea/utility/SimpleTransformationMap.java index 5fd18e5..ffcfebe 100644 --- a/src/main/java/com/gtnewhorizons/postea/utility/SimpleTransformationMap.java +++ b/src/main/java/com/gtnewhorizons/postea/utility/SimpleTransformationMap.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.postea.utility; -import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -8,9 +7,12 @@ import javax.annotation.Nullable; import net.minecraftforge.oredict.OreDictionary; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; + public class SimpleTransformationMap { - private final Map> registry = new HashMap<>(); + private final Map> registry = new Object2ObjectOpenHashMap<>(); /** * Puts a mapping in this registry. @@ -21,7 +23,7 @@ public class SimpleTransformationMap { */ public void put(String originalId, int originalMeta, T data) { if (originalMeta == -1) originalMeta = OreDictionary.WILDCARD_VALUE; - this.registry.computeIfAbsent(originalId, k -> new HashMap<>()) + this.registry.computeIfAbsent(originalId, k -> new Int2ObjectOpenHashMap<>()) .put(originalMeta, data); } diff --git a/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java b/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java index f3ea427..35240ce 100644 --- a/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java +++ b/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java @@ -1,8 +1,7 @@ package com.gtnewhorizons.postea.utility; -import java.util.Collection; +import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import java.util.function.Function; import javax.annotation.Nullable; @@ -19,26 +18,43 @@ import org.apache.commons.lang3.tuple.Pair; import com.google.common.collect.LinkedListMultimap; import com.gtnewhorizons.postea.api.IDExtenderCompat; +import cpw.mods.fml.common.event.FMLModIdMappingEvent; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; public class TransformerRegistry { - private static final LinkedListMultimap> ITEM_REPLACEMENT_MAP = LinkedListMultimap + private static final LinkedListMultimap> ITEM_REPLACEMENT_MAP = LinkedListMultimap .create(); - public static final LinkedListMultimap> BLOCK_REPLACEMENT_MAP = LinkedListMultimap + public static final LinkedListMultimap> BLOCK_REPLACEMENT_MAP = LinkedListMultimap .create(); - public static final IntOpenHashSet POSTEA_MARKED_IDS = new IntOpenHashSet(); + private static final Int2ObjectOpenHashMap>>> RUNTIME_ITEM_REPLACEMENT_MAP = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectOpenHashMap>>> RUNTIME_BLOCK_REPLACEMENT_MAP = new Int2ObjectOpenHashMap<>(); + private static final IntOpenHashSet DUMMY_IDS = new IntOpenHashSet(); + /** + * Map of IDs that were remapped by forge in the currently loaded save file. (usually happens due to missing + * mappings) + * Should be faster than asking block.getIdFromBlock() and Item.getIdFromItem() + */ + public static final Int2IntOpenHashMap REMAPPED_IDS = new Int2IntOpenHashMap(); + + /** + * Stores simple item transformations (eg if old id is # with meta # then turn into item with id # with meta #. + */ public static final SimpleTransformationMap> SIMPLE_ITEM_TRANSFORMATION_MAP = new SimpleTransformationMap<>(); + /** + * Stores simple block transformations (eg if old id is # with meta # then turn into item with id # with meta #. + */ public static final SimpleTransformationMap> SIMPLE_BLOCK_TRANSFORMATION_MAP = new SimpleTransformationMap<>(); - public static void addBlockReplacement(String originalId, - BiFunction transformer) { + public static void addBlockReplacement(String originalId, Function transformer) { MissingMappingHandler.createDummyBlockIfNeeded(originalId); BLOCK_REPLACEMENT_MAP.put(originalId, transformer); } - public static void addItemReplacement(String originalId, Function transformer) { + public static void addItemReplacement(String originalId, Function transformer) { MissingMappingHandler.createDummyItemIfNeeded(originalId); ITEM_REPLACEMENT_MAP.put(originalId, transformer); } @@ -68,74 +84,157 @@ public class TransformerRegistry { .put(originalId, originalMeta, Pair.of(Item.getIdFromItem(newItem), (short) newMeta)); } + private static boolean simpleBlockTransformer(BlockConversionInfo info, + Map> metaMap) { + Pair mapping = SimpleTransformationMap.getFromSubmap(metaMap, info.metadata); + if (mapping == null) return false; + int key = mapping.getKey(); + info.blockID = REMAPPED_IDS.getOrDefault(key, key); + if (mapping.getValue() != OreDictionary.WILDCARD_VALUE) { + info.metadata = mapping.getValue(); + } + return true; + } + + private static boolean simpleItemStackTransformer(NBTTagCompound tag, Map> metaMap) { + short meta = tag.getShort("Damage"); + Pair mapping = SimpleTransformationMap.getFromSubmap(metaMap, meta); + if (mapping == null) return false; + int key = mapping.getKey(); + IDExtenderCompat.setItemStackID(tag, REMAPPED_IDS.getOrDefault(key, key)); + if (mapping.getValue() != OreDictionary.WILDCARD_VALUE) { + tag.setShort("Damage", mapping.getValue()); + } + return true; + } + + public static boolean isDummyId(int id) { + return DUMMY_IDS.contains(id); + } + + // region FML Life Cycle handlers public static void onLoadCompleted() { + // add handlers for simple block remaps for (Map.Entry>> kv : SIMPLE_BLOCK_TRANSFORMATION_MAP.entrySet()) { MissingMappingHandler.createDummyBlockIfNeeded(kv.getKey()); - BLOCK_REPLACEMENT_MAP.put(kv.getKey(), (info, world) -> simpleBlockTransformer(info, world, kv.getValue())); + BLOCK_REPLACEMENT_MAP.put(kv.getKey(), info -> simpleBlockTransformer(info, kv.getValue())); } + // add handlers for simple item remaps for (Map.Entry>> kv : SIMPLE_ITEM_TRANSFORMATION_MAP.entrySet()) { MissingMappingHandler.createDummyItemIfNeeded(kv.getKey()); ITEM_REPLACEMENT_MAP.put(kv.getKey(), tag -> simpleItemStackTransformer(tag, kv.getValue())); } } - private static @Nullable BlockConversionInfo simpleBlockTransformer(BlockConversionInfo info, World world, - Map> metaMap) { - Pair mapping = SimpleTransformationMap.getFromSubmap(metaMap, info.metadata); - if (mapping == null) return null; - info.blockID = mapping.getKey(); - if (mapping.getValue() != OreDictionary.WILDCARD_VALUE) { - info.metadata = mapping.getValue(); + public static void onIdMappingsChanged(FMLModIdMappingEvent event) { + REMAPPED_IDS.clear(); + DUMMY_IDS.clear(); + RUNTIME_BLOCK_REPLACEMENT_MAP.clear(); + RUNTIME_ITEM_REPLACEMENT_MAP.clear(); + // Store the remapped ids since the simple transform pivot on ids since it's faster using the block/item. + for (FMLModIdMappingEvent.ModRemapping remap : event.remappedIds) { + // the remapping type doesn't actually matter, FML doesn't seem to handle it right anyway. + REMAPPED_IDS.putIfAbsent(remap.oldId, remap.newId); } - return info; - } - - private static @Nullable NBTTagCompound simpleItemStackTransformer(NBTTagCompound tag, - Map> metaMap) { - short meta = tag.getShort("Damage"); - Pair mapping = SimpleTransformationMap.getFromSubmap(metaMap, meta); - if (mapping == null) return null; - IDExtenderCompat.setItemStackID(tag, mapping.getKey()); - if (mapping.getValue() != OreDictionary.WILDCARD_VALUE) { - tag.setShort("Damage", mapping.getValue()); - } - return tag; - } - - public static void onIdMappingsChanged() { - POSTEA_MARKED_IDS.clear(); - for (String name : BLOCK_REPLACEMENT_MAP.keySet()) { - Block block = Block.getBlockFromName(name); - if (block != null) { + // create a lookup for source block ids. + for (String key : BLOCK_REPLACEMENT_MAP.keys()) { + String name = MissingMappingHandler.getDummyIdIfMissing(key); + if (Block.blockRegistry.getObject(name) instanceof Block block) { int id = Block.getIdFromBlock(block); - POSTEA_MARKED_IDS.add(id); + RUNTIME_BLOCK_REPLACEMENT_MAP.put(id, Pair.of(key, BLOCK_REPLACEMENT_MAP.get(key))); + // not using .equals() on purpose, I'm looking for instance, not content equality + if (key != name) DUMMY_IDS.add(id); + } + } + // create a lookup for source block ids. + for (String key : ITEM_REPLACEMENT_MAP.keys()) { + String name = MissingMappingHandler.getDummyIdIfMissing(key); + if (Item.itemRegistry.getObject(name) instanceof Item item) { + int id = Item.getIdFromItem(item); + RUNTIME_ITEM_REPLACEMENT_MAP.put(id, Pair.of(key, ITEM_REPLACEMENT_MAP.get(key))); + // not using .equals() on purpose, I'm looking for instance, not content equality + if (key != name) DUMMY_IDS.add(id); } } } public static void onServerStopping() { - POSTEA_MARKED_IDS.clear(); + RUNTIME_BLOCK_REPLACEMENT_MAP.clear(); + RUNTIME_ITEM_REPLACEMENT_MAP.clear(); + REMAPPED_IDS.clear(); + DUMMY_IDS.clear(); } + // endregion FML Life Cycle handlers - public static Collection> getItemReplacement(String itemNameInternal) { - return ITEM_REPLACEMENT_MAP.get(MissingMappingHandler.getOriginalIDIfIDIsDummyID(itemNameInternal)); - } - - public static BlockConversionInfo getBlockReplacement(BlockConversionInfo blockConversionInfo, World world) { - // auto change the value in case a handler check it, we can't do anything about the block id - // but hopefully that's never going to be an issue - String originalName = MissingMappingHandler.getOriginalIDIfIDIsDummyID(blockConversionInfo.blockName); - blockConversionInfo.blockName = originalName; - for (BiFunction transformer : BLOCK_REPLACEMENT_MAP - .get(originalName)) { - BlockConversionInfo result = transformer.apply(blockConversionInfo, world); - if (result != null) return result; + // region block replacement handler + public static @Nullable BlockConversionInfo getBlockReplacement(int blockId, byte metadata, World world, int x, + int y, int z) { + Pair>> data = RUNTIME_BLOCK_REPLACEMENT_MAP.get(blockId); + if (data == null) return null; + BlockConversionInfo blockConversionInfo = new BlockConversionInfo( + // transparently maps the id to the original id if it's a dummy id + data.getKey(), + blockId, + metadata, + x, + y, + z, + world); + for (Function transformer : data.getValue()) { + if (transformer.apply(blockConversionInfo)) { + // Not sure if we want to keep this around since this would affect perf a bit, good for debugging + // though. + // if (DUMMY_IDS.contains(result.blockID)) FMLLog.warning("returned a success on a non-transformed + // block!"); + return blockConversionInfo; + } + } + // if we're transforming a dummy block from a missing mapping and it wasn't handled, delete the block. + if (TransformerRegistry.isDummyId(blockId)) { + blockConversionInfo.blockID = 0; + blockConversionInfo.metadata = 0; + return blockConversionInfo; } return null; } + // endregion block replacement handler - // We need this to save reprocessing blocks. - public static boolean blockNotConvertible(int blockID) { - return !POSTEA_MARKED_IDS.contains(blockID); + // region item replacement handler + public static void transformItem(NBTTagCompound tag) { + // abort early if tag is bad. + if (tag.hasNoTags() || !tag.hasKey("id")) return; + // get handler + int id = IDExtenderCompat.getItemStackID(tag); + Pair>> data = RUNTIME_ITEM_REPLACEMENT_MAP.get(id); + // abort early if handler not found + if (data == null || data.getValue() + .isEmpty()) return; + + // apply handlers + for (Function transformer : data.getValue()) { + if (transformer.apply(tag)) { + // Not sure if we want to keep this around since this would affect perf a bit, good for debugging + // though. + // if (DUMMY_IDS.contains(IDExtenderCompat.getItemStackID(tag))) FMLLog.warning("returned a success on a + // non-transformed block!"); + return; + } + } + // if no handler overwrote anything on this stack, and it's a dummy, delete the stack. + if (TransformerRegistry.isDummyId(id)) { + tag.removeTag("id"); + tag.removeTag("idExt"); + } } + + /** + * A method to go from tag -> name since that's not provided to handlers usually. + * Could be useful if you have one handler function handling a lot of complex transforms. + */ + public static @Nullable String getMappedItemName(int itemId) { + Pair>> data = RUNTIME_ITEM_REPLACEMENT_MAP.get(itemId); + if (data == null) return null; + return data.getKey(); + } + // endregion item replacement handler }