Registry: Add support for registering ItemBlocks before their Blocks

This commit is contained in:
Player 2014-04-07 17:30:38 +02:00
parent 07d5d5c7af
commit ea2972725a
2 changed files with 114 additions and 24 deletions

View file

@ -103,6 +103,14 @@ public class FMLControlledNamespacedRegistry<I> 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<I> 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<I> 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<I> extends RegistryNamespaced {
// internal
@SuppressWarnings("unchecked")
public void serializeInto(Map<String, Integer> idMapping)
public void serializeInto(Map<String, Integer> idMapping) // for saving
{
for (I thing : (Iterable<I>) this)
{
@ -236,11 +260,20 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
}
}
public Map<String, String> getAliases()
public Map<String, String> 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<I> 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();

View file

@ -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<String, Integer> dataList, Set<Integer> blockedIds)
{
BitSet availabilityMap = new BitSet(32000);
BitSet availabilityMap = new BitSet(MAX_ITEM_ID + 1);
// reserve all ids occupied by blocks
for (Entry<String, Integer> entry : dataList.entrySet())
@ -675,9 +680,9 @@ public class GameData {
private GameData()
{
iBlockRegistry = new FMLControlledNamespacedRegistry<Block>("air", 4095, 0, Block.class,'\u0001');
iItemRegistry = new FMLControlledNamespacedRegistry<Item>(null, 32000, 4096, Item.class,'\u0002');
availabilityMap = new BitSet(32000);
iBlockRegistry = new FMLControlledNamespacedRegistry<Block>("air", MAX_BLOCK_ID, MIN_BLOCK_ID, Block.class,'\u0001');
iItemRegistry = new FMLControlledNamespacedRegistry<Item>(null, MAX_ITEM_ID, MIN_ITEM_ID, Item.class,'\u0002');
availabilityMap = new BitSet(MAX_ITEM_ID + 1);
blockedIds = new HashSet<Integer>();
}
@ -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<Item>) 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