diff --git a/src/main/java/com/gtnewhorizons/postea/Postea.java b/src/main/java/com/gtnewhorizons/postea/Postea.java index 50d3877..cf55f7a 100644 --- a/src/main/java/com/gtnewhorizons/postea/Postea.java +++ b/src/main/java/com/gtnewhorizons/postea/Postea.java @@ -1,6 +1,7 @@ package com.gtnewhorizons.postea; import com.gtnewhorizons.postea.utility.MissingMappingHandler; +import com.gtnewhorizons.postea.utility.SimpleTransformationRegistry; import com.gtnewhorizons.postea.utility.TransformerRegistry; import cpw.mods.fml.common.Mod; @@ -27,7 +28,8 @@ public class Postea { @Mod.EventHandler public void onIdMappingsChanged(FMLModIdMappingEvent event) { - TransformerRegistry.onIdMappingsChanged(event); + SimpleTransformationRegistry.onIdMappingsChanged(); + TransformerRegistry.onIdMappingsChanged(); } @Mod.EventHandler @@ -37,7 +39,7 @@ public class Postea { @Mod.EventHandler public void onLoadCompleted(FMLLoadCompleteEvent event) { - TransformerRegistry.onLoadCompleted(); + SimpleTransformationRegistry.onLoadCompleted(); MissingMappingHandler.onLoadCompleted(); } diff --git a/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java b/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java index 18ed85e..72e66d4 100644 --- a/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java +++ b/src/main/java/com/gtnewhorizons/postea/api/BlockReplacementManager.java @@ -11,6 +11,7 @@ import net.minecraftforge.oredict.OreDictionary; import com.gtnewhorizons.postea.utility.BlockConversionInfo; import com.gtnewhorizons.postea.utility.MissingMappingHandler; +import com.gtnewhorizons.postea.utility.SimpleTransformationRegistry; import com.gtnewhorizons.postea.utility.TransformerRegistry; public class BlockReplacementManager { @@ -43,8 +44,9 @@ public class BlockReplacementManager { */ @Deprecated @SuppressWarnings("unused") - public static void addBlockReplacement(@Nonnull String originalId, - @Nonnull BiFunction transformer) { + public static void addBlockReplacement(String originalId, + BiFunction transformer) { + if (transformer == null) throw new IllegalArgumentException("transformer is null"); addTransformationHandler(originalId, info -> transformer.apply(info, info.world) != null); } @@ -73,8 +75,7 @@ public class BlockReplacementManager { * @param transformer The transformer to apply. */ @SuppressWarnings("unused") - public static void addTransformationHandler(@Nonnull String originalId, - @Nonnull Function transformer) { + public static void addTransformationHandler(String originalId, 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); @@ -93,7 +94,7 @@ public class BlockReplacementManager { * @param block The block to remap to. */ @SuppressWarnings("unused") - public static void replaceMissingBlockWithNewBlock(@Nonnull String originalId, @Nonnull Block block) { + public static void replaceMissingBlockWithNewBlock(String originalId, Block block) { if (originalId == null) throw new IllegalArgumentException("original id is null"); if (block == null) throw new IllegalArgumentException("block is null"); MissingMappingHandler.addSimpleReplacement(originalId, block); @@ -239,10 +240,10 @@ public class BlockReplacementManager { * @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, - int newMeta, boolean skipStackRemap) { + public static void addBlockReplacement(String originalId, int originalMeta, Block block, int newMeta, + boolean skipStackRemap) { if (originalId == null) throw new IllegalArgumentException("original id is null"); if (block == null) throw new IllegalArgumentException("block is null"); - TransformerRegistry.addSimpleTransformer(originalId, originalMeta, block, newMeta, skipStackRemap); + SimpleTransformationRegistry.addSimpleTransformer(originalId, originalMeta, block, newMeta, skipStackRemap); } } diff --git a/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java b/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java index ec1558a..9c5f4d5 100644 --- a/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java +++ b/src/main/java/com/gtnewhorizons/postea/api/ItemStackReplacementManager.java @@ -1,9 +1,9 @@ package com.gtnewhorizons.postea.api; +import java.util.function.BiFunction; import java.util.function.Function; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import net.minecraft.init.Items; import net.minecraft.item.Item; @@ -12,109 +12,17 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.oredict.OreDictionary; import com.gtnewhorizons.postea.utility.MissingMappingHandler; +import com.gtnewhorizons.postea.utility.SimpleTransformationRegistry; import com.gtnewhorizons.postea.utility.TransformerRegistry; 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.
+     * @deprecated Use {@link #addTransformationHandler(String, BiFunction)} 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
@@ -139,9 +47,9 @@ public class ItemStackReplacementManager {
      */
     @Deprecated
     @SuppressWarnings("unused")
-    public static void addItemReplacement(@Nonnull String originalId,
-        @Nonnull Function transformer) {
-        addTransformationHandler(originalId, tag -> transformer.apply(tag) != null);
+    public static void addItemReplacement(String originalId, Function transformer) {
+        if (transformer == null) throw new IllegalArgumentException("transformer is null");
+        addTransformationHandler(originalId, (_originalId, tag) -> transformer.apply(tag) != null);
     }
 
     /**
@@ -168,8 +76,8 @@ public class ItemStackReplacementManager {
      * @param transformer The transformer to apply.
      */
     @SuppressWarnings("unused")
-    public static void addTransformationHandler(@Nonnull String originalId,
-        @Nonnull Function transformer) {
+    public static void addTransformationHandler(String originalId,
+        BiFunction transformer) {
         if (originalId == null) throw new IllegalArgumentException("original id is null");
         if (transformer == null) throw new IllegalArgumentException("transformer is null");
         TransformerRegistry.addItemReplacement(originalId, transformer);
@@ -187,7 +95,7 @@ public class ItemStackReplacementManager {
      * @param item       The item to remap to.
      */
     @SuppressWarnings("unused")
-    public static void replaceMissingItemWithNewItem(@Nonnull String originalId, @Nonnull Item item) {
+    public static void replaceMissingItemWithNewItem(String originalId, Item item) {
         if (originalId == null) throw new IllegalArgumentException("original id is null");
         if (item == null) throw new IllegalArgumentException("item is null");
         MissingMappingHandler.addSimpleReplacement(originalId, item);
@@ -289,6 +197,8 @@ 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.
+     * 
+ * DOES NOT COPY NBT FROM STACK. * * @implNote All transformations registered using this handler are executed in constant time regardless of how many * transformations are registered to a single id. @@ -308,6 +218,8 @@ public class ItemStackReplacementManager { /** * Adds a mapping to remap stacks of a given item to another item with a given damage/meta value. + *
+ * DOES NOT COPY NBT FROM STACK. * * @implNote All transformations registered using this handler are executed in constant time regardless of how many * transformations are registered to a single id. @@ -368,6 +280,8 @@ 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. + *
+ * DOES NOT COPY NBT FROM STACK. * * @implNote All transformations registered using this handler are executed in constant time regardless of how many * transformations are registered to a single id. @@ -384,6 +298,8 @@ 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. + *
+ * DOES NOT COPY NBT FROM STACK. * * @implNote All transformations registered using this handler are executed in constant time regardless of how many * transformations are registered to a single id. @@ -434,10 +350,10 @@ public class ItemStackReplacementManager { * @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, int newMeta, + public static void addItemReplacement(String originalId, int originalMeta, 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"); - TransformerRegistry.addSimpleTransformer(originalId, originalMeta, item, newMeta, skipBlockRemap); + SimpleTransformationRegistry.addSimpleTransformer(originalId, originalMeta, item, newMeta, skipBlockRemap); } } diff --git a/src/main/java/com/gtnewhorizons/postea/utility/SimpleTransformationRegistry.java b/src/main/java/com/gtnewhorizons/postea/utility/SimpleTransformationRegistry.java new file mode 100644 index 0000000..a1e219f --- /dev/null +++ b/src/main/java/com/gtnewhorizons/postea/utility/SimpleTransformationRegistry.java @@ -0,0 +1,186 @@ +package com.gtnewhorizons.postea.utility; + +import java.util.Map; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.oredict.OreDictionary; + +import org.apache.commons.lang3.tuple.Pair; + +import com.gtnewhorizons.postea.api.IDExtenderCompat; + +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; + +/** + * A registry for simple item and block transformations. A common use case is mod removal, replacements or deprecations. + * When replacing one mod with another in GTNH, we have to migrate most if not all the mod's items and this often means + * having to migrate almost every block and item from the original mod. + *
+ * If a mod uses metas to differentiate items (eg: GT5u) that will end up being migrated to separate items, this would + * usually result in potentially multiple handlers per item if they are destined to be split into separate mods, + * which increases the execution time linearly. The simple transformations uses a better algorithm for these + * which is executed in constant time regardless of how many transformations have been assigned to a given id. (mostly + * at the cost of higher memory usage due to caching) + */ +public class SimpleTransformationRegistry { + + /** + * 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 final Object2IntOpenHashMap RUNTIME_BLOCK_ID_LOOKUP = new Object2IntOpenHashMap<>(); + /** + * 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<>(); + public static final Object2IntOpenHashMap RUNTIME_ITEM_ID_LOOKUP = new Object2IntOpenHashMap<>(); + + // region FML Life Cycle handlers + /** + * Registers 1 handler per targeted ID to minimize the number of handlers. The meta-map is captured to reduce + * the execution time of the handlers. + */ + public static void onLoadCompleted() { + // add handlers for simple block remaps + for (Map.Entry>> kv : SIMPLE_BLOCK_TRANSFORMATION_MAP.entrySet()) { + Map> subMap = kv.getValue(); + TransformerRegistry.addBlockReplacement(kv.getKey(), info -> simpleBlockTransformer(info, subMap)); + } + // add handlers for simple item remaps + for (Map.Entry>> kv : SIMPLE_ITEM_TRANSFORMATION_MAP.entrySet()) { + Map> subMap = kv.getValue(); + TransformerRegistry + .addItemReplacement(kv.getKey(), (originalId, tag) -> simpleItemStackTransformer(tag, subMap)); + } + } + + /** + * Pre-caches updated block and item IDs for any simple transformation to speed-up the handlers. + */ + public static void onIdMappingsChanged() { + RUNTIME_ITEM_ID_LOOKUP.clear(); + RUNTIME_BLOCK_ID_LOOKUP.clear(); + for (Map.Entry>> kv : SIMPLE_BLOCK_TRANSFORMATION_MAP.entrySet()) { + for (Pair mapping : kv.getValue() + .values()) { + RUNTIME_BLOCK_ID_LOOKUP.computeIfAbsent(mapping.getKey(), Block::getIdFromBlock); + } + } + for (Map.Entry>> kv : SIMPLE_ITEM_TRANSFORMATION_MAP.entrySet()) { + for (Pair mapping : kv.getValue() + .values()) { + RUNTIME_ITEM_ID_LOOKUP.computeIfAbsent(mapping.getKey(), Item::getIdFromItem); + } + } + } + // endregion FML Life Cycle handlers + + // region block transformations + /** + * Registers a simple Block transformation for a simple 1:1 transformation: + *
    + *
  • block -> block (with same meta)
  • + *
  • block -> block with specific meta
  • + *
  • block with meta -> block (with same meta)
  • + *
  • block with meta -> block with specific meta
  • + *
+ * + * @implNote With these simple transformers, it doesn't matter how many transformations are registered to a single + * id, only a single handler will be registered per id during the FMLLoadCompleted event. Replacements + * are by using hash-table lookups so the execution of the transformation should be constant-time + * regardless + * of how many handlers were registered to a single id. + * + * @param originalId The original namespaced id of the block to replace. + * @param originalMeta The meta value of the block to replace. OreDictionary.WILDCARD_VALUE acts as a wildcard + * and fallback if there isn't simple transformation registered for a given meta. + * @param newBlock What block to replace with. + * @param newMeta The meta value of the transformed block. OreDictionary.WILDCARD_VALUE preserves the + * existing value. + * @param skipStackRemap Set to true to skip registering an identical simple item transformation. + */ + public static void addSimpleTransformer(String originalId, int originalMeta, Block newBlock, int newMeta, + boolean skipStackRemap) { + if (newMeta == -1) newMeta = OreDictionary.WILDCARD_VALUE; + SIMPLE_BLOCK_TRANSFORMATION_MAP.put(originalId, originalMeta, Pair.of(newBlock, (short) newMeta)); + if (!skipStackRemap) { + Item item = Item.getItemFromBlock(newBlock); + if (item != null) { + SIMPLE_ITEM_TRANSFORMATION_MAP.put(originalId, originalMeta, Pair.of(item, (short) newMeta)); + } + } + } + + /** + * The handler for the simple block transformation. + * + * @implNote This implementation has constant-time runtime. + */ + private static boolean simpleBlockTransformer(BlockConversionInfo info, Map> metaMap) { + Pair mapping = SimpleTransformationMap.getFromSubmap(metaMap, info.metadata); + if (mapping == null) return false; + info.blockID = RUNTIME_BLOCK_ID_LOOKUP.computeIfAbsent(mapping.getKey(), Block::getIdFromBlock); + if (mapping.getValue() != OreDictionary.WILDCARD_VALUE) { + info.metadata = mapping.getValue(); + } + return true; + } + // endregion block transformations + + // region item transformations + /** + * Registers a simple Block transformation for a simple 1:1 transformation: + *
    + *
  • item -> item (with same meta)
  • + *
  • item -> item with specific meta
  • + *
  • item with meta/damage -> item (with same meta/damage)
  • + *
  • item with meta/damage -> item with specific meta/damage
  • + *
+ * + * @implNote With these simple transformers, it doesn't matter how many transformations are registered to a single + * id, only a single handler will be registered per id during the FMLLoadCompleted event. Replacements + * are by using hash-table lookups so the execution of the transformation should be constant-time + * regardless + * of how many handlers were registered to a single id. + * + * @param originalId The original namespaced id of the item to replace. + * @param originalMeta The meta/damage value of the item to replace. OreDictionary.WILDCARD_VALUE acts as a + * wildcard + * and fallback if there isn't simple transformation registered for a given meta. + * @param newItem What item to replace with. + * @param newMeta The meta/damage value of the transformed item. OreDictionary.WILDCARD_VALUE preserves the + * existing + * value. + * @param skipBlockRemap Set to true to skip registering an identical block transformation if the item is an + * ItemBlock. + */ + public static void addSimpleTransformer(String originalId, int originalMeta, Item newItem, int newMeta, + boolean skipBlockRemap) { + if (newMeta == -1) newMeta = OreDictionary.WILDCARD_VALUE; + if (!skipBlockRemap && newItem instanceof ItemBlock ib) { + SIMPLE_BLOCK_TRANSFORMATION_MAP.put(originalId, originalMeta, Pair.of(ib.field_150939_a, (short) newMeta)); + } + SIMPLE_ITEM_TRANSFORMATION_MAP.put(originalId, originalMeta, Pair.of(newItem, (short) newMeta)); + } + + /** + * The handler for the simple item transformation. + * + * @implNote This implementation has constant-time runtime. + */ + private static boolean simpleItemStackTransformer(NBTTagCompound tag, Map> metaMap) { + short meta = tag.getShort("Damage"); + Pair mapping = SimpleTransformationMap.getFromSubmap(metaMap, meta); + if (mapping == null) return false; + IDExtenderCompat + .setItemStackID(tag, RUNTIME_ITEM_ID_LOOKUP.computeIfAbsent(mapping.getKey(), Item::getIdFromItem)); + if (mapping.getValue() != OreDictionary.WILDCARD_VALUE) { + tag.setShort("Damage", mapping.getValue()); + } + return true; + } + // endregion item transformations +} diff --git a/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java b/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java index dcb5348..85e1027 100644 --- a/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java +++ b/src/main/java/com/gtnewhorizons/postea/utility/TransformerRegistry.java @@ -1,141 +1,93 @@ package com.gtnewhorizons.postea.utility; import java.util.List; -import java.util.Map; +import java.util.function.BiFunction; import java.util.function.Function; import javax.annotation.Nullable; import net.minecraft.block.Block; import net.minecraft.item.Item; -import net.minecraft.item.ItemBlock; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; -import net.minecraftforge.oredict.OreDictionary; 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; +/** + * A registry for all transformer functions + */ 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 .create(); - private static final Int2ObjectOpenHashMap>>> RUNTIME_ITEM_REPLACEMENT_MAP = new Int2ObjectOpenHashMap<>(); + /** + * Map of itemID -> originalName+transformationHandler. + * Pivoting on the id is faster than pivoting on the strings so this should help with perf a bit. + * Generated during the mapping update event whilst entering/loading a world. + */ + private static final Int2ObjectOpenHashMap>>> RUNTIME_ITEM_REPLACEMENT_MAP = new Int2ObjectOpenHashMap<>(); + /** + * Map of blockIO -> originalName+transformationHandler. + * Pivoting on the id is faster than pivoting on the strings so this should help with perf a bit. + * Generated during the mapping update event whilst entering/loading a world. + */ 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() + * List of ids used by dummy items and blocks generated by Postea. + * Generated during the mapping update event whilst entering/loading a world. */ - public static final Int2IntOpenHashMap REMAPPED_IDS = new Int2IntOpenHashMap(); + public static final IntOpenHashSet DUMMY_IDS = new IntOpenHashSet(); /** - * Stores simple item transformations (eg if old id is # with meta # then turn into item with id # with meta #.) + * Registers a block transformation handler function for the given id. + * + * @implNote It doesn't matter if the id doesn't belong to an existing block, a dummy block will be created when + * Postea detects that the id isn't associated to a block during the FMLLoadCompleted event. Dependent + * mods don't need to worry about this as the block name in the BlockConversionInfo is automatically + * updated to the original id when attempting to transform a dummy block. When Postea runs a + * transformation on a dummy block and no handler return true, Postea will automatically convert it to + * air. + * + * @param originalId The namespaced id of the block to transform. + * @param transformer The transformation handler that will execute a transformation on the block. */ - 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, Function transformer) { MissingMappingHandler.createDummyBlockIfNeeded(originalId); BLOCK_REPLACEMENT_MAP.put(originalId, transformer); } - public static void addItemReplacement(String originalId, Function transformer) { + /** + * Registers an item transformation handler function for the given id. + * + * @implNote It doesn't matter if the id doesn't belong to an existing item, a dummy item will be created when + * Postea detects that the id isn't associated to an item during the FMLLoadCompleted event. Dependent + * mods don't need to worry about this as transformation handlers are given the original id associated + * with the nbt they are provided when the handler is executed. When Postea runs a transformation on a + * dummy item and no handler returns true, Postea will automatically invalidate the stack by removing + * its id and idExt fields. + * + * @param originalId The namespaced id of the block to transform. + * @param transformer The transformation handler that will execute a transformation on the block. + */ + public static void addItemReplacement(String originalId, BiFunction transformer) { MissingMappingHandler.createDummyItemIfNeeded(originalId); ITEM_REPLACEMENT_MAP.put(originalId, transformer); } - public static void addSimpleTransformer(String originalId, int originalMeta, Block newBlock, int newMeta, - boolean skipStackRemap) { - if (newMeta == -1) newMeta = OreDictionary.WILDCARD_VALUE; - SIMPLE_BLOCK_TRANSFORMATION_MAP - .put(originalId, originalMeta, Pair.of(Block.getIdFromBlock(newBlock), (short) newMeta)); - if (!skipStackRemap) { - Item item = Item.getItemFromBlock(newBlock); - if (item != null) { - SIMPLE_ITEM_TRANSFORMATION_MAP - .put(originalId, originalMeta, Pair.of(Item.getIdFromItem(item), (short) newMeta)); - } - } - } - - public static void addSimpleTransformer(String originalId, int originalMeta, Item newItem, int newMeta, - boolean skipBlockRemap) { - if (newMeta == -1) newMeta = OreDictionary.WILDCARD_VALUE; - if (!skipBlockRemap && newItem instanceof ItemBlock ib) { - SIMPLE_BLOCK_TRANSFORMATION_MAP - .put(originalId, originalMeta, Pair.of(Block.getIdFromBlock(ib.field_150939_a), (short) newMeta)); - } - SIMPLE_ITEM_TRANSFORMATION_MAP - .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 -> 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())); - } - } - - public static void onIdMappingsChanged(FMLModIdMappingEvent event) { - REMAPPED_IDS.clear(); + public static void onIdMappingsChanged() { 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); - } // create a lookup for source block ids. for (String key : BLOCK_REPLACEMENT_MAP.keys()) { String name = MissingMappingHandler.getDummyIdIfMissing(key); @@ -161,12 +113,15 @@ public class TransformerRegistry { public static void onServerStopping() { RUNTIME_BLOCK_REPLACEMENT_MAP.clear(); RUNTIME_ITEM_REPLACEMENT_MAP.clear(); - REMAPPED_IDS.clear(); DUMMY_IDS.clear(); } // endregion FML Life Cycle handlers - // region block replacement handler + // region transformation handlers + public static boolean isDummyId(int id) { + return TransformerRegistry.DUMMY_IDS.contains(id); + } + 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); @@ -197,22 +152,20 @@ public class TransformerRegistry { } return null; } - // endregion block replacement handler - // 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); + 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)) { + for (BiFunction transformer : data.getValue()) { + if (transformer.apply(data.getKey(), 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 @@ -226,15 +179,5 @@ public class TransformerRegistry { 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 + // endregion transformation handlers }