From ea2972725aebd5f72b20896fff2d6575de764aeb Mon Sep 17 00:00:00 2001 From: Player Date: Mon, 7 Apr 2014 17:30:38 +0200 Subject: [PATCH] Registry: Add support for registering ItemBlocks before their Blocks --- .../FMLControlledNamespacedRegistry.java | 41 +++++++- .../mods/fml/common/registry/GameData.java | 97 +++++++++++++++---- 2 files changed, 114 insertions(+), 24 deletions(-) diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java b/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java index 4a746b2fb..21bbf9218 100644 --- a/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -103,6 +103,14 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { } } + /** + * Fetch the object identified by the specified name or the default object. + * + * For blocks the default object is the air block, for items it's null. + * + * @param name Unique name identifying the object. + * @return Registered object of the default object if it wasn't found- + */ @Override public I getObject(String name) { @@ -110,6 +118,14 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { return object == null ? this.optionalDefaultObject : object; } + /** + * Fetch the object identified by the specified id or the default object. + * + * For blocks the default object is the air block, for items it's null. + * + * @param id ID identifying the object. + * @return Registered object of the default object if it wasn't found- + */ @Override public I getObjectById(int id) { @@ -182,6 +198,14 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { return ret; } + /** + * Determine if the registry has an entry for the specified name. + * + * Aliased names will be resolved as well. + * + * @param name Object name to check. + * @return true if a matching entry was found. + */ @Override public boolean containsKey(String name) { @@ -228,7 +252,7 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { // internal @SuppressWarnings("unchecked") - public void serializeInto(Map idMapping) + public void serializeInto(Map idMapping) // for saving { for (I thing : (Iterable) this) { @@ -236,11 +260,20 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { } } - public Map getAliases() + public Map getAliases() // for saving { return ImmutableMap.copyOf(aliases); } + /** + * Add the specified object to the registry. + * + * @param id ID to use if available, auto-assigned otherwise. + * @param name Name to use, prefixed by the mod id. + * @param thing Object to add. + * @param availabilityMap Map marking available IDs for auto assignment. + * @return ID eventually allocated. + */ int add(int id, String name, I thing, BitSet availabilityMap) { if (name == null) throw new NullPointerException("Can't use a null-name for the registry."); @@ -256,9 +289,9 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { { idToUse = availabilityMap.nextClearBit(minId); } - if (idToUse >= maxId) + if (idToUse > maxId) { - throw new RuntimeException(String.format("Invalid id %s - not accepted",id)); + throw new RuntimeException(String.format("Invalid id %d - maximum id range exceeded.", id)); } ModContainer mc = Loader.instance().activeModContainer(); diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java b/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java index 94abc62c1..47ba3f537 100644 --- a/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java +++ b/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java @@ -56,6 +56,11 @@ import cpw.mods.fml.common.registry.GameRegistry.Type; import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier; public class GameData { + private static final int MIN_BLOCK_ID = 0; + private static final int MAX_BLOCK_ID = 4095; + private static final int MIN_ITEM_ID = 4096; + private static final int MAX_ITEM_ID = 31999; + private static final GameData mainData = new GameData(); /** @@ -233,7 +238,7 @@ public class GameData { */ public static void fixBrokenIds(Map dataList, Set blockedIds) { - BitSet availabilityMap = new BitSet(32000); + BitSet availabilityMap = new BitSet(MAX_ITEM_ID + 1); // reserve all ids occupied by blocks for (Entry entry : dataList.entrySet()) @@ -675,9 +680,9 @@ public class GameData { private GameData() { - iBlockRegistry = new FMLControlledNamespacedRegistry("air", 4095, 0, Block.class,'\u0001'); - iItemRegistry = new FMLControlledNamespacedRegistry(null, 32000, 4096, Item.class,'\u0002'); - availabilityMap = new BitSet(32000); + iBlockRegistry = new FMLControlledNamespacedRegistry("air", MAX_BLOCK_ID, MIN_BLOCK_ID, Block.class,'\u0001'); + iItemRegistry = new FMLControlledNamespacedRegistry(null, MAX_ITEM_ID, MIN_ITEM_ID, Item.class,'\u0002'); + availabilityMap = new BitSet(MAX_ITEM_ID + 1); blockedIds = new HashSet(); } @@ -725,32 +730,29 @@ public class GameData { ModContainer mc = Loader.instance().activeModContainer(); customOwners.put(new UniqueIdentifier(modId, name), mc); } - if (item instanceof ItemBlock) + if (item instanceof ItemBlock) // ItemBlock, adjust id and clear the slot already occupied by the corresponding block { - // ItemBlock, clear the item slot already occupied by the corresponding block - idHint = iBlockRegistry.getId(((ItemBlock) item).field_150939_a); + Block block = ((ItemBlock) item).field_150939_a; + idHint = iBlockRegistry.getId(block); - if (idHint == -1) + if (idHint == -1) // ItemBlock before its Block { - throw new RuntimeException("Cannot register an itemblock before its block"); + idHint = availabilityMap.nextClearBit(MIN_BLOCK_ID); // find suitable id here, iItemRegistry would search from MIN_ITEM_ID + if (idHint > MAX_BLOCK_ID) throw new RuntimeException(String.format("Invalid id %d - maximum id range exceeded.", idHint)); } - - if (iItemRegistry.getObjectById(idHint) != null) + else // ItemBlock after its Block { - throw new IllegalStateException(String.format("The Item Registry slot %d is already used by %s", idHint, iItemRegistry.getObjectById(idHint))); + FMLLog.fine("Found matching Block %s for ItemBlock %s at id %d", block, item, idHint); + freeSlot(idHint, item); // temporarily free the slot occupied by the Block for the item registration } - - freeSlot(idHint); // temporarily free the slot occupied by the Block for the item registration } int itemId = iItemRegistry.add(idHint, name, item, availabilityMap); - if (item instanceof ItemBlock) + if (item instanceof ItemBlock) // verify { - if (itemId != idHint) // just in case of bugs... - { - throw new IllegalStateException("ItemBlock insertion failed."); - } + if (itemId != idHint) throw new IllegalStateException("Block -> ItemBlock insertion failed."); + verifyItemBlockName((ItemBlock) item); } // block the Block Registry slot with the same id @@ -771,8 +773,35 @@ public class GameData { ModContainer mc = Loader.instance().activeModContainer(); customOwners.put(new UniqueIdentifier(modId, name), mc); } + + // handle ItemBlock-before-Block registrations + ItemBlock itemBlock = null; + + for (Item item : (Iterable) iItemRegistry) // find matching ItemBlock + { + if (item instanceof ItemBlock && ((ItemBlock) item).field_150939_a == block) + { + itemBlock = (ItemBlock) item; + break; + } + } + + if (itemBlock != null) // has ItemBlock, adjust id and clear the slot already occupied by the corresponding item + { + idHint = iItemRegistry.getId(itemBlock); + FMLLog.fine("Found matching ItemBlock %s for Block %s at id %d", itemBlock, block, idHint); + freeSlot(idHint, block); // temporarily free the slot occupied by the Item for the block registration + } + + // add int blockId = iBlockRegistry.add(idHint, name, block, availabilityMap); + if (itemBlock != null) // verify + { + if (blockId != idHint) throw new IllegalStateException("ItemBlock -> Block insertion failed."); + verifyItemBlockName(itemBlock); + } + useSlot(blockId); return blockId; @@ -792,11 +821,39 @@ public class GameData { availabilityMap.set(id); } - private void freeSlot(int id) + /** + * Free the specified slot. + * + * The slot must not be occupied by something else than the specified object within the same type. + * The same object is permitted for handling duplicate registrations. + * + * @param id id to free + * @param obj object allowed besides different types (block vs item) + */ + private void freeSlot(int id, Object obj) { + FMLControlledNamespacedRegistry registry = (obj instanceof Block) ? iBlockRegistry : iItemRegistry; + Object thing = registry.getRaw(id); + + if (thing != null && thing != obj) + { + throw new IllegalStateException(String.format("Can't free registry slot %d occupied by %s", id, thing)); + } + availabilityMap.clear(id); } + private void verifyItemBlockName(ItemBlock item) + { + String blockName = iBlockRegistry.getNameForObject(item.field_150939_a); + String itemName = iItemRegistry.getNameForObject(item); + + if (blockName != null && !blockName.equals(itemName)) + { + FMLLog.bigWarning("Block <-> ItemBlock name mismatch, block name %s, item name %s", blockName, itemName); + } + } + @SuppressWarnings("unchecked") private void testConsistency() { // test if there's an entry for every set bit in availabilityMap