Fix remaining issue with autoassigned Block and Item IDs overlapping.

This commit is contained in:
Player 2014-03-20 12:41:59 +01:00
parent 0cd40e70b6
commit ff6083e77b
7 changed files with 455 additions and 407 deletions

View file

@ -12,7 +12,7 @@
public class Block
{
- public static final RegistryNamespaced field_149771_c = new RegistryNamespacedDefaultedByKey("air");
+ public static final RegistryNamespaced field_149771_c = GameData.blockRegistry;
+ public static final RegistryNamespaced field_149771_c = GameData.getBlockRegistry();
private CreativeTabs field_149772_a;
protected String field_149768_d;
public static final Block.SoundType field_149769_e = new Block.SoundType("stone", 1.0F, 1.0F);

View file

@ -14,7 +14,7 @@
public class Item
{
- public static final RegistryNamespaced field_150901_e = new RegistryNamespaced();
+ public static final RegistryNamespaced field_150901_e = GameData.itemRegistry;
+ public static final RegistryNamespaced field_150901_e = GameData.getItemRegistry();
protected static final UUID field_111210_e = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF");
private CreativeTabs field_77701_a;
protected static Random field_77697_d = new Random();

View file

@ -453,7 +453,6 @@ public class Loader
{
initializeLoader();
mods = Lists.newArrayList();
GameData.fixupRegistries();
namedMods = Maps.newHashMap();
modController = new LoadController(this);
modController.transition(LoaderState.LOADING, false);
@ -493,7 +492,6 @@ public class Loader
}
modController.transition(LoaderState.PREINITIALIZATION, false);
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, disc.getASMTable(), canonicalConfigDir);
GameData.freezeData();
modController.transition(LoaderState.INITIALIZATION, false);
}
@ -676,6 +674,7 @@ public class Loader
modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
modController.transition(LoaderState.AVAILABLE, false);
modController.distributeStateMessage(LoaderState.AVAILABLE);
GameData.freezeData();
// Dump the custom registry data map, if necessary
GameData.dumpRegistry(minecraftDir);
FMLLog.info("Forge Mod Loader has successfully loaded %d mod%s", mods.size(), mods.size() == 1 ? "" : "s");

View file

@ -1,158 +1,76 @@
package cpw.mods.fml.common.registry;
import gnu.trove.map.hash.TIntIntHashMap;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.minecraft.util.ObjectIntIdentityMap;
import net.minecraft.util.RegistryNamespaced;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
static class FMLObjectIntIdentityMap extends ObjectIntIdentityMap {
private TIntIntHashMap frozenMap;
private TIntIntHashMap oldMap;
private TIntIntHashMap newMap;
private ArrayList<Integer> frozenIndex;
private ArrayList<Integer> oldIndex;
private ArrayList<Integer> newIndex;
public FMLObjectIntIdentityMap()
{
}
boolean containsID(int id)
{
return func_148744_b(id);
}
Object get(int id)
{
return func_148745_a(id);
}
int get(Object obj)
{
return func_148747_b(obj);
}
@SuppressWarnings("unchecked")
void beginSwap()
{
oldMap = field_148749_a;
newMap = new TIntIntHashMap(256, 0.5F, -1, -1);
oldIndex = (ArrayList<Integer>) field_148748_b;
newIndex = new ArrayList<Integer>(oldIndex.size());
}
@SuppressWarnings("unchecked")
void freezeMap()
{
frozenMap = new TIntIntHashMap(field_148749_a);
frozenIndex = new ArrayList<Integer>(field_148748_b);
}
void revertToFrozen()
{
field_148749_a = frozenMap;
field_148748_b = frozenIndex;
}
void completeSwap()
{
field_148749_a = newMap;
field_148748_b = newIndex;
oldIndex = newIndex = null;
oldMap = newMap = null;
}
void revertSwap()
{
field_148749_a = oldMap;
field_148748_b = oldIndex;
oldIndex = newIndex = null;
oldMap = newMap = null;
}
void putNew(int id, Object item)
{
field_148749_a = newMap;
field_148748_b = newIndex;
super.func_148746_a(item, id);
field_148749_a = oldMap;
field_148748_b = oldIndex;
}
List<Integer> usedIds()
{
return Ints.asList(field_148749_a.keys());
}
}
private final Class<I> superType;
private String optionalDefaultName;
private I optionalDefaultObject;
private BiMap<String,Integer> namedIds = HashBiMap.create();
private BiMap<String,Integer> frozenIds;
private Map<String,Integer> transactionalNamedIds;
private BitSet transactionalAvailabilityMap;
private BitSet availabilityMap;
int maxId;
private int minId;
private char discriminator;
public FMLControlledNamespacedRegistry(String optionalDefault, int maxIdValue, int minIdValue, Class<I> type, char discriminator)
FMLControlledNamespacedRegistry(String optionalDefault, int maxIdValue, int minIdValue, Class<I> type, char discriminator)
{
this.superType = type;
this.discriminator = discriminator;
this.optionalDefaultName = optionalDefault;
this.availabilityMap = new BitSet(maxIdValue);
this.maxId = maxIdValue;
this.minId = minIdValue;
this.field_148759_a = new FMLObjectIntIdentityMap();
}
void set(FMLControlledNamespacedRegistry<I> registry)
{
if (this.superType != registry.superType) throw new IllegalArgumentException("incompatible registry");
this.discriminator = registry.discriminator;
this.optionalDefaultName = registry.optionalDefaultName;
this.maxId = registry.maxId;
this.minId = registry.minId;
field_148759_a = new ObjectIntIdentityMap();
field_82596_a.clear();
for (Iterator<Object> it = registry.iterator(); it.hasNext(); )
{
I obj = (I) it.next();
super.func_148756_a(registry.getId(obj), registry.func_148750_c(obj), obj);
}
}
/**
* Add an object to the registry, trying to use the specified id.
*
* @deprecated register through {@link GameRegistry} instead.
*/
@Override
@Deprecated
public void func_148756_a(int id, String name, Object thing)
{
FMLLog.finer("Add : %s %d %s", name, id, thing);
add(id, name, superType.cast(thing));
GameData.getMain().register(thing, name, id);
}
int swap(int id, String name, I thing)
{
FMLLog.fine("Swap : %s %d %s", name, id, thing);
BitSet temporary = availabilityMap;
availabilityMap = transactionalAvailabilityMap;
int idToUse = id;
if (id == 0 || availabilityMap.get(id))
{
idToUse = availabilityMap.nextClearBit(minId);
}
if (idToUse >= maxId)
{
throw new RuntimeException(String.format("Invalid id %s - not accepted",id));
}
namedIds.forcePut(func_148755_c(name),idToUse);
reassignMapping(name, idToUse);
useSlot(idToUse);
availabilityMap = temporary;
FMLLog.fine("Swap : %s %d %s", name, idToUse, thing);
return idToUse;
}
public int add(int id, String name, I thing)
int add(int id, String name, I thing, BitSet availabilityMap)
{
if (name.equals(optionalDefaultName))
{
@ -175,9 +93,7 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
String prefix = mc.getModId();
name = prefix + ":"+ name;
}
namedIds.forcePut(func_148755_c(name),idToUse);
super.func_148756_a(idToUse, name, thing);
useSlot(idToUse);
FMLLog.finer("Add : %s %d %s", name, idToUse, thing);
return idToUse;
}
@ -197,9 +113,9 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
}
private FMLObjectIntIdentityMap idMap()
private ObjectIntIdentityMap idMap()
{
return (FMLObjectIntIdentityMap) field_148759_a;
return field_148759_a;
}
@SuppressWarnings("unchecked")
@ -208,52 +124,17 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
return (BiMap<String,I>) field_82596_a;
}
void beginIdSwap()
Map<String,Integer> getEntriesNotIn(FMLControlledNamespacedRegistry<I> registry)
{
idMap().beginSwap();
transactionalNamedIds = Maps.newHashMap();
transactionalAvailabilityMap = new BitSet();
}
Map<String,Integer> ret = new HashMap<String, Integer>();
void reassignMapping(String name, int newId)
{
Object item = nameMap().get(name);
idMap().putNew(newId, item);
transactionalNamedIds.put(name,newId);
transactionalAvailabilityMap.set(newId);
}
void freezeMap()
{
if (frozenIds == null)
for (Iterator<Object> it = iterator(); it.hasNext(); )
{
frozenIds = ImmutableBiMap.copyOf(namedIds);
idMap().freezeMap();
I thing = (I) it.next();
if (!registry.field_148758_b.containsKey(thing)) ret.put(func_148750_c(thing), getId(thing));
}
}
void revertToFrozen()
{
namedIds = HashBiMap.create(frozenIds);
idMap().revertToFrozen();
}
Map<String,Integer> getMissingMappings()
{
return Maps.difference(frozenIds, transactionalNamedIds).entriesOnlyOnLeft();
}
void completeIdSwap()
{
idMap().completeSwap();
namedIds.clear();
namedIds.putAll(transactionalNamedIds);
transactionalNamedIds = null;
}
void revertSwap()
{
idMap().revertSwap();
transactionalNamedIds = null;
return ret;
}
public I get(int id)
@ -271,54 +152,54 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
return func_148757_b(thing);
}
public I getRaw(int id)
{
return superType.cast(super.func_148754_a(id));
}
public I getRaw(String name)
{
return superType.cast(super.func_82594_a(name));
}
public void serializeInto(Map<String, Integer> idMapping)
{
for (Entry<String, Integer> id: namedIds.entrySet())
for (Iterator<Object> it = iterator(); it.hasNext(); )
{
idMapping.put(discriminator+id.getKey(), id.getValue());
I thing = (I) it.next();
idMapping.put(discriminator+func_148750_c(thing), getId(thing));
}
}
public void useSlot(int id)
{
if (id >= maxId) return;
availabilityMap.set(id);
}
List<Integer> usedIds()
{
return ((FMLObjectIntIdentityMap)field_148759_a).usedIds();
}
public int getId(String itemName)
{
if (namedIds.containsKey(itemName))
{
return namedIds.get(itemName);
}
else
{
return -1;
}
I obj = getRaw(itemName);
if (obj == null) return -1;
return getId(obj);
}
public boolean contains(String itemName)
{
return namedIds.containsKey(itemName);
return field_82596_a.containsKey(itemName);
}
void dump()
{
for (Entry<String, Integer> entry : namedIds.entrySet())
List<Integer> ids = new ArrayList<Integer>();
for (Iterator<Object> it = iterator(); it.hasNext(); )
{
String name = entry.getKey();
Object thing = idMap().get(entry.getValue().intValue());
FMLLog.finer("Registry : %s %d %s", name, entry.getValue(), thing);
ids.add(getId((I) it.next()));
}
// sort by id
Collections.sort(ids);
for (int id : ids)
{
I thing = getRaw(id);
FMLLog.finer("Registry : %s %d %s", func_148750_c(thing), id, thing);
}
}
BitSet slots()
{
return (BitSet) availabilityMap.clone();
}
}

View file

@ -15,15 +15,18 @@ package cpw.mods.fml.common.registry;
import java.io.File;
import java.io.IOException;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.logging.log4j.Level;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.util.RegistryNamespaced;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
@ -37,6 +40,7 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.google.common.io.Files;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
@ -45,57 +49,53 @@ import cpw.mods.fml.common.event.FMLMissingMappingsEvent.MissingMapping;
import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier;
public class GameData {
private static Table<String, String, ItemStack> customItemStacks = HashBasedTable.create();
private static final GameData mainData = new GameData();
public static final FMLControlledNamespacedRegistry<Block> blockRegistry = new FMLControlledNamespacedRegistry<Block>("air", 4095, 0, Block.class,'\u0001');
public static final FMLControlledNamespacedRegistry<Item> itemRegistry = new FMLControlledNamespacedRegistry<Item>(null, 32000, 4096, Item.class,'\u0002');
/**
* @deprecated use {@link getBlockRegistry()} instead.
*/
@Deprecated
public static final FMLControlledNamespacedRegistry<Block> blockRegistry = getBlockRegistry();
/**
* @deprecated use {@link getItemRegistry()} instead.
*/
@Deprecated
public static final FMLControlledNamespacedRegistry<Item> itemRegistry = getItemRegistry();
private static Table<String, String, ItemStack> customItemStacks = HashBasedTable.create();
private static Map<UniqueIdentifier, ModContainer> customOwners = Maps.newHashMap();
private static GameData frozen;
// public api
public static FMLControlledNamespacedRegistry<Block> getBlockRegistry() {
return getMain().iBlockRegistry;
}
public static FMLControlledNamespacedRegistry<Item> getItemRegistry() {
return getMain().iItemRegistry;
}
public static ModContainer findModOwner(String string)
{
UniqueIdentifier ui = new UniqueIdentifier(string);
if (customOwners.containsKey(ui))
{
return customOwners.get(ui);
}
return Loader.instance().getIndexedModList().get(ui.modId);
}
// internal from here
public static Map<String,Integer> buildItemDataList()
{
Map<String,Integer> idMapping = Maps.newHashMap();
blockRegistry.serializeInto(idMapping);
itemRegistry.serializeInto(idMapping);
getMain().iBlockRegistry.serializeInto(idMapping);
getMain().iItemRegistry.serializeInto(idMapping);
return idMapping;
}
static Item findItem(String modId, String name)
{
return (Item) itemRegistry.func_82594_a(modId + ":" + name);
}
static Block findBlock(String modId, String name)
{
String key = modId + ":" + name;
return blockRegistry.contains(key) ? blockRegistry.func_82594_a(key) : null;
}
static ItemStack findItemStack(String modId, String name)
{
ItemStack is = customItemStacks.get(modId, name);
if (is == null)
{
Item i = findItem(modId, name);
if (i != null)
{
is = new ItemStack(i, 0 ,0);
}
}
if (is == null)
{
Block b = findBlock(modId, name);
if (b != null)
{
is = new ItemStack(b, 0, Short.MAX_VALUE);
}
}
return is;
}
static void registerCustomItemStack(String name, ItemStack itemStack)
{
customItemStacks.put(Loader.instance().activeModContainer().getModId(), name, itemStack);
}
public static void dumpRegistry(File minecraftDir)
{
if (customItemStacks == null)
@ -124,10 +124,48 @@ public class GameData {
}
}
static Item findItem(String modId, String name)
{
return (Item) getMain().iItemRegistry.func_82594_a(modId + ":" + name);
}
static Block findBlock(String modId, String name)
{
String key = modId + ":" + name;
return getMain().iBlockRegistry.contains(key) ? getMain().iBlockRegistry.func_82594_a(key) : null;
}
static ItemStack findItemStack(String modId, String name)
{
ItemStack is = customItemStacks.get(modId, name);
if (is == null)
{
Item i = findItem(modId, name);
if (i != null)
{
is = new ItemStack(i, 0 ,0);
}
}
if (is == null)
{
Block b = findBlock(modId, name);
if (b != null)
{
is = new ItemStack(b, 0, Short.MAX_VALUE);
}
}
return is;
}
static void registerCustomItemStack(String name, ItemStack itemStack)
{
customItemStacks.put(Loader.instance().activeModContainer().getModId(), name, itemStack);
}
static UniqueIdentifier getUniqueName(Block block)
{
if (block == null) return null;
String name = blockRegistry.func_148750_c(block);
String name = getMain().iBlockRegistry.func_148750_c(block);
UniqueIdentifier ui = new UniqueIdentifier(name);
if (customItemStacks.contains(ui.modId, ui.name))
{
@ -140,7 +178,7 @@ public class GameData {
static UniqueIdentifier getUniqueName(Item item)
{
if (item == null) return null;
String name = itemRegistry.func_148750_c(item);
String name = getMain().iItemRegistry.func_148750_c(item);
UniqueIdentifier ui = new UniqueIdentifier(name);
if (customItemStacks.contains(ui.modId, ui.name))
{
@ -150,181 +188,120 @@ public class GameData {
return ui;
}
private static Map<UniqueIdentifier, ModContainer> customOwners = Maps.newHashMap();
static void registerBlockAndItem(ItemBlock item, Block block, String name, String modId)
{
ModContainer mc = Loader.instance().activeModContainer();
if (modId != null)
{
customOwners.put(new UniqueIdentifier(modId, name), mc);
}
BitSet blockAvailability = blockRegistry.slots();
BitSet itemAvailability = itemRegistry.slots();
blockAvailability.or(itemAvailability);
int blockId = blockAvailability.nextClearBit(0);
if (blockId >= blockRegistry.maxId)
{
throw new RuntimeException(String.format("No more space for block allocations: used %d block ids", blockId -1));
}
int actualBlockId = blockRegistry.add(blockId, name, block);
int itemId = itemRegistry.add(blockId, name, item);
if (blockId != actualBlockId || itemId != blockId)
{
throw new RuntimeException(String.format("There was a failure to allocate a matching block and item pair for %s: requested %d, got %d and %d", name, blockId, actualBlockId, itemId));
}
}
static void registerItem(Item item, String name, String modId)
{
ModContainer mc = Loader.instance().activeModContainer();
if (modId != null)
{
customOwners.put(new UniqueIdentifier(modId, name), mc);
}
if (item instanceof ItemBlock)
{
throw new RuntimeException("Cannot register an itemblock separately from it's block");
}
int itemId = itemRegistry.add(0, name, item);
blockRegistry.useSlot(itemId);
}
static void registerBlock(Block block, String name, String modId)
{
ModContainer mc = Loader.instance().activeModContainer();
if (modId != null)
{
customOwners.put(new UniqueIdentifier(modId, name), mc);
}
int blockId = blockRegistry.add(0, name, block);
itemRegistry.useSlot(blockId);
}
public static ModContainer findModOwner(String string)
{
UniqueIdentifier ui = new UniqueIdentifier(string);
if (customOwners.containsKey(ui))
{
return customOwners.get(ui);
}
return Loader.instance().getIndexedModList().get(ui.modId);
}
public static void fixupRegistries()
{
for (Integer id : blockRegistry.usedIds())
{
itemRegistry.useSlot(id);
}
for (Integer id : itemRegistry.usedIds())
{
blockRegistry.useSlot(id);
}
}
public static List<String> injectWorldIDMap(Map<String, Integer> dataList, boolean injectFrozenData, boolean isLocalWorld)
{
FMLLog.info("Injecting existing block and item data into this %s instance", FMLCommonHandler.instance().getEffectiveSide().isServer() ? "server" : "client");
Map<String, Integer[]> remaps = Maps.newHashMap();
ArrayListMultimap<String,String> missing = ArrayListMultimap.create();
blockRegistry.dump();
itemRegistry.dump();
blockRegistry.beginIdSwap();
itemRegistry.beginIdSwap();
for (Entry<String, Integer> entry : dataList.entrySet())
ArrayListMultimap<String,String> missingMappings = ArrayListMultimap.create();
getMain().testConsistency();
getMain().iBlockRegistry.dump();
getMain().iItemRegistry.dump();
GameData newData = new GameData();
// process blocks and items in the world, blocks in the first pass, items in the second
// blocks need to be added first for proper ItemBlock handling
for (int pass = 0; pass < 2; pass++)
{
String itemName = entry.getKey();
char discriminator = itemName.charAt(0);
itemName = itemName.substring(1);
Integer newId = entry.getValue();
int currId;
boolean isBlock = discriminator == '\u0001';
if (isBlock)
{
currId = blockRegistry.getId(itemName);
}
else
{
currId = itemRegistry.getId(itemName);
}
boolean isBlock = (pass == 0);
if (currId == -1)
for (Entry<String, Integer> entry : dataList.entrySet())
{
FMLLog.info("Found a missing id from the world %s", itemName);
missing.put(itemName.substring(0, itemName.indexOf(':')), itemName);
}
else if (currId != newId)
{
FMLLog.info("Found %s id mismatch %s : %d %d", isBlock ? "block" : "item", itemName, currId, newId);
remaps.put(itemName, new Integer[] { currId, newId });
}
String itemName = entry.getKey();
int newId = entry.getValue();
if (isBlock)
{
blockRegistry.reassignMapping(itemName, newId);
}
else
{
itemRegistry.reassignMapping(itemName, newId);
}
}
List<String> missedMappings = Loader.instance().fireMissingMappingEvent(missing, isLocalWorld);
if (!missedMappings.isEmpty())
{
blockRegistry.revertSwap();
itemRegistry.revertSwap();
return missedMappings;
}
// names starting with 0x1 are blocks, skip if the type isn't handled by this pass
if ((itemName.charAt(0) == '\u0001') != isBlock) continue;
if (injectFrozenData)
{
FMLLog.info("Injecting new block and item data into this server instance");
Map<String, Integer> missingBlocks = Maps.newHashMap(blockRegistry.getMissingMappings());
Map<String, Integer> missingItems = Maps.newHashMap(itemRegistry.getMissingMappings());
itemName = itemName.substring(1);
int currId = isBlock ? getMain().iBlockRegistry.getId(itemName) : getMain().iItemRegistry.getId(itemName);
for (Entry<String, Integer> item: missingItems.entrySet())
{
String itemName = item.getKey();
if (missingBlocks.containsKey(itemName))
if (currId == -1)
{
int blockId = blockRegistry.swap(item.getValue(), itemName, blockRegistry.get(itemName));
itemRegistry.swap(blockId, itemName, itemRegistry.get(itemName));
FMLLog.info("Injecting new block/item %s : %d", itemName, blockId);
missingBlocks.remove(itemName);
if (Integer.valueOf(blockId) != item.getValue())
{
remaps.put(itemName, new Integer[] { item.getValue(), blockId });
}
FMLLog.info("Found a missing id from the world %s", itemName);
missingMappings.put(itemName.substring(0, itemName.indexOf(':')), itemName);
continue; // no block/item -> nothing to add
}
else if (currId != newId)
{
FMLLog.info("Found %s id mismatch %s : %d (was %d)", isBlock ? "block" : "item", itemName, currId, newId);
remaps.put(itemName, new Integer[] { currId, newId });
}
if (isBlock)
{
Block block = getMain().iBlockRegistry.getRaw(itemName);
if (block == null) throw new IllegalStateException(String.format("Can't find block for name %s, id %d", itemName, currId));
currId = newData.registerBlock(block, itemName, null, newId);
}
else
{
FMLLog.info("Injecting new item %s", itemName);
int itemId = itemRegistry.swap(item.getValue(), itemName, itemRegistry.get(itemName));
if (Integer.valueOf(itemId) != item.getValue())
{
remaps.put(itemName, new Integer[] { item.getValue(), itemId });
}
Item item = getMain().iItemRegistry.getRaw(itemName);
if (item == null) throw new IllegalStateException(String.format("Can't find item for name %s, id %d", itemName, currId));
currId = newData.registerItem(item, itemName, null, newId);
}
}
for (Entry<String, Integer> block : missingBlocks.entrySet())
{
FMLLog.info("Injecting new block %s", block.getKey());
int blockId = blockRegistry.swap(block.getValue(), block.getKey(), blockRegistry.get(block.getKey()));
if (Integer.valueOf(blockId) != block.getValue())
if (currId != newId)
{
remaps.put(block.getKey(), new Integer[] { block.getValue(), blockId });
throw new IllegalStateException(String.format("Can't map %s %s to id %d, already occupied by %s",
isBlock ? "block" : "item",
itemName,
newId,
isBlock ? newData.iBlockRegistry.get(newId) : newData.iItemRegistry.get(newId)));
}
}
}
blockRegistry.completeIdSwap();
itemRegistry.completeIdSwap();
blockRegistry.dump();
itemRegistry.dump();
List<String> missedMappings = Loader.instance().fireMissingMappingEvent(missingMappings, isLocalWorld);
if (!missedMappings.isEmpty()) return missedMappings;
if (injectFrozenData) // add blocks + items missing from the map
{
FMLLog.info("Injecting new block and item data into this server instance");
Map<String, Integer> missingBlocks = frozen.iBlockRegistry.getEntriesNotIn(newData.iBlockRegistry);
Map<String, Integer> missingItems = frozen.iItemRegistry.getEntriesNotIn(newData.iItemRegistry);
for (int pass = 0; pass < 2; pass++)
{
boolean isBlock = pass == 0;
Map<String, Integer> missing = (pass == 0) ? missingBlocks : missingItems;
for (Entry<String, Integer> entry : missing.entrySet())
{
String itemName = entry.getKey();
int currId = entry.getValue();
int newId;
if (isBlock)
{
newId = newData.registerBlock(frozen.iBlockRegistry.get(itemName), itemName, null, currId);
}
else
{
newId = newData.registerItem(frozen.iItemRegistry.get(itemName), itemName, null, currId);
}
FMLLog.info("Injected new block/item %s : %d (was %d)", itemName, newId, currId);
if (newId != currId) // a new id was assigned
{
remaps.put(itemName, new Integer[] { entry.getValue(), newId });
}
}
}
}
newData.testConsistency();
getMain().set(newData);
getMain().iBlockRegistry.dump();
getMain().iItemRegistry.dump();
Loader.instance().fireRemapEvent(remaps);
return ImmutableList.of();
}
public static List<String> processIdRematches(List<MissingMapping> remaps, boolean isLocalWorld)
{
List<String> failed = Lists.newArrayList();
@ -367,15 +344,206 @@ public class GameData {
public static void freezeData()
{
FMLLog.fine("Freezing block and item id maps");
blockRegistry.freezeMap();
itemRegistry.freezeMap();
frozen = new GameData(getMain());
frozen.testConsistency();
}
public static void revertToFrozen()
{
FMLLog.fine("Reverting to frozen data state");
blockRegistry.revertToFrozen();
itemRegistry.revertToFrozen();
if (frozen == null)
{
FMLLog.warning("Can't revert to frozen GameData state without freezing first.");
}
else
{
FMLLog.fine("Reverting to frozen data state.");
getMain().set(frozen);
}
}
protected static GameData getMain()
{
return mainData;
}
// internal registry objects
private final FMLControlledNamespacedRegistry<Block> iBlockRegistry;
private final FMLControlledNamespacedRegistry<Item> iItemRegistry;
// bit set marking ids as occupied
private final BitSet availabilityMap;
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);
}
private GameData(GameData data)
{
this();
set(data);
}
private void set(GameData data)
{
iBlockRegistry.set(data.iBlockRegistry);
iItemRegistry.set(data.iItemRegistry);
availabilityMap.clear();
availabilityMap.or(data.availabilityMap);
}
void register(Object obj, String name, int idHint)
{
if (obj instanceof Block)
{
registerBlock((Block) obj, name, null, idHint);
}
else if (obj instanceof Item)
{
registerItem((Item) obj, name, null, idHint);
}
else
{
throw new IllegalArgumentException("An invalid registry object is to be added, only instances of Block or Item are allowed.");
}
}
int registerItem(Item item, String name, String modId)
{
return registerItem(item, name, modId, 0);
}
int registerItem(Item item, String name, String modId, int idHint)
{
if (modId != null)
{
ModContainer mc = Loader.instance().activeModContainer();
customOwners.put(new UniqueIdentifier(modId, name), mc);
}
if (item instanceof ItemBlock)
{
// ItemBlock, clear the item slot already occupied by the corresponding block
idHint = iBlockRegistry.getId(((ItemBlock) item).field_150939_a);
if (idHint == -1)
{
throw new RuntimeException("Cannot register an itemblock before its block");
}
if (iItemRegistry.get(idHint) != null)
{
throw new IllegalStateException(String.format("The Item Registry slot %d is already used by %s", idHint, iItemRegistry.get(idHint)));
}
if (!freeSlot(idHint)) // temporarily free the slot occupied by the Block for the ItemBlock registration
{
throw new IllegalStateException(String.format("The Registry slot %d is supposed to be blocked by the ItemBlock's Block's blockId at this point.", idHint));
}
}
int itemId = iItemRegistry.add(idHint, name, item, availabilityMap);
if (item instanceof ItemBlock)
{
if (itemId != idHint) // just in case of bugs...
{
throw new IllegalStateException("ItemBlock insertion failed.");
}
}
// normal item, block the Block Registry slot with the same id
if (useSlot(itemId))
{
throw new IllegalStateException(String.format("Registry slot %d is supposed to be empty when adding a non-ItemBlock with the same id.", itemId));
}
return itemId;
}
int registerBlock(Block block, String name, String modId)
{
return registerBlock(block, name, modId, 0);
}
int registerBlock(Block block, String name, String modId, int idHint)
{
if (modId != null)
{
ModContainer mc = Loader.instance().activeModContainer();
customOwners.put(new UniqueIdentifier(modId, name), mc);
}
int blockId = iBlockRegistry.add(idHint, name, block, availabilityMap);
if (useSlot(blockId))
{
throw new IllegalStateException(String.format("Registry slot %d is supposed to be empty when adding a Block with the same id.", blockId));
}
return blockId;
}
private boolean useSlot(int id)
{
boolean oldValue = availabilityMap.get(id);
availabilityMap.set(id);
return oldValue;
}
private boolean freeSlot(int id)
{
boolean oldValue = availabilityMap.get(id);
availabilityMap.clear(id);
return oldValue;
}
private void testConsistency() {
// test if there's an entry for every set bit in availabilityMap
for (int i = availabilityMap.nextSetBit(0); i >= 0; i = availabilityMap.nextSetBit(i+1))
{
if (iBlockRegistry.getRaw(i) == null && iItemRegistry.getRaw(i) == null)
{
throw new IllegalStateException(String.format("availabilityMap references empty entries for id %d.", i));
}
}
// test if there's a bit in availabilityMap set for every entry in the block registry
for (Iterator<Object> it = iBlockRegistry.iterator(); it.hasNext(); )
{
Block block = (Block) it.next();
int id = iBlockRegistry.getId(block);
if (!availabilityMap.get(id))
{
throw new IllegalStateException(String.format("Registry entry for block %s, id %d, marked as empty.", block, id));
}
}
// test if there's a bit in availabilityMap set for every entry in the item registry,
// check if ItemBlocks have blocks with matching ids in the block registry
for (Iterator<Object> it = iItemRegistry.iterator(); it.hasNext(); )
{
Item item = (Item) it.next();
int id = iItemRegistry.getId(item);
if (!availabilityMap.get(id))
{
throw new IllegalStateException(String.format("Registry entry for item %s, id %d, marked as empty.", item, id));
}
if (item instanceof ItemBlock)
{
Block block = ((ItemBlock) item).field_150939_a;
if (iBlockRegistry.getId(block) != id)
{
throw new IllegalStateException(String.format("Registry entry for ItemBlock %s, id %d, is missing or uses the non-matching id %d.", item, id, iBlockRegistry.getId(block)));
}
}
}
FMLLog.fine("Registry consistency check successful");
}
}

View file

@ -136,7 +136,7 @@ public class GameRegistry
*/
public static Item registerItem(Item item, String name, String modId)
{
GameData.registerItem(item, name, modId);
GameData.getMain().registerItem(item, name, modId);
return item;
}
@ -192,13 +192,11 @@ public class GameRegistry
Constructor<? extends ItemBlock> itemCtor = itemclass.getConstructor(ctorArgClasses);
i = itemCtor.newInstance(ObjectArrays.concat(block, itemCtorArgs));
}
// block registration has to happen first
GameData.getMain().registerBlock(block, name, modId);
if (i != null)
{
GameData.registerBlockAndItem(i, block, name, modId);
}
else
{
GameData.registerBlock(block, name, modId);
GameData.getMain().registerItem(i, name, modId);
}
return block;
}

View file

@ -74,6 +74,8 @@ public net.minecraft.client.gui.GuiScreen field_146297_k # minecraft instance -
# Minecraft
#public atv.D #FD:Minecraft/field_71425_J #running
public net.minecraft.client.Minecraft field_71446_o # textureManager
## ItemBlock
public net.minecraft.item.ItemBlock field_150939_a
protected net.minecraft.util.ObjectIntIdentityMap field_148749_a # internal map
protected net.minecraft.util.ObjectIntIdentityMap field_148748_b # internal index list