Restructure block and item mapping data in world save and network to potentially expand to custom mod ID registry syncing. Tip: ONLY use those functions in GameData that are marked as public API as internal API may change in 1.8.

This commit is contained in:
Lex Manos 2014-10-01 01:07:23 -07:00
parent 27ea6bb6fa
commit 10d3062fc6
13 changed files with 338 additions and 386 deletions

View File

@ -38,7 +38,7 @@ minecraft {
installerVersion = "1.4"
}
group = 'net.minecraftforge.fml'
group = 'net.minecraftforge'
version = getVersionFromGit(getProject())
jenkins {

View File

@ -126,10 +126,8 @@ public class FMLClientHandler implements IFMLSidedHandler
private DummyModContainer optifineContainer;
@SuppressWarnings("unused")
private boolean guiLoaded;
@SuppressWarnings("unused")
private boolean serverIsRunning;
private MissingModsException modsMissing;
@ -148,7 +146,6 @@ public class FMLClientHandler implements IFMLSidedHandler
private List<IResourcePack> resourcePackList;
@SuppressWarnings("unused")
private IReloadableResourceManager resourceManager;
private Map<String, IResourcePack> resourcePackMap;

View File

@ -168,7 +168,6 @@ public class GuiModList extends GuiScreen
}
else
{
@SuppressWarnings("resource")
InputStream logoResource = getClass().getResourceAsStream(logoFile);
if (logoResource != null)
{

View File

@ -24,6 +24,7 @@ import java.util.Set;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagIntArray;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.storage.SaveHandler;
import net.minecraft.world.storage.WorldInfo;
@ -92,66 +93,63 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai
public NBTTagCompound getDataForWriting(SaveHandler handler, WorldInfo info)
{
NBTTagCompound fmlData = new NBTTagCompound();
NBTTagList list = new NBTTagList();
NBTTagList modList = new NBTTagList();
for (ModContainer mc : Loader.instance().getActiveModList())
{
NBTTagCompound mod = new NBTTagCompound();
mod.setString("ModId", mc.getModId());
mod.setString("ModVersion", mc.getVersion());
list.appendTag(mod);
modList.appendTag(mod);
}
fmlData.setTag("ModList", list);
// name <-> id mappings
NBTTagList dataList = new NBTTagList();
FMLLog.fine("Gathering id map for writing to world save %s", info.getWorldName());
GameData.GameDataSnapshot dataSnapshot = GameData.buildItemDataList();
for (Entry<String, Integer> item : dataSnapshot.idMap.entrySet())
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K",item.getKey());
tag.setInteger("V",item.getValue());
dataList.appendTag(tag);
}
fmlData.setTag("ItemData", dataList);
// blocked ids
fmlData.setIntArray("BlockedItemIds", GameData.getBlockedIds());
// block aliases
NBTTagList blockAliasList = new NBTTagList();
for (Entry<String, String> entry : GameData.getBlockRegistry().getAliases().entrySet())
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", entry.getKey());
tag.setString("V", entry.getValue());
blockAliasList.appendTag(tag);
}
fmlData.setTag("BlockAliases", blockAliasList);
NBTTagList blockSubstitutionsList = new NBTTagList();
for (String entry : dataSnapshot.blockSubstitutions)
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", entry);
blockSubstitutionsList.appendTag(tag);
}
fmlData.setTag("BlockSubstitutions", blockSubstitutionsList);
// item aliases
NBTTagList itemAliasList = new NBTTagList();
for (Entry<String, String> entry : GameData.getItemRegistry().getAliases().entrySet())
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", entry.getKey());
tag.setString("V", entry.getValue());
itemAliasList.appendTag(tag);
}
fmlData.setTag("ItemAliases", itemAliasList);
fmlData.setTag("ModList", modList);
NBTTagList itemSubstitutionsList = new NBTTagList();
for (String entry : dataSnapshot.itemSubstitutions)
NBTTagCompound registries = new NBTTagCompound();
fmlData.setTag("Registries", registries);
FMLLog.fine("Gathering id map for writing to world save %s", info.getWorldName());
GameData.GameDataSnapshot dataSnapshot = GameData.takeSnapshot();
for (Map.Entry<String, GameData.GameDataSnapshot.Entry> e : dataSnapshot.entries.entrySet())
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", entry);
itemSubstitutionsList.appendTag(tag);
NBTTagCompound data = new NBTTagCompound();
registries.setTag(e.getKey(), data);
NBTTagList ids = new NBTTagList();
for (Entry<String, Integer> item : e.getValue().ids.entrySet())
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", item.getKey());
tag.setInteger("V", item.getValue());
ids.appendTag(tag);
}
data.setTag("ids", ids);
NBTTagList aliases = new NBTTagList();
for (Entry<String, String> entry : e.getValue().aliases.entrySet())
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", entry.getKey());
tag.setString("V", entry.getValue());
aliases.appendTag(tag);
}
data.setTag("aliases", aliases);
NBTTagList subs = new NBTTagList();
for (String entry : e.getValue().substitutions)
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", entry);
subs.appendTag(tag);
}
data.setTag("substitutions", subs);
int[] blocked = new int[e.getValue().blocked.size()];
int idx = 0;
for (Integer i : e.getValue().blocked)
{
blocked[idx++] = i;
}
data.setIntArray("blocked", blocked);
}
fmlData.setTag("ItemSubstitutions", itemSubstitutionsList);
return fmlData;
}
@ -181,97 +179,143 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai
List<String> failedElements = null;
if (tag.hasKey("ModItemData"))
if (tag.hasKey("ModItemData")) // Pre 1.7
{
GameData.GameDataSnapshot snapshot = new GameData.GameDataSnapshot();
GameData.GameDataSnapshot.Entry items = new GameData.GameDataSnapshot.Entry();
snapshot.entries.put("fml:blocks", new GameData.GameDataSnapshot.Entry());
snapshot.entries.put("fml:items", items);
FMLLog.info("Attempting to convert old world data to new system. This may be trouble!");
NBTTagList modList = tag.getTagList("ModItemData", (byte)10);
Map<String,Integer> dataList = Maps.newLinkedHashMap();
for (int i = 0; i < modList.tagCount(); i++)
{
NBTTagCompound itemTag = modList.getCompoundTagAt(i);
String modId = itemTag.getString("ModId");
String itemType = itemTag.getString("ItemType");
int itemId = itemTag.getInteger("ItemId");
int ordinal = itemTag.getInteger("ordinal");
String forcedModId = itemTag.hasKey("ForcedModId") ? itemTag.getString("ForcedModId") : null;
String forcedName = itemTag.hasKey("ForcedName") ? itemTag.getString("ForcedName") : null;
NBTTagCompound data = modList.getCompoundTagAt(i);
String forcedModId = data.hasKey("ForcedModId") ? data.getString("ForcedModId") : null;
String forcedName = data.hasKey("ForcedName") ? data.getString("ForcedName") : null;
if (forcedName == null)
{
FMLLog.warning("Found unlabelled item in world save, this may cause problems. The item type %s:%d will not be present", itemType, ordinal);
FMLLog.warning("Found unlabelled item in world save, this may cause problems. The item type %s:%d will not be present", data.getString("ItemType"), data.getInteger("ordinal"));
}
else
{
// all entries are Items, blocks were only saved through their ItemBlock
String itemLabel = String.format("%c%s:%s", '\u0002', forcedModId != null ? forcedModId : modId, forcedName);
dataList.put(itemLabel, itemId);
String itemLabel = String.format("%s:%s", forcedModId != null ? forcedModId : data.getString("ModId"), forcedName);
items.ids.put(itemLabel, data.getInteger("ItemId"));
}
}
failedElements = GameData.injectWorldIDMap(dataList, ImmutableSet.<String>of(), ImmutableSet.<String>of(), true, true);
failedElements = GameData.injectSnapshot(snapshot, true, true);
}
else if (tag.hasKey("ItemData"))
else if (tag.hasKey("ItemData")) // 1.7
{
// name <-> id mappings
GameData.GameDataSnapshot snapshot = new GameData.GameDataSnapshot();
GameData.GameDataSnapshot.Entry blocks = new GameData.GameDataSnapshot.Entry();
GameData.GameDataSnapshot.Entry items = new GameData.GameDataSnapshot.Entry();
snapshot.entries.put("fml:blocks", blocks);
snapshot.entries.put("fml:items", items);
NBTTagList list = tag.getTagList("ItemData", 10);
Map<String,Integer> dataList = Maps.newLinkedHashMap();
for (int i = 0; i < list.tagCount(); i++)
{
NBTTagCompound dataTag = list.getCompoundTagAt(i);
dataList.put(dataTag.getString("K"), dataTag.getInteger("V"));
NBTTagCompound e = list.getCompoundTagAt(i);
String name = e.getString("K");
if (name.charAt(0) == '\u0001')
blocks.ids.put(name.substring(1), e.getInteger("V"));
else if (name.charAt(0) == '\u0002')
items.ids.put(name.substring(1), e.getInteger("V"));
}
Set<Integer> blockedIds = new HashSet<Integer>();
if (!tag.hasKey("BlockedItemIds")) // no blocked id info -> old 1.7 save
{
// old early 1.7 save potentially affected by the registry mapping bug
// fix the ids the best we can...
GameData.fixBrokenIds(dataList, blockedIds);
GameData.fixBrokenIds(blocks, items, blockedIds);
}
// blocked ids
for (int id : tag.getIntArray("BlockedItemIds"))
else
{
blockedIds.add(id);
for (int id : tag.getIntArray("BlockedItemIds"))
{
blockedIds.add(id);
}
}
// block aliases
Map<String, String> blockAliases = new HashMap<String, String>();
blocks.blocked.addAll(blockedIds);
items.blocked.addAll(blockedIds);
list = tag.getTagList("BlockAliases", 10);
for (int i = 0; i < list.tagCount(); i++)
{
NBTTagCompound dataTag = list.getCompoundTagAt(i);
blockAliases.put(dataTag.getString("K"), dataTag.getString("V"));
blocks.aliases.put(dataTag.getString("K"), dataTag.getString("V"));
}
Set<String> blockSubstitutions = Sets.newHashSet();
if (tag.hasKey("BlockSubstitutions", 9))
{
list = tag.getTagList("BlockSubstitutions", 10);
for (int i = 0; i < list.tagCount(); i++)
{
NBTTagCompound dataTag = list.getCompoundTagAt(i);
blockSubstitutions.add(dataTag.getString("K"));
blocks.substitutions.add(dataTag.getString("K"));
}
}
// item aliases
Map<String, String> itemAliases = new HashMap<String, String>();
list = tag.getTagList("ItemAliases", 10);
for (int i = 0; i < list.tagCount(); i++)
{
NBTTagCompound dataTag = list.getCompoundTagAt(i);
itemAliases.put(dataTag.getString("K"), dataTag.getString("V"));
items.aliases.put(dataTag.getString("K"), dataTag.getString("V"));
}
Set<String> itemSubstitutions = Sets.newHashSet();
if (tag.hasKey("ItemSubstitutions", 9))
{
list = tag.getTagList("ItemSubstitutions", 10);
for (int i = 0; i < list.tagCount(); i++)
{
NBTTagCompound dataTag = list.getCompoundTagAt(i);
itemSubstitutions.add(dataTag.getString("K"));
items.substitutions.add(dataTag.getString("K"));
}
}
failedElements = GameData.injectWorldIDMap(dataList, blockedIds, blockAliases, itemAliases, blockSubstitutions, itemSubstitutions, true, true);
failedElements = GameData.injectSnapshot(snapshot, true, true);
}
else if (tag.hasKey("Registries")) // 1.8, genericed out the 'registries' list
{
GameData.GameDataSnapshot snapshot = new GameData.GameDataSnapshot();
NBTTagCompound regs = tag.getCompoundTag("Registries");
for (String key : (Set<String>)regs.getKeySet())
{
GameData.GameDataSnapshot.Entry entry = new GameData.GameDataSnapshot.Entry();
snapshot.entries.put(key, entry);
NBTTagList list = regs.getCompoundTag(key).getTagList("ids", 10);
for (int x = 0; x < list.tagCount(); x++)
{
NBTTagCompound e = list.getCompoundTagAt(x);
entry.ids.put(e.getString("K"), e.getInteger("V"));
}
list = regs.getCompoundTag(key).getTagList("aliases", 10);
for (int x = 0; x < list.tagCount(); x++)
{
NBTTagCompound e = list.getCompoundTagAt(x);
entry.aliases.put(e.getString("K"), e.getString("V"));
}
list = regs.getCompoundTag(key).getTagList("substitutions", 10);
for (int x = 0; x < list.tagCount(); x++)
{
NBTTagCompound e = list.getCompoundTagAt(x);
entry.substitutions.add(e.getString("K"));
}
int[] blocked = regs.getCompoundTag(key).getIntArray("blocked");
for (int i : blocked)
{
entry.blocked.add(i);
}
}
failedElements = GameData.injectSnapshot(snapshot, true, true);
}
if (failedElements != null && !failedElements.isEmpty())

View File

@ -695,8 +695,6 @@ public class Loader
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,5 +1,6 @@
package net.minecraftforge.fml.common.network.handshake;
import java.util.HashSet;
import java.util.List;
import net.minecraftforge.fml.common.FMLLog;
@ -85,8 +86,28 @@ enum FMLHandshakeClientState implements IHandshakeState<FMLHandshakeClientState>
@Override
public FMLHandshakeClientState accept(ChannelHandlerContext ctx, FMLHandshakeMessage msg)
{
FMLHandshakeMessage.ModIdData modIds = (FMLHandshakeMessage.ModIdData)msg;
List<String> locallyMissing = GameData.injectWorldIDMap(modIds.dataList(), modIds.blockSubstitutions(), modIds.itemSubstitutions(), false, false);
FMLHandshakeMessage.RegistryData pkt = (FMLHandshakeMessage.RegistryData)msg;
GameData.GameDataSnapshot snap = ctx.channel().attr(NetworkDispatcher.FML_GAMEDATA_SNAPSHOT).get();
if (snap == null)
{
snap = new GameData.GameDataSnapshot();
ctx.channel().attr(NetworkDispatcher.FML_GAMEDATA_SNAPSHOT).set(snap);
}
GameData.GameDataSnapshot.Entry entry = new GameData.GameDataSnapshot.Entry();
entry.ids.putAll(pkt.getIdMap());
entry.substitutions.addAll(pkt.getSubstitutions());
snap.entries.put(pkt.getName(), entry);
if (pkt.hasMore())
{
FMLLog.fine("Received Mod Registry mapping for %s: %d IDs %d subs", pkt.getName(), entry.ids.size(), entry.substitutions.size());
return WAITINGSERVERCOMPLETE;
}
ctx.channel().attr(NetworkDispatcher.FML_GAMEDATA_SNAPSHOT).remove();
List<String> locallyMissing = GameData.injectSnapshot(snap, false, false);
if (!locallyMissing.isEmpty())
{
NetworkDispatcher dispatcher = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();

View File

@ -10,7 +10,7 @@ public class FMLHandshakeCodec extends FMLIndexedMessageToMessageCodec<FMLHandsh
addDiscriminator((byte)0, FMLHandshakeMessage.ServerHello.class);
addDiscriminator((byte)1, FMLHandshakeMessage.ClientHello.class);
addDiscriminator((byte)2, FMLHandshakeMessage.ModList.class);
addDiscriminator((byte)3, FMLHandshakeMessage.ModIdData.class);
addDiscriminator((byte)3, FMLHandshakeMessage.RegistryData.class);
addDiscriminator((byte)-1, FMLHandshakeMessage.HandshakeAck.class);
addDiscriminator((byte)-2, FMLHandshakeMessage.HandshakeReset.class);
}

View File

@ -125,91 +125,91 @@ public abstract class FMLHandshakeMessage {
}
}
public static class ModIdData extends FMLHandshakeMessage {
public ModIdData()
public static class RegistryData extends FMLHandshakeMessage
{
public RegistryData()
{
}
public ModIdData(GameData.GameDataSnapshot snapshot)
public RegistryData(boolean hasMore, String name, GameData.GameDataSnapshot.Entry entry)
{
this.modIds = snapshot.idMap;
this.blockSubstitutions = snapshot.blockSubstitutions;
this.itemSubstitutions = snapshot.itemSubstitutions;
this.hasMore = hasMore;
this.name = name;
this.ids = entry.ids;
this.substitutions = entry.substitutions;
}
private Map<String,Integer> modIds;
private Set<String> blockSubstitutions;
private Set<String> itemSubstitutions;
private boolean hasMore;
private String name;
private Map<String,Integer> ids;
private Set<String> substitutions;
@Override
public void fromBytes(ByteBuf buffer)
{
this.hasMore = buffer.readBoolean();
this.name = ByteBufUtils.readUTF8String(buffer);
int length = ByteBufUtils.readVarInt(buffer, 3);
modIds = Maps.newHashMap();
blockSubstitutions = Sets.newHashSet();
itemSubstitutions = Sets.newHashSet();
ids = Maps.newHashMap();
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;
ids.put(ByteBufUtils.readUTF8String(buffer), ByteBufUtils.readVarInt(buffer, 3));
}
length = ByteBufUtils.readVarInt(buffer, 3);
substitutions = Sets.newHashSet();
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));
substitutions.add(ByteBufUtils.readUTF8String(buffer));
}
//if (!buffer.isReadable()) return; // In case we expand
}
@Override
public void toBytes(ByteBuf buffer)
{
ByteBufUtils.writeVarInt(buffer, modIds.size(), 3);
for (Entry<String, Integer> entry: modIds.entrySet())
buffer.writeBoolean(this.hasMore);
ByteBufUtils.writeUTF8String(buffer, this.name);
ByteBufUtils.writeVarInt(buffer, ids.size(), 3);
for (Entry<String, Integer> entry: ids.entrySet())
{
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.writeVarInt(buffer, substitutions.size(), 3);
for (String entry: substitutions)
{
ByteBufUtils.writeUTF8String(buffer, entry);
}
}
public Map<String,Integer> dataList()
public Map<String,Integer> getIdMap()
{
return modIds;
return ids;
}
public Set<String> blockSubstitutions()
public Set<String> getSubstitutions()
{
return blockSubstitutions;
return substitutions;
}
public Set<String> itemSubstitutions()
public String getName()
{
return itemSubstitutions;
return this.name;
}
public boolean hasMore()
{
return this.hasMore;
}
@Override
public String toString(Class<? extends Enum<?>> side)
{
return super.toString(side) + ":"+modIds.size()+" mappings";
return super.toString(side) + ":"+ids.size()+" mappings";
}
}
public static class HandshakeAck extends FMLHandshakeMessage {

View File

@ -1,5 +1,9 @@
package net.minecraftforge.fml.common.network.handshake;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.network.NetworkRegistry;
@ -56,7 +60,13 @@ enum FMLHandshakeServerState implements IHandshakeState<FMLHandshakeServerState>
{
if (!ctx.channel().attr(NetworkDispatcher.IS_LOCAL).get())
{
ctx.writeAndFlush(new FMLHandshakeMessage.ModIdData(GameData.buildItemDataList())).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
GameData.GameDataSnapshot snapshot = GameData.takeSnapshot();
Iterator<Map.Entry<String, GameData.GameDataSnapshot.Entry>> itr = snapshot.entries.entrySet().iterator();
while (itr.hasNext())
{
Entry<String, GameData.GameDataSnapshot.Entry> e = itr.next();
ctx.writeAndFlush(new FMLHandshakeMessage.RegistryData(itr.hasNext(), e.getKey(), e.getValue())).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
}
ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck(ordinal())).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
NetworkRegistry.INSTANCE.fireNetworkHandshake(ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(), Side.SERVER);

View File

@ -41,6 +41,7 @@ import net.minecraftforge.fml.common.network.PacketLoggingHandler;
import net.minecraftforge.fml.common.network.internal.FMLMessage;
import net.minecraftforge.fml.common.network.internal.FMLNetworkHandler;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
import net.minecraftforge.fml.common.registry.GameData;
import net.minecraftforge.fml.relauncher.Side;
public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> implements ChannelOutboundHandler {
@ -74,6 +75,7 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> imple
public static final AttributeKey<NetworkDispatcher> FML_DISPATCHER = AttributeKey.valueOf("fml:dispatcher");
public static final AttributeKey<Boolean> IS_LOCAL = AttributeKey.valueOf("fml:isLocal");
public static final AttributeKey<GameData.GameDataSnapshot> FML_GAMEDATA_SNAPSHOT = AttributeKey.valueOf("fml:gameDataSnapshot");
public final NetworkManager manager;
private final ServerConfigurationManager scm;
private EntityPlayerMP player;

View File

@ -29,18 +29,16 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespacedDefaul
private I optionalDefaultObject;
private int maxId;
private int minId;
private char discriminator;
// 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<String, String> aliases = new HashMap<String, String>();
private BiMap<String, I> persistentSubstitutions;
private BiMap<String, I> activeSubstitutions = HashBiMap.create();
FMLControlledNamespacedRegistry(Object defaultKey, int maxIdValue, int minIdValue, Class<I> type, char discriminator)
FMLControlledNamespacedRegistry(Object defaultKey, int maxIdValue, int minIdValue, Class<I> type)
{
super(defaultKey);
this.superType = type;
this.discriminator = discriminator;
this.optionalDefaultKey = defaultKey;
this.maxId = maxIdValue;
this.minId = minIdValue;
@ -98,7 +96,6 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespacedDefaul
{
if (this.superType != registry.superType) throw new IllegalArgumentException("incompatible registry");
this.discriminator = registry.discriminator;
this.optionalDefaultKey = registry.optionalDefaultKey;
this.maxId = registry.maxId;
this.minId = registry.minId;
@ -333,13 +330,16 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespacedDefaul
{
for (I thing : this.typeSafeIterable())
{
idMapping.put(discriminator+getNameForObject(thing).toString(), getId(thing));
idMapping.put(getNameForObject(thing).toString(), getId(thing));
}
}
public Map<String, String> getAliases() // for saving
public void serializeAliases(Map<String, String> map)
{
return ImmutableMap.copyOf(aliases);
map.putAll(this.aliases);
}
public void serializeSubstitutions(Set<String> set)
{
set.addAll(activeSubstitutions.keySet());
}
/**
@ -504,11 +504,6 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespacedDefaul
getPersistentSubstitutions().put(nameToReplace, replacement);
}
public void serializeSubstitutions(Set<String> blockSubs)
{
blockSubs.addAll(activeSubstitutions.keySet());
}
private BiMap<String, I> getPersistentSubstitutions()
{
if (persistentSubstitutions == null)

View File

@ -14,6 +14,7 @@ package net.minecraftforge.fml.common.registry;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
@ -63,11 +64,6 @@ public class GameData {
static final int MAX_ITEM_ID = 31999;
private static final GameData mainData = new GameData();
private static final FMLControlledNamespacedRegistry<Block> blockRegistry = getBlockRegistry();
private 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;
@ -91,43 +87,59 @@ public class GameData {
return getMain().iItemRegistry;
}
/**
* @deprecated no replacement planned
*/
@Deprecated
public static ModContainer findModOwner(String string)
/***************************************************
* INTERNAL CODE FROM HERE ON DO NOT USE!
***************************************************/
public static class GameDataSnapshot
{
UniqueIdentifier ui = new UniqueIdentifier(string);
if (customOwners.containsKey(ui))
public static class Entry
{
return customOwners.get(ui);
public final Map<String, Integer> ids;
public final Set<String> substitutions;
public final Map<String, String> aliases;
public final Set<Integer> blocked;
public Entry()
{
this(new HashMap<String, Integer>(), new HashSet<String>(), new HashMap<String, String>(), new HashSet<Integer>());
}
public Entry(Map<String, Integer> ids, Set<String> substitions, Map<String, String> aliases, Set<Integer> blocked)
{
this.ids = ids;
this.substitutions = substitions;
this.aliases = aliases;
this.blocked = blocked;
}
public Entry(FMLControlledNamespacedRegistry registry)
{
this.ids = Maps.newHashMap();
this.substitutions = Sets.newHashSet();
this.aliases = Maps.newHashMap();
this.blocked = Sets.newHashSet();
registry.serializeInto(this.ids);
registry.serializeSubstitutions(this.substitutions);
registry.serializeAliases(this.aliases);
if (GameData.getBlockRegistry() == registry ||
GameData.getItemRegistry() == registry)
{
this.blocked.addAll(GameData.getMain().blockedIds);
}
}
}
return Loader.instance().getIndexedModList().get(ui.modId);
public final Map<String, Entry> entries = Maps.newHashMap();
}
// internal from here
public static class GameDataSnapshot {
public final Map<String,Integer> idMap;
public final Set<String> blockSubstitutions;
public final Set<String> itemSubstitutions;
public GameDataSnapshot(Map<String, Integer> idMap, Set<String> blockSubstitutions, Set<String> itemSubstitutions)
{
this.idMap = idMap;
this.blockSubstitutions = blockSubstitutions;
this.itemSubstitutions = itemSubstitutions;
}
}
public static GameDataSnapshot buildItemDataList()
public static GameDataSnapshot takeSnapshot()
{
Map<String,Integer> idMapping = Maps.newHashMap();
getMain().iBlockRegistry.serializeInto(idMapping);
getMain().iItemRegistry.serializeInto(idMapping);
Set<String> blockSubs = Sets.newHashSet();
getMain().iBlockRegistry.serializeSubstitutions(blockSubs);
Set<String> itemSubs = Sets.newHashSet();
getMain().iItemRegistry.serializeSubstitutions(itemSubs);
return new GameDataSnapshot(idMapping, blockSubs, itemSubs);
GameDataSnapshot snap = new GameDataSnapshot();
snap.entries.put("fml:blocks", new GameDataSnapshot.Entry(getMain().getBlockRegistry()));
snap.entries.put("fml:items", new GameDataSnapshot.Entry(getMain().getItemRegistry()));
return snap;
}
public static int[] getBlockedIds()
@ -144,34 +156,6 @@ public class GameData {
return ret;
}
public static void dumpRegistry(File minecraftDir)
{
if (customItemStacks == null)
{
return;
}
if (Boolean.valueOf(System.getProperty("fml.dumpRegistry", "false")).booleanValue())
{
ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder();
for (String modId : customItemStacks.rowKeySet())
{
builder.putAll(modId, customItemStacks.row(modId).keySet());
}
File f = new File(minecraftDir, "itemStackRegistry.csv");
MapJoiner mapJoiner = Joiner.on("\n").withKeyValueSeparator(",");
try
{
Files.write(mapJoiner.join(builder.build().entries()), f, Charsets.UTF_8);
FMLLog.log(Level.INFO, "Dumped item registry data to %s", f.getAbsolutePath());
}
catch (IOException e)
{
FMLLog.log(Level.ERROR, e, "Failed to write registry data to %s", f.getAbsolutePath());
}
}
}
static Item findItem(String modId, String name)
{
return (Item) getMain().iItemRegistry.getObject(modId + ":" + name);
@ -183,57 +167,18 @@ public class GameData {
return getMain().iBlockRegistry.containsKey(key) ? getMain().iBlockRegistry.getObject(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;
Object name = getMain().iBlockRegistry.getNameForObject(block);
UniqueIdentifier ui = new UniqueIdentifier(name);
if (customItemStacks.contains(ui.modId, ui.name))
{
return null;
}
return ui;
return new UniqueIdentifier(name);
}
static UniqueIdentifier getUniqueName(Item item)
{
if (item == null) return null;
Object name = getMain().iItemRegistry.getNameForObject(item);
UniqueIdentifier ui = new UniqueIdentifier(name);
if (customItemStacks.contains(ui.modId, ui.name))
{
return null;
}
return ui;
return new UniqueIdentifier(name);
}
/**
@ -244,20 +189,14 @@ public class GameData {
*
* @param dataList List containing the IDs to fix
*/
public static void fixBrokenIds(Map<String, Integer> dataList, Set<Integer> blockedIds)
public static void fixBrokenIds(GameDataSnapshot.Entry blocks, GameDataSnapshot.Entry items, Set<Integer> blockedIds)
{
BitSet availabilityMap = new BitSet(MAX_ITEM_ID + 1);
// reserve all ids occupied by blocks
for (Entry<String, Integer> entry : dataList.entrySet())
for (Entry<String, Integer> entry : blocks.ids.entrySet())
{
String itemName = entry.getKey();
String realName = itemName.substring(1);
if (itemName.charAt(0) == '\u0001') // is a block
{
availabilityMap.set(entry.getValue());
}
availabilityMap.set(entry.getValue());
}
Set<Integer> newBlockedIds = new HashSet<Integer>();
@ -265,71 +204,65 @@ public class GameData {
Map<String, Integer> itemsToRelocate = new HashMap<String, Integer>();
// check all ids occupied by items
for (Entry<String, Integer> entry : dataList.entrySet())
for (Entry<String, Integer> entry : items.ids.entrySet())
{
String itemName = entry.getKey();
int oldId = entry.getValue();
String name = entry.getKey();
Item item = getMain().iItemRegistry.getRaw(name);
boolean blockThisId = false; // block oldId unless it's used by a block
if (itemName.charAt(0) != '\u0001') // is an item
if (item == null) // item no longer available
{
int oldId = entry.getValue();
String realName = itemName.substring(1);
String blockName = '\u0001' + realName;
Item item = getMain().iItemRegistry.getRaw(realName);
boolean blockThisId = false; // block oldId unless it's used by a block
if (item == null) // item no longer available
// can't fix items without reliably checking if they are ItemBlocks
FMLLog.warning("Item %s (old id %d) is no longer available and thus can't be fixed.", name, oldId);
itemsToRemove.add(name);
blockThisId = true;
}
else if (item instanceof ItemBlock)
{
if (blocks.ids.containsKey(name)) // the item was an ItemBlock before
{
// can't fix items without reliably checking if they are ItemBlocks
FMLLog.warning("Item %s (old id %d) is no longer available and thus can't be fixed.", realName, oldId);
itemsToRemove.add(itemName);
blockThisId = true;
}
else if (item instanceof ItemBlock)
{
if (dataList.containsKey(blockName)) // the item was an ItemBlock before
{
int blockId = dataList.get(blockName);
int blockId = blocks.ids.get(name);
if (blockId != oldId) // mis-located ItemBlock
{
// relocate to the matching block
FMLLog.warning("ItemBlock %s (old id %d) doesn't have the same id as its block (%d).", realName, oldId, blockId);
itemsToRelocate.put(entry.getKey(), blockId);
blockThisId = true;
}
else // intact ItemBlock
{
availabilityMap.set(oldId); // occupy id
}
}
else // the item hasn't been an ItemBlock before, but it's now
if (blockId != oldId) // mis-located ItemBlock
{
// can't fix these, drop them
FMLLog.warning("Item %s (old id %d) has been migrated to an ItemBlock and can't be fixed.", realName, oldId);
itemsToRemove.add(itemName);
// relocate to the matching block
FMLLog.warning("ItemBlock %s (old id %d) doesn't have the same id as its block (%d).", name, oldId, blockId);
itemsToRelocate.put(name, blockId);
blockThisId = true;
}
else // intact ItemBlock
{
availabilityMap.set(oldId); // occupy id
}
}
else if (availabilityMap.get(oldId)) // normal item, id is already occupied
else // the item hasn't been an ItemBlock before, but it's now
{
// remove the item mapping
FMLLog.warning("Item %s (old id %d) is conflicting with another block/item and can't be fixed.", realName, oldId);
itemsToRemove.add(itemName);
}
else // intact Item
{
availabilityMap.set(oldId); // occupy id
// can't fix these, drop them
FMLLog.warning("Item %s (old id %d) has been migrated to an ItemBlock and can't be fixed.", name, oldId);
itemsToRemove.add(name);
blockThisId = true;
}
}
else if (availabilityMap.get(oldId)) // normal item, id is already occupied
{
// remove the item mapping
FMLLog.warning("Item %s (old id %d) is conflicting with another block/item and can't be fixed.", name, oldId);
itemsToRemove.add(name);
}
else // intact Item
{
availabilityMap.set(oldId); // occupy id
}
// handle blocking the id from future use if possible (i.e. not used by a conflicting block)
// blockThisId requests don't modify availabilityMap, it could only be set by a block (or another item, which isn't being handled)
if (blockThisId && !availabilityMap.get(oldId))
{
// there's no block occupying this id, thus block the id from future use
// as there may still be ItemStacks in the world referencing it
newBlockedIds.add(oldId);
availabilityMap.set(oldId);
}
// handle blocking the id from future use if possible (i.e. not used by a conflicting block)
// blockThisId requests don't modify availabilityMap, it could only be set by a block (or another item, which isn't being handled)
if (blockThisId && !availabilityMap.get(oldId))
{
// there's no block occupying this id, thus block the id from future use
// as there may still be ItemStacks in the world referencing it
newBlockedIds.add(oldId);
availabilityMap.set(oldId);
}
}
@ -350,9 +283,9 @@ public class GameData {
// confirm missing mods causing item removal
Set<String> modsMissing = new HashSet<String>();
for (String itemName : itemsToRemove)
for (String name : itemsToRemove)
{
modsMissing.add(itemName.substring(1, itemName.indexOf(':')));
modsMissing.add(name.substring(0, name.indexOf(':')));
}
for (Iterator<String> it = modsMissing.iterator(); it.hasNext(); )
@ -396,32 +329,22 @@ public class GameData {
}
// apply fix
for (String itemName : itemsToRemove)
for (String name : itemsToRemove)
{
int id = dataList.remove(itemName);
FMLLog.warning("Removed Item %s, old id %d.", itemName.substring(1), id);
FMLLog.warning("Removed Item %s, old id %d.", name, items.ids.remove(name));
}
for (Map.Entry<String, Integer> entry : itemsToRelocate.entrySet())
{
String itemName = entry.getKey();
int newId = entry.getValue();
int oldId = dataList.put(itemName, newId);
FMLLog.warning("Remapped Item %s to id %d, old id %d.", itemName.substring(1), newId, oldId);
int oldId = items.ids.put(entry.getKey(), newId);
FMLLog.warning("Remapped Item %s to id %d, old id %d.", entry.getKey(), newId, oldId);
}
blockedIds.addAll(newBlockedIds);
}
public static List<String> injectWorldIDMap(Map<String, Integer> dataList, Set<String> blockSubstitutions, Set<String> itemSubstitutions, boolean injectFrozenData, boolean isLocalWorld)
{
return injectWorldIDMap(dataList, new HashSet<Integer>(), new HashMap<String, String>(), new HashMap<String, String>(), blockSubstitutions, itemSubstitutions, injectFrozenData, isLocalWorld);
}
public static List<String> injectWorldIDMap(Map<String, Integer> dataList, Set<Integer> blockedIds, Map<String, String> blockAliases, Map<String, String> itemAliases, Set<String> blockSubstitutions, Set<String> itemSubstitutions, boolean injectFrozenData, boolean isLocalWorld)
public static List<String> injectSnapshot(GameDataSnapshot snapshot, 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();
@ -430,28 +353,31 @@ public class GameData {
getMain().iBlockRegistry.dump();
getMain().iItemRegistry.dump();
GameDataSnapshot.Entry blocks = snapshot.entries.get("fml:blocks");
GameDataSnapshot.Entry items = snapshot.entries.get("fml:items");
GameData newData = new GameData();
for (int id : blockedIds)
for (int id : blocks.blocked)
{
newData.block(id);
}
for (Map.Entry<String, String> entry : blockAliases.entrySet())
for (Map.Entry<String, String> entry : blocks.aliases.entrySet())
{
newData.iBlockRegistry.addAlias(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, String> entry : itemAliases.entrySet())
for (Map.Entry<String, String> entry : items.aliases.entrySet())
{
newData.iItemRegistry.addAlias(entry.getKey(), entry.getValue());
}
for (String entry : blockSubstitutions)
for (String entry : blocks.substitutions)
{
newData.iBlockRegistry.activateSubstitution(entry);
}
for (String entry : itemSubstitutions)
for (String entry : items.substitutions)
{
newData.iItemRegistry.activateSubstitution(entry);
}
@ -459,14 +385,14 @@ public class GameData {
{
for (String newBlockSubstitution : getMain().blockSubstitutions.keySet())
{
if (!blockSubstitutions.contains(newBlockSubstitution))
if (!blocks.substitutions.contains(newBlockSubstitution))
{
newData.iBlockRegistry.activateSubstitution(newBlockSubstitution);
}
}
for (String newItemSubstitution : getMain().itemSubstitutions.keySet())
{
if (!itemSubstitutions.contains(newItemSubstitution))
if (!items.substitutions.contains(newItemSubstitution))
{
newData.iItemRegistry.activateSubstitution(newItemSubstitution);
}
@ -477,16 +403,12 @@ public class GameData {
for (int pass = 0; pass < 2; pass++)
{
boolean isBlock = (pass == 0);
Map<String, Integer> ids = (isBlock ? blocks.ids : items.ids);
for (Entry<String, Integer> entry : dataList.entrySet())
for (Entry<String, Integer> entry : ids.entrySet())
{
String itemName = entry.getKey();
int newId = entry.getValue();
// names starting with 0x1 are blocks, skip if the type isn't handled by this pass
if ((itemName.charAt(0) == '\u0001') != isBlock) continue;
itemName = itemName.substring(1);
int currId = isBlock ? getMain().iBlockRegistry.getId(itemName) : getMain().iItemRegistry.getId(itemName);
if (currId == -1)
@ -743,8 +665,8 @@ public class GameData {
private GameData()
{
iBlockRegistry = new FMLControlledNamespacedRegistry<Block>(new ResourceLocation("minecraft:air"), MAX_BLOCK_ID, MIN_BLOCK_ID, Block.class,'\u0001');
iItemRegistry = new FMLControlledNamespacedRegistry<Item>(null, MAX_ITEM_ID, MIN_ITEM_ID, Item.class,'\u0002');
iBlockRegistry = new FMLControlledNamespacedRegistry<Block>(new ResourceLocation("minecraft:air"), MAX_BLOCK_ID, MIN_BLOCK_ID, Block.class);
iItemRegistry = new FMLControlledNamespacedRegistry<Item>(null, MAX_ITEM_ID, MIN_ITEM_ID, Item.class);
availabilityMap = new BitSet(MAX_ITEM_ID + 1);
blockedIds = new HashSet<Integer>();
}

View File

@ -332,42 +332,6 @@ public class GameRegistry
return GameData.findItem(modId, name);
}
/**
* Manually register a custom item stack with FML for later tracking. It is automatically scoped with the active modid
*
* @param name The name to register it under
* @param itemStack The itemstack to register
*/
public static void registerCustomItemStack(String name, ItemStack itemStack)
{
GameData.registerCustomItemStack(name, itemStack);
}
/**
* Lookup an itemstack based on mod and name. It will create "default" itemstacks from blocks and items if no
* explicit itemstack is found.
*
* If it is built from a block, the metadata is by default the "wildcard" value.
*
* Custom itemstacks can be dumped from minecraft by setting the system property fml.dumpRegistry to true
* (-Dfml.dumpRegistry=true on the command line will work)
*
* @param modId The modid of the stack owner
* @param name The name of the stack
* @param stackSize The size of the stack returned
* @return The custom itemstack or null if no such itemstack was found
*/
public static ItemStack findItemStack(String modId, String name, int stackSize)
{
ItemStack foundStack = GameData.findItemStack(modId, name);
if (foundStack != null)
{
ItemStack is = foundStack.copy();
is.stackSize = Math.min(stackSize, is.getMaxStackSize());
return is;
}
return null;
}
public static final class UniqueIdentifier
{
public final String modId;