From dddbbd3bac849b9b5e553e9bd6d4b437b9bdbdff Mon Sep 17 00:00:00 2001 From: cpw Date: Wed, 20 Aug 2014 10:28:39 -0400 Subject: [PATCH] ItemStack swapping --- .../cpw/mods/fml/common/FMLContainer.java | 50 ++++++----- .../common/event/FMLMissingMappingsEvent.java | 23 ++++- .../handshake/FMLHandshakeClientState.java | 2 +- .../handshake/FMLHandshakeMessage.java | 48 ++++++++++- .../registry/ExistingAliasException.java | 9 -- .../ExistingSubstitutionException.java | 9 ++ .../FMLControlledNamespacedRegistry.java | 85 +++++++++++++++---- .../mods/fml/common/registry/GameData.java | 85 +++++++++++++++++-- .../fml/common/registry/GameRegistry.java | 19 +++-- .../IncompatibleSubstitutionException.java | 10 +++ .../fml/common/registry/RegistryDelegate.java | 54 ++++++++++-- 11 files changed, 313 insertions(+), 81 deletions(-) delete mode 100644 fml/src/main/java/cpw/mods/fml/common/registry/ExistingAliasException.java create mode 100644 fml/src/main/java/cpw/mods/fml/common/registry/ExistingSubstitutionException.java create mode 100644 fml/src/main/java/cpw/mods/fml/common/registry/IncompatibleSubstitutionException.java diff --git a/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java b/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java index 03f8e588f..14c05ac8f 100644 --- a/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java +++ b/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java @@ -30,9 +30,9 @@ import net.minecraft.world.storage.WorldInfo; import org.apache.logging.log4j.Level; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @@ -105,8 +105,8 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai // name <-> id mappings NBTTagList dataList = new NBTTagList(); FMLLog.fine("Gathering id map for writing to world save %s", info.getWorldName()); - Map itemList = GameData.buildItemDataList(); - for (Entry item : itemList.entrySet()) + GameData.GameDataSnapshot dataSnapshot = GameData.buildItemDataList(); + for (Entry item : dataSnapshot.idMap.entrySet()) { NBTTagCompound tag = new NBTTagCompound(); tag.setString("K",item.getKey()); @@ -126,15 +126,14 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai blockAliasList.appendTag(tag); } fmlData.setTag("BlockAliases", blockAliasList); - NBTTagList blockPersistentAliasList = new NBTTagList(); - for (Entry entry : GameData.getBlockRegistry().getPersistentAliases().entrySet()) + NBTTagList blockSubstitutionsList = new NBTTagList(); + for (String entry : dataSnapshot.blockSubstitutions) { NBTTagCompound tag = new NBTTagCompound(); - tag.setString("K", entry.getKey()); - tag.setString("V", entry.getValue()); - blockPersistentAliasList.appendTag(tag); + tag.setString("K", entry); + blockSubstitutionsList.appendTag(tag); } - fmlData.setTag("PersistentBlockAliases", blockPersistentAliasList); + fmlData.setTag("BlockSubstitutions", blockSubstitutionsList); // item aliases NBTTagList itemAliasList = new NBTTagList(); for (Entry entry : GameData.getItemRegistry().getAliases().entrySet()) @@ -146,15 +145,14 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai } fmlData.setTag("ItemAliases", itemAliasList); - NBTTagList itemPersistentAliasList = new NBTTagList(); - for (Entry entry : GameData.getItemRegistry().getPersistentAliases().entrySet()) + NBTTagList itemSubstitutionsList = new NBTTagList(); + for (String entry : dataSnapshot.itemSubstitutions) { NBTTagCompound tag = new NBTTagCompound(); - tag.setString("K", entry.getKey()); - tag.setString("V", entry.getValue()); - itemPersistentAliasList.appendTag(tag); + tag.setString("K", entry); + itemSubstitutionsList.appendTag(tag); } - fmlData.setTag("ItemPersistentAliases", itemPersistentAliasList); + fmlData.setTag("ItemSubstitutions", itemSubstitutionsList); return fmlData; } @@ -209,7 +207,7 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai dataList.put(itemLabel, itemId); } } - failedElements = GameData.injectWorldIDMap(dataList, true, true); + failedElements = GameData.injectWorldIDMap(dataList, ImmutableSet.of(), ImmutableSet.of(), true, true); } else if (tag.hasKey("ItemData")) @@ -245,14 +243,14 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai NBTTagCompound dataTag = list.getCompoundTagAt(i); blockAliases.put(dataTag.getString("K"), dataTag.getString("V")); } - BiMap blockPersistentAliases = HashBiMap.create(); - if (tag.hasKey("BlockPersistentAliases", 10)) + Set blockSubstitutions = Sets.newHashSet(); + if (tag.hasKey("BlockSubstitutions", 9)) { - list = tag.getTagList("BlockPersistentAliases", 10); + list = tag.getTagList("BlockSubstitutions", 10); for (int i = 0; i < list.tagCount(); i++) { NBTTagCompound dataTag = list.getCompoundTagAt(i); - blockPersistentAliases.put(dataTag.getString("K"), dataTag.getString("V")); + blockSubstitutions.add(dataTag.getString("K")); } } // item aliases @@ -264,17 +262,17 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai itemAliases.put(dataTag.getString("K"), dataTag.getString("V")); } - BiMap itemPersistentAliases = HashBiMap.create(); - if (tag.hasKey("ItemPersistentAliases", 10)) + Set itemSubstitutions = Sets.newHashSet(); + if (tag.hasKey("ItemSubstitutions", 9)) { - list = tag.getTagList("ItemPersistentAliases", 10); + list = tag.getTagList("ItemSubstitutions", 10); for (int i = 0; i < list.tagCount(); i++) { NBTTagCompound dataTag = list.getCompoundTagAt(i); - itemPersistentAliases.put(dataTag.getString("K"), dataTag.getString("V")); + itemSubstitutions.add(dataTag.getString("K")); } } - failedElements = GameData.injectWorldIDMap(dataList, blockedIds, blockAliases, itemAliases, true, true); + failedElements = GameData.injectWorldIDMap(dataList, blockedIds, blockAliases, itemAliases, blockSubstitutions, itemSubstitutions, true, true); } if (failedElements != null && !failedElements.isEmpty()) diff --git a/fml/src/main/java/cpw/mods/fml/common/event/FMLMissingMappingsEvent.java b/fml/src/main/java/cpw/mods/fml/common/event/FMLMissingMappingsEvent.java index fdaac1d9b..94ea8e3ea 100644 --- a/fml/src/main/java/cpw/mods/fml/common/event/FMLMissingMappingsEvent.java +++ b/fml/src/main/java/cpw/mods/fml/common/event/FMLMissingMappingsEvent.java @@ -32,7 +32,28 @@ public class FMLMissingMappingsEvent extends FMLEvent { * @author cpw * */ - public static enum Action { DEFAULT, IGNORE, WARN, FAIL, REMAP } + public static enum Action { + /** + * Take the default action + */ + DEFAULT, + /** + * Ignore this missing mapping. This means the mapping will be abandoned + */ + IGNORE, + /** + * Generate a warning but allow loading to continue + */ + WARN, + /** + * Fail to load + */ + FAIL, + /** + * Remap this name to a new name (add a migration mapping) + */ + REMAP + } public static class MissingMapping { public final GameRegistry.Type type; public final String name; diff --git a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java index d463939ec..12ed5f369 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java @@ -85,7 +85,7 @@ enum FMLHandshakeClientState implements IHandshakeState public FMLHandshakeClientState accept(ChannelHandlerContext ctx, FMLHandshakeMessage msg) { FMLHandshakeMessage.ModIdData modIds = (FMLHandshakeMessage.ModIdData)msg; - List locallyMissing = GameData.injectWorldIDMap(modIds.dataList(), false, false); + List locallyMissing = GameData.injectWorldIDMap(modIds.dataList(), modIds.blockSubstitutions(), modIds.itemSubstitutions(), false, false); if (!locallyMissing.isEmpty()) { NetworkDispatcher dispatcher = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); diff --git a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java index 1c440fe1e..d9987034b 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java @@ -13,11 +13,13 @@ import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.network.internal.FMLProxyPacket; +import cpw.mods.fml.common.registry.GameData; public abstract class FMLHandshakeMessage { public static FMLProxyPacket makeCustomChannelRegistration(Set channels) @@ -128,22 +130,43 @@ public abstract class FMLHandshakeMessage { } - public ModIdData(Map modIds) + public ModIdData(GameData.GameDataSnapshot snapshot) { - this.modIds = modIds; + this.modIds = snapshot.idMap; + this.blockSubstitutions = snapshot.blockSubstitutions; + this.itemSubstitutions = snapshot.itemSubstitutions; } private Map modIds; + private Set blockSubstitutions; + private Set itemSubstitutions; @Override public void fromBytes(ByteBuf buffer) { int length = ByteBufUtils.readVarInt(buffer, 3); modIds = Maps.newHashMap(); + blockSubstitutions = Sets.newHashSet(); + itemSubstitutions = Sets.newHashSet(); for (int i = 0; i < length; i++) { modIds.put(ByteBufUtils.readUTF8String(buffer),ByteBufUtils.readVarInt(buffer, 3)); } + // we don't have any more data to read + if (!buffer.isReadable()) + { + return; + } + length = ByteBufUtils.readVarInt(buffer, 3); + for (int i = 0; i < length; i++) + { + blockSubstitutions.add(ByteBufUtils.readUTF8String(buffer)); + } + length = ByteBufUtils.readVarInt(buffer, 3); + for (int i = 0; i < length; i++) + { + itemSubstitutions.add(ByteBufUtils.readUTF8String(buffer)); + } } @Override @@ -155,12 +178,33 @@ public abstract class FMLHandshakeMessage { ByteBufUtils.writeUTF8String(buffer, entry.getKey()); ByteBufUtils.writeVarInt(buffer, entry.getValue(), 3); } + + ByteBufUtils.writeVarInt(buffer, blockSubstitutions.size(), 3); + for (String entry: blockSubstitutions) + { + ByteBufUtils.writeUTF8String(buffer, entry); + } + ByteBufUtils.writeVarInt(buffer, blockSubstitutions.size(), 3); + + for (String entry: itemSubstitutions) + { + ByteBufUtils.writeUTF8String(buffer, entry); + } } public Map dataList() { return modIds; } + public Set blockSubstitutions() + { + return blockSubstitutions; + } + public Set itemSubstitutions() + { + return itemSubstitutions; + } + @Override public String toString(Class> side) { diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/ExistingAliasException.java b/fml/src/main/java/cpw/mods/fml/common/registry/ExistingAliasException.java deleted file mode 100644 index aedd66bce..000000000 --- a/fml/src/main/java/cpw/mods/fml/common/registry/ExistingAliasException.java +++ /dev/null @@ -1,9 +0,0 @@ -package cpw.mods.fml.common.registry; - -public class ExistingAliasException extends Exception { - public ExistingAliasException(String fromName, String toName) { - } - - private static final long serialVersionUID = 1L; - -} diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/ExistingSubstitutionException.java b/fml/src/main/java/cpw/mods/fml/common/registry/ExistingSubstitutionException.java new file mode 100644 index 000000000..ec8f614fc --- /dev/null +++ b/fml/src/main/java/cpw/mods/fml/common/registry/ExistingSubstitutionException.java @@ -0,0 +1,9 @@ +package cpw.mods.fml.common.registry; + +public class ExistingSubstitutionException extends Exception { + public ExistingSubstitutionException(String fromName, Object toReplace) { + } + + private static final long serialVersionUID = 1L; + +} 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 8503c5132..ec4af988c 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 @@ -15,7 +15,6 @@ 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.ImmutableMap; import cpw.mods.fml.common.FMLLog; @@ -31,7 +30,8 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { // aliases redirecting legacy names to the actual name, may need recursive application to find the final name. // these need to be registry specific, it's possible to only have a loosely linked item for a block which may get renamed by itself. private final Map aliases = new HashMap(); - private BiMap persistentAliases = HashBiMap.create(); + private BiMap persistentSubstitutions; + private BiMap activeSubstitutions = HashBiMap.create(); FMLControlledNamespacedRegistry(String optionalDefault, int maxIdValue, int minIdValue, Class type, char discriminator) { @@ -62,9 +62,9 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { // id -> obj lookup is inconsistent if (getRaw(id) != obj) throw new IllegalStateException(String.format("Registry entry for id %d, name %s, doesn't yield the expected %s %s.", id, name, type, obj)); // name -> obj lookup is inconsistent - if (getRaw(name) != obj) throw new IllegalStateException(String.format("Registry entry for name %s, id %d, doesn't yield the expected %s %s.", name, id, type, obj)); + if (!(activeSubstitutions.containsKey(name) || activeSubstitutions.containsValue(name)) && getRaw(name) != obj ) throw new IllegalStateException(String.format("Registry entry for name %s, id %d, doesn't yield the expected %s %s.", name, id, type, obj)); // name -> id lookup is inconsistent - if (getId(name) != id) throw new IllegalStateException(String.format("Registry entry for name %s doesn't yield the expected id %d.", name, id)); + if (!(activeSubstitutions.containsKey(name) || activeSubstitutions.containsValue(name)) && getId(name) != id) throw new IllegalStateException(String.format("Registry entry for name %s doesn't yield the expected id %d.", name, id)); // id isn't marked as unavailable if (!availabilityMap.get(id)) throw new IllegalStateException(String.format("Registry entry for %s %s, id %d, name %s, marked as empty.", type, obj, id, name)); // entry is blocked, thus should be empty @@ -235,9 +235,6 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { */ public I getRaw(String name) { - String aliasName = persistentAliases.get(name); - name = aliasName != null ? aliasName : name; - I ret = superType.cast(super.getObject(name)); if (ret == null) // no match, try aliases recursively @@ -322,10 +319,6 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { return ImmutableMap.copyOf(aliases); } - public Map getPersistentAliases() - { - return ImmutableBiMap.copyOf(persistentAliases); - } /** * Add the specified object to the registry. * @@ -345,7 +338,10 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { { this.optionalDefaultObject = thing; } - + if (getPersistentSubstitutions().containsValue(thing)) + { + throw new IllegalArgumentException(String.format("The object %s (%s) cannot be added to the registry. It is already being used as a substitute for %s", thing.getClass(), name, getPersistentSubstitutions().inverse().get(thing))); + } int idToUse = id; if (idToUse < 0 || availabilityMap.get(idToUse)) { @@ -365,7 +361,7 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { { throw new IllegalArgumentException(String.format("The name %s has been registered twice, for %s and %s.", name, getRaw(name), thing)); } - if (getId(thing) >= 0) // duplicate object + if (getId(thing) >= 0) // duplicate object - but only if it's not being substituted { int foundId = getId(thing); Object otherThing = getRaw(foundId); @@ -376,6 +372,10 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { FMLLog.bigWarning("The object %s (name %s) is being added too late.", thing, name); } + if (activeSubstitutions.containsKey(name)) + { + thing = activeSubstitutions.get(name); + } addObjectRaw(idToUse, name, thing); FMLLog.finer("Registry add: %s %d %s (req. id %d)", name, idToUse, thing, id); @@ -394,7 +394,13 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { for (I thing : this.typeSafeIterable()) { - if (!registry.field_148758_b.containsKey(thing)) ret.put(getNameForObject(thing), getId(thing)); + if (!registry.field_148758_b.containsKey(thing)) + { + if (!registry.activeSubstitutions.containsKey(getNameForObject(thing))) + { + ret.put(getNameForObject(thing), getId(thing)); + } + } } return ret; @@ -441,11 +447,54 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { return GameData.buildDelegate(thing, clazz); } - public void addPersistentAlias(String fromName, String toName) throws ExistingAliasException { - if (persistentAliases.containsKey(fromName) || persistentAliases.containsKey(toName) || persistentAliases.containsValue(fromName) || persistentAliases.containsValue(toName)) + void activateSubstitution(String nameToReplace) + { + if (getPersistentSubstitutions().containsKey(nameToReplace)) { - throw new ExistingAliasException(fromName, toName); + activeSubstitutions.put(nameToReplace, getPersistentSubstitutions().get(nameToReplace)); } - persistentAliases.put(fromName, toName); + } + + public void addSubstitutionAlias(String modId, String nameToReplace, Object toReplace) throws ExistingSubstitutionException { + if (getPersistentSubstitutions().containsKey(nameToReplace) || getPersistentSubstitutions().containsValue(toReplace)) + { + FMLLog.severe("The substitution of %s has already occured. You cannot duplicate substitutions", nameToReplace); + throw new ExistingSubstitutionException(nameToReplace, toReplace); + } + I replacement = superType.cast(toReplace); + I original = getRaw(nameToReplace); + if (!original.getClass().isAssignableFrom(replacement.getClass())) + { + FMLLog.severe("The substitute %s for %s (type %s) is type incompatible. This won't work", replacement.getClass().getName(), nameToReplace, original.getClass().getName()); + throw new IncompatibleSubstitutionException(nameToReplace, replacement, original); + } + int existingId = getId(replacement); + if (existingId != -1) + { + FMLLog.severe("The substitute %s for %s is registered into the game independently. This won't work", replacement.getClass().getName(), nameToReplace); + throw new IllegalArgumentException("The object substitution is already registered. This won't work"); + } + getPersistentSubstitutions().put(nameToReplace, replacement); + } + + public void serializeSubstitutions(Set blockSubs) + { + blockSubs.addAll(activeSubstitutions.keySet()); + } + + @Override + public int getIDForObject(Object p_148757_1_) + { + + int id = super.getIDForObject(p_148757_1_); + return id; + } + private BiMap getPersistentSubstitutions() + { + if (persistentSubstitutions == null) + { + persistentSubstitutions = GameData.getMain().getPersistentSubstitutionMap(superType); + } + return persistentSubstitutions; } } 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 53949893d..816ab0e49 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 @@ -34,11 +34,14 @@ import org.apache.logging.log4j.Level; import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.base.Joiner.MapJoiner; +import com.google.common.collect.BiMap; import com.google.common.collect.HashBasedTable; +import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.collect.Table; import com.google.common.io.Files; @@ -112,12 +115,27 @@ public class GameData { // internal from here - public static Map buildItemDataList() + public static class GameDataSnapshot { + public final Map idMap; + public final Set blockSubstitutions; + public final Set itemSubstitutions; + public GameDataSnapshot(Map idMap, Set blockSubstitutions, Set itemSubstitutions) + { + this.idMap = idMap; + this.blockSubstitutions = blockSubstitutions; + this.itemSubstitutions = itemSubstitutions; + } + } + public static GameDataSnapshot buildItemDataList() { Map idMapping = Maps.newHashMap(); getMain().iBlockRegistry.serializeInto(idMapping); getMain().iItemRegistry.serializeInto(idMapping); - return idMapping; + Set blockSubs = Sets.newHashSet(); + getMain().iBlockRegistry.serializeSubstitutions(blockSubs); + Set itemSubs = Sets.newHashSet(); + getMain().iItemRegistry.serializeSubstitutions(itemSubs); + return new GameDataSnapshot(idMapping, blockSubs, itemSubs); } public static int[] getBlockedIds() @@ -407,12 +425,12 @@ public class GameData { blockedIds.addAll(newBlockedIds); } - public static List injectWorldIDMap(Map dataList, boolean injectFrozenData, boolean isLocalWorld) + public static List injectWorldIDMap(Map dataList, Set blockSubstitutions, Set itemSubstitutions, boolean injectFrozenData, boolean isLocalWorld) { - return injectWorldIDMap(dataList, new HashSet(), new HashMap(), new HashMap(), injectFrozenData, isLocalWorld); + return injectWorldIDMap(dataList, new HashSet(), new HashMap(), new HashMap(), blockSubstitutions, itemSubstitutions, injectFrozenData, isLocalWorld); } - public static List injectWorldIDMap(Map dataList, Set blockedIds, Map blockAliases, Map itemAliases, boolean injectFrozenData, boolean isLocalWorld) + public static List injectWorldIDMap(Map dataList, Set blockedIds, Map blockAliases, Map itemAliases, Set blockSubstitutions, Set itemSubstitutions, 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(); @@ -438,6 +456,31 @@ public class GameData { newData.iItemRegistry.addAlias(entry.getKey(), entry.getValue()); } + for (String entry : blockSubstitutions) + { + newData.iBlockRegistry.activateSubstitution(entry); + } + for (String entry : itemSubstitutions) + { + newData.iItemRegistry.activateSubstitution(entry); + } + if (injectFrozenData) + { + for (String newBlockSubstitution : getMain().blockSubstitutions.keySet()) + { + if (!blockSubstitutions.contains(newBlockSubstitution)) + { + newData.iBlockRegistry.activateSubstitution(newBlockSubstitution); + } + } + for (String newItemSubstitution : getMain().itemSubstitutions.keySet()) + { + if (!itemSubstitutions.contains(newItemSubstitution)) + { + newData.iItemRegistry.activateSubstitution(newItemSubstitution); + } + } + } // 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++) @@ -479,10 +522,11 @@ public class GameData { if (currId != newId) { - throw new IllegalStateException(String.format("Can't map %s %s to id %d, already occupied by %s, blocked %b, ItemBlock %b", + throw new IllegalStateException(String.format("Can't map %s %s to id %d (seen at: %d), already occupied by %s, blocked %b, ItemBlock %b", isBlock ? "block" : "item", itemName, newId, + currId, isBlock ? newData.iBlockRegistry.getRaw(newId) : newData.iItemRegistry.getRaw(newId), newData.blockedIds.contains(newId), isBlock ? false : (getMain().iItemRegistry.getRaw(currId) instanceof ItemBlock))); @@ -763,6 +807,10 @@ public class GameData { if (item instanceof ItemBlock) // ItemBlock, adjust id and clear the slot already occupied by the corresponding block { Block block = ((ItemBlock) item).field_150939_a; + if (idHint != -1 && getMain().blockSubstitutions.containsKey(name)) + { + block = getMain().blockSubstitutions.get(name); + } int id = iBlockRegistry.getId(block); if (id == -1) // ItemBlock before its Block @@ -948,13 +996,32 @@ public class GameData { FMLLog.fine("Registry consistency check successful"); } - - void registerPersistentAlias(String fromName, String toName, GameRegistry.Type type) throws ExistingAliasException + + void registerSubstitutionAlias(String nameToSubstitute, Type type, Object toReplace) throws ExistingSubstitutionException { - type.getRegistry().addPersistentAlias(fromName, toName); + type.getRegistry().addSubstitutionAlias(Loader.instance().activeModContainer().getModId(),nameToSubstitute, toReplace); } static RegistryDelegate buildDelegate(T referant, Class type) { return new RegistryDelegate.Delegate(referant, type); } + + private BiMap itemSubstitutions = HashBiMap.create(); + private BiMap blockSubstitutions = HashBiMap.create(); + @SuppressWarnings("unchecked") + BiMap getPersistentSubstitutionMap(Class type) + { + if (type.equals(Item.class)) + { + return (BiMap) itemSubstitutions; + } + else if (type.equals(Block.class)) + { + return (BiMap) blockSubstitutions; + } + else + { + throw new RuntimeException("WHAT?"); + } + } } diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java b/fml/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java index 6a26bb3c0..093ef06f6 100644 --- a/fml/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java +++ b/fml/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java @@ -146,18 +146,19 @@ public class GameRegistry /** - * Add a forced persistent alias for the block or item to another block or item. This will have - * the effect of using the substituted block or item instead of the original, whereever it is + * Add a forced persistent substitution alias for the block or item to another block or item. This will have + * the effect of using the substituted block or item instead of the original, where ever it is * referenced. - * - * @param toName The name to link to (this is the NEW block or item) - * @param fromName The name to link from (this is the OLD block or item being substituted) + * + * @param nameToSubstitute The name to link to (this is the NEW block or item) * @param type The type (Block or Item) - * @throws ExistingAliasException if someone else has already registered an alias either from or to one of the names + * @param object a NEW instance that is type compatible with the existing instance + * @throws ExistingSubstitutionException if someone else has already registered an alias either from or to one of the names + * @throws IncompatibleSubstitutionException if the substitution is incompatible */ - public static void addAlias(String toName, String fromName, GameRegistry.Type type) throws ExistingAliasException + public static void addSubstitutionAlias(String nameToSubstitute, GameRegistry.Type type, Object object) throws ExistingSubstitutionException { - GameData.getMain().registerPersistentAlias(fromName, toName, type); + GameData.getMain().registerSubstitutionAlias(nameToSubstitute, type, object); } /** @@ -406,7 +407,7 @@ public class GameRegistry } } - public static enum Type { + public static enum Type { BLOCK { @Override diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/IncompatibleSubstitutionException.java b/fml/src/main/java/cpw/mods/fml/common/registry/IncompatibleSubstitutionException.java new file mode 100644 index 000000000..c4d2114ac --- /dev/null +++ b/fml/src/main/java/cpw/mods/fml/common/registry/IncompatibleSubstitutionException.java @@ -0,0 +1,10 @@ +package cpw.mods.fml.common.registry; + +public class IncompatibleSubstitutionException extends RuntimeException { + public IncompatibleSubstitutionException(String fromName, Object replacement, Object original) + { + } + + private static final long serialVersionUID = 1L; + +} diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/RegistryDelegate.java b/fml/src/main/java/cpw/mods/fml/common/registry/RegistryDelegate.java index 811228605..64db87a51 100644 --- a/fml/src/main/java/cpw/mods/fml/common/registry/RegistryDelegate.java +++ b/fml/src/main/java/cpw/mods/fml/common/registry/RegistryDelegate.java @@ -1,29 +1,53 @@ package cpw.mods.fml.common.registry; +import com.google.common.base.Objects; + /** * A registry delegate for holding references to items or blocks - * + * These should be safe to use in things like lists though aliased items and blocks will not + * have object identity with respect to their delegate. + * * @author cpw * * @param the type of thing we're holding onto */ public interface RegistryDelegate { + /** + * Get the referent pointed at by this delegate. This will be the currently active item or block, and will change + * as world saves come and go. Note that item.delegate.get() may NOT be the same object as item, due to item and + * block substitution. + * + * @return The referred object + */ T get(); + + /** + * Get the name of this delegate. This is completely static after registration has completed and will never change. + * @return The name + */ String name(); + + /** + * Get the delegate type. It will be Item or Block. + * @return The type of delegate + */ Class type(); - + + /* + * This is the internal implementation class of the delegate. + */ final class Delegate implements RegistryDelegate { private T referant; private String name; private final Class type; - + public Delegate(T referant, Class type) { this.referant = referant; this.type = type; } - + @Override public T get() { return referant; @@ -34,19 +58,37 @@ public interface RegistryDelegate { return name; } + @Override public Class type() { return this.type; } - + void changeReference(T newTarget) { this.referant = newTarget; } - + void setName(String name) { this.name = name; } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof Delegate) + { + Delegate other = (Delegate) obj; + return Objects.equal(other.name, name); + } + return false; + } + + @Override + public int hashCode() + { + return Objects.hashCode(name); + } } }