From e58562d3edfd1cd37fdc0a9e54181aed7433fdff Mon Sep 17 00:00:00 2001 From: Player Date: Thu, 20 Mar 2014 12:41:59 +0100 Subject: [PATCH] Fix remaining issue with autoassigned Block and Item IDs overlapping. --- .../net/minecraft/block/Block.java.patch | 2 +- .../net/minecraft/item/Item.java.patch | 2 +- src/main/java/cpw/mods/fml/common/Loader.java | 3 +- .../FMLControlledNamespacedRegistry.java | 267 +++----- .../mods/fml/common/registry/GameData.java | 576 +++++++++++------- .../fml/common/registry/GameRegistry.java | 10 +- src/main/resources/fml_at.cfg | 2 + 7 files changed, 455 insertions(+), 407 deletions(-) diff --git a/patches/minecraft/net/minecraft/block/Block.java.patch b/patches/minecraft/net/minecraft/block/Block.java.patch index 5f824bc2dd..7e49ded730 100644 --- a/patches/minecraft/net/minecraft/block/Block.java.patch +++ b/patches/minecraft/net/minecraft/block/Block.java.patch @@ -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); diff --git a/patches/minecraft/net/minecraft/item/Item.java.patch b/patches/minecraft/net/minecraft/item/Item.java.patch index f2a83cb9f7..c577fca1eb 100644 --- a/patches/minecraft/net/minecraft/item/Item.java.patch +++ b/patches/minecraft/net/minecraft/item/Item.java.patch @@ -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(); diff --git a/src/main/java/cpw/mods/fml/common/Loader.java b/src/main/java/cpw/mods/fml/common/Loader.java index c7be7ee627..b45ecf075b 100644 --- a/src/main/java/cpw/mods/fml/common/Loader.java +++ b/src/main/java/cpw/mods/fml/common/Loader.java @@ -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"); diff --git a/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java b/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java index b5cc9f16b3..1ae9d56b7f 100644 --- a/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -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 extends RegistryNamespaced { - static class FMLObjectIntIdentityMap extends ObjectIntIdentityMap { - private TIntIntHashMap frozenMap; - private TIntIntHashMap oldMap; - private TIntIntHashMap newMap; - private ArrayList frozenIndex; - private ArrayList oldIndex; - private ArrayList 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) field_148748_b; - newIndex = new ArrayList(oldIndex.size()); - } - - @SuppressWarnings("unchecked") - void freezeMap() - { - frozenMap = new TIntIntHashMap(field_148749_a); - frozenIndex = new ArrayList(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 usedIds() - { - return Ints.asList(field_148749_a.keys()); - } - - } - private final Class superType; private String optionalDefaultName; private I optionalDefaultObject; - private BiMap namedIds = HashBiMap.create(); - private BiMap frozenIds; - private Map transactionalNamedIds; - private BitSet transactionalAvailabilityMap; - private BitSet availabilityMap; int maxId; private int minId; private char discriminator; - public FMLControlledNamespacedRegistry(String optionalDefault, int maxIdValue, int minIdValue, Class type, char discriminator) + FMLControlledNamespacedRegistry(String optionalDefault, int maxIdValue, int minIdValue, Class 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 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 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 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 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 extends RegistryNamespaced { return (BiMap) field_82596_a; } - void beginIdSwap() + Map getEntriesNotIn(FMLControlledNamespacedRegistry registry) { - idMap().beginSwap(); - transactionalNamedIds = Maps.newHashMap(); - transactionalAvailabilityMap = new BitSet(); - } + Map ret = new HashMap(); - 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 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 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 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 idMapping) { - for (Entry id: namedIds.entrySet()) + for (Iterator 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 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 entry : namedIds.entrySet()) + List ids = new ArrayList(); + + for (Iterator 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(); - } } diff --git a/src/main/java/cpw/mods/fml/common/registry/GameData.java b/src/main/java/cpw/mods/fml/common/registry/GameData.java index c245791ab4..e3d549f22d 100644 --- a/src/main/java/cpw/mods/fml/common/registry/GameData.java +++ b/src/main/java/cpw/mods/fml/common/registry/GameData.java @@ -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 customItemStacks = HashBasedTable.create(); + private static final GameData mainData = new GameData(); - public static final FMLControlledNamespacedRegistry blockRegistry = new FMLControlledNamespacedRegistry("air", 4095, 0, Block.class,'\u0001'); - public static final FMLControlledNamespacedRegistry itemRegistry = new FMLControlledNamespacedRegistry(null, 32000, 4096, Item.class,'\u0002'); + /** + * @deprecated use {@link getBlockRegistry()} instead. + */ + @Deprecated + public static final FMLControlledNamespacedRegistry blockRegistry = getBlockRegistry(); + /** + * @deprecated use {@link getItemRegistry()} instead. + */ + @Deprecated + public static final FMLControlledNamespacedRegistry itemRegistry = getItemRegistry(); + + private static Table customItemStacks = HashBasedTable.create(); + private static Map customOwners = Maps.newHashMap(); + private static GameData frozen; + + // public api + + public static FMLControlledNamespacedRegistry getBlockRegistry() { + return getMain().iBlockRegistry; + } + + public static FMLControlledNamespacedRegistry 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 buildItemDataList() { Map 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 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 injectWorldIDMap(Map dataList, boolean injectFrozenData, boolean isLocalWorld) { + FMLLog.info("Injecting existing block and item data into this %s instance", FMLCommonHandler.instance().getEffectiveSide().isServer() ? "server" : "client"); Map remaps = Maps.newHashMap(); - ArrayListMultimap missing = ArrayListMultimap.create(); - blockRegistry.dump(); - itemRegistry.dump(); - blockRegistry.beginIdSwap(); - itemRegistry.beginIdSwap(); - for (Entry entry : dataList.entrySet()) + ArrayListMultimap 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 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 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 missingBlocks = Maps.newHashMap(blockRegistry.getMissingMappings()); - Map missingItems = Maps.newHashMap(itemRegistry.getMissingMappings()); + itemName = itemName.substring(1); + int currId = isBlock ? getMain().iBlockRegistry.getId(itemName) : getMain().iItemRegistry.getId(itemName); - for (Entry 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 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 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 missingBlocks = frozen.iBlockRegistry.getEntriesNotIn(newData.iBlockRegistry); + Map missingItems = frozen.iItemRegistry.getEntriesNotIn(newData.iItemRegistry); + + for (int pass = 0; pass < 2; pass++) + { + boolean isBlock = pass == 0; + Map missing = (pass == 0) ? missingBlocks : missingItems; + + for (Entry 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 processIdRematches(List remaps, boolean isLocalWorld) { List 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 iBlockRegistry; + private final FMLControlledNamespacedRegistry iItemRegistry; + // bit set marking ids as occupied + private final BitSet availabilityMap; + + private GameData() + { + iBlockRegistry = new FMLControlledNamespacedRegistry("air", 4095, 0, Block.class,'\u0001'); + iItemRegistry = new FMLControlledNamespacedRegistry(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 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 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"); + } } diff --git a/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java b/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java index 21da07b343..cf5913ede8 100644 --- a/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java +++ b/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java @@ -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 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; } diff --git a/src/main/resources/fml_at.cfg b/src/main/resources/fml_at.cfg index 3e86a81a89..b775c517a1 100644 --- a/src/main/resources/fml_at.cfg +++ b/src/main/resources/fml_at.cfg @@ -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