isolate simple transformations to own file + more doc + cleanup

This commit is contained in:
Guillaume Mercier committed 2026-01-23 17:59:04 -05:00
1 parent d20a5fe43f
commit 9672270b1e
5 files changed
+272 -224

No files matched your search

@@ -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();
}
@@ -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<BlockConversionInfo, World, BlockConversionInfo> transformer) {
public static void addBlockReplacement(String originalId,
BiFunction<BlockConversionInfo, World, BlockConversionInfo> 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<BlockConversionInfo, Boolean> transformer) {
public static void addTransformationHandler(String originalId, Function<BlockConversionInfo, Boolean> 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);
}
}
@@ -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.
* <pre>{@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;
* }
* }
* }<pre/>
*
* @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.
* <pre>{@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;
* }
* }
* }<pre/>
*
* @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<NBTTagCompound, NBTTagCompound> transformer) {
addTransformationHandler(originalId, tag -> transformer.apply(tag) != null);
public static void addItemReplacement(String originalId, Function<NBTTagCompound, NBTTagCompound> 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<NBTTagCompound, Boolean> transformer) {
public static void addTransformationHandler(String originalId,
BiFunction<String, NBTTagCompound, Boolean> 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.
* <br>
* 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.
* <br>
* 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.
* <br>
* 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.
* <br>
* 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);
}
}
@@ -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.
* <hr>
* 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<Pair<Block, Short>> SIMPLE_BLOCK_TRANSFORMATION_MAP = new SimpleTransformationMap<>();
public static final Object2IntOpenHashMap<Block> 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<Pair<Item, Short>> SIMPLE_ITEM_TRANSFORMATION_MAP = new SimpleTransformationMap<>();
public static final Object2IntOpenHashMap<Item> 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<String, Map<Integer, Pair<Block, Short>>> kv : SIMPLE_BLOCK_TRANSFORMATION_MAP.entrySet()) {
Map<Integer, Pair<Block, Short>> subMap = kv.getValue();
TransformerRegistry.addBlockReplacement(kv.getKey(), info -> simpleBlockTransformer(info, subMap));
}
// add handlers for simple item remaps
for (Map.Entry<String, Map<Integer, Pair<Item, Short>>> kv : SIMPLE_ITEM_TRANSFORMATION_MAP.entrySet()) {
Map<Integer, Pair<Item, Short>> 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<String, Map<Integer, Pair<Block, Short>>> kv : SIMPLE_BLOCK_TRANSFORMATION_MAP.entrySet()) {
for (Pair<Block, Short> mapping : kv.getValue()
.values()) {
RUNTIME_BLOCK_ID_LOOKUP.computeIfAbsent(mapping.getKey(), Block::getIdFromBlock);
}
}
for (Map.Entry<String, Map<Integer, Pair<Item, Short>>> kv : SIMPLE_ITEM_TRANSFORMATION_MAP.entrySet()) {
for (Pair<Item, Short> 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:
* <ul>
* <li>block -> block (with same meta)</li>
* <li>block -> block with specific meta</li>
* <li>block with meta -> block (with same meta)</li>
* <li>block with meta -> block with specific meta</li>
* </ul>
*
* @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<Integer, Pair<Block, Short>> metaMap) {
Pair<Block, Short> 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:
* <ul>
* <li>item -> item (with same meta)</li>
* <li>item -> item with specific meta</li>
* <li>item with meta/damage -> item (with same meta/damage)</li>
* <li>item with meta/damage -> item with specific meta/damage</li>
* </ul>
*
* @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<Integer, Pair<Item, Short>> metaMap) {
short meta = tag.getShort("Damage");
Pair<Item, Short> 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
}
@@ -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<String, Function<NBTTagCompound, Boolean>> ITEM_REPLACEMENT_MAP = LinkedListMultimap
private static final LinkedListMultimap<String, BiFunction<String, NBTTagCompound, Boolean>> ITEM_REPLACEMENT_MAP = LinkedListMultimap
.create();
public static final LinkedListMultimap<String, Function<BlockConversionInfo, Boolean>> BLOCK_REPLACEMENT_MAP = LinkedListMultimap
.create();
private static final Int2ObjectOpenHashMap<Pair<String, List<Function<NBTTagCompound, Boolean>>>> 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<Pair<String, List<BiFunction<String, NBTTagCompound, Boolean>>>> 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<Pair<String, List<Function<BlockConversionInfo, Boolean>>>> 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<Pair<Integer, Short>> 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<Pair<Integer, Short>> SIMPLE_BLOCK_TRANSFORMATION_MAP = new SimpleTransformationMap<>();
public static void addBlockReplacement(String originalId, Function<BlockConversionInfo, Boolean> transformer) {
MissingMappingHandler.createDummyBlockIfNeeded(originalId);
BLOCK_REPLACEMENT_MAP.put(originalId, transformer);
}
public static void addItemReplacement(String originalId, Function<NBTTagCompound, Boolean> 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<String, NBTTagCompound, Boolean> 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<Integer, Pair<Integer, Short>> metaMap) {
Pair<Integer, Short> 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<Integer, Pair<Integer, Short>> metaMap) {
short meta = tag.getShort("Damage");
Pair<Integer, Short> 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<String, Map<Integer, Pair<Integer, Short>>> 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<String, Map<Integer, Pair<Integer, Short>>> 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<String, List<Function<BlockConversionInfo, Boolean>>> 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<String, List<Function<NBTTagCompound, Boolean>>> data = RUNTIME_ITEM_REPLACEMENT_MAP.get(id);
Pair<String, List<BiFunction<String, NBTTagCompound, Boolean>>> data = RUNTIME_ITEM_REPLACEMENT_MAP.get(id);
// abort early if handler not found
if (data == null || data.getValue()
.isEmpty()) return;
// apply handlers
for (Function<NBTTagCompound, Boolean> transformer : data.getValue()) {
if (transformer.apply(tag)) {
for (BiFunction<String, NBTTagCompound, Boolean> 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<String, List<Function<NBTTagCompound, Boolean>>> data = RUNTIME_ITEM_REPLACEMENT_MAP.get(itemId);
if (data == null) return null;
return data.getKey();
}
// endregion item replacement handler
// endregion transformation handlers
}