001 package cpw.mods.fml.common.registry;
002
003 import java.util.Map;
004 import java.util.Set;
005 import java.util.concurrent.CountDownLatch;
006
007 import net.minecraft.item.Item;
008 import net.minecraft.nbt.NBTTagCompound;
009 import net.minecraft.nbt.NBTTagList;
010
011 import com.google.common.base.Function;
012 import com.google.common.collect.MapDifference;
013 import com.google.common.collect.Maps;
014 import com.google.common.collect.Sets;
015
016 import cpw.mods.fml.common.FMLLog;
017 import cpw.mods.fml.common.Loader;
018 import cpw.mods.fml.common.LoaderState;
019 import cpw.mods.fml.common.ModContainer;
020
021 public class GameData {
022 private static Map<Integer, ItemData> idMap = Maps.newHashMap();
023 private static CountDownLatch serverValidationLatch;
024 private static CountDownLatch clientValidationLatch;
025 private static MapDifference<Integer, ItemData> difference;
026 private static boolean shouldContinue = true;
027 private static boolean isSaveValid = true;
028
029 public static void newItemAdded(Item item)
030 {
031 ModContainer mc = Loader.instance().activeModContainer();
032 if (mc == null)
033 {
034 mc = Loader.instance().getMinecraftModContainer();
035 if (Loader.instance().hasReachedState(LoaderState.AVAILABLE))
036 {
037 FMLLog.severe("It appears something has tried to allocate an Item outside of the initialization phase of Minecraft, this could be very bad for your network connectivity.");
038 }
039 }
040 String itemType = item.getClass().getName();
041 ItemData itemData = new ItemData(item, mc);
042 if (idMap.containsKey(item.shiftedIndex))
043 {
044 ItemData id = idMap.get(item.shiftedIndex);
045 FMLLog.warning("[ItemTracker] The mod %s is attempting to overwrite existing item at %d (%s from %s) with %s", mc.getModId(), id.getItemId(), id.getItemType(), id.getModId(), itemType);
046 }
047 idMap.put(item.shiftedIndex, itemData);
048 FMLLog.fine("[ItemTracker] Adding item %s(%d) owned by %s", item.getClass().getName(), item.shiftedIndex, mc.getModId());
049 }
050
051 public static void validateWorldSave(Set<ItemData> worldSaveItems)
052 {
053 isSaveValid = true;
054 shouldContinue = true;
055 // allow ourselves to continue if there's no saved data
056 if (worldSaveItems == null)
057 {
058 serverValidationLatch.countDown();
059 try
060 {
061 clientValidationLatch.await();
062 }
063 catch (InterruptedException e)
064 {
065 }
066 return;
067 }
068
069 Function<? super ItemData, Integer> idMapFunction = new Function<ItemData, Integer>() {
070 public Integer apply(ItemData input) {
071 return input.getItemId();
072 };
073 };
074
075 Map<Integer,ItemData> worldMap = Maps.uniqueIndex(worldSaveItems,idMapFunction);
076 difference = Maps.difference(worldMap, idMap);
077 FMLLog.fine("The difference set is %s", difference);
078 if (!difference.entriesDiffering().isEmpty() || !difference.entriesOnlyOnLeft().isEmpty())
079 {
080 FMLLog.severe("FML has detected item discrepancies");
081 FMLLog.severe("Missing items : %s", difference.entriesOnlyOnLeft());
082 FMLLog.severe("Mismatched items : %s", difference.entriesDiffering());
083 isSaveValid = false;
084 serverValidationLatch.countDown();
085 }
086 else
087 {
088 isSaveValid = true;
089 serverValidationLatch.countDown();
090 }
091 try
092 {
093 clientValidationLatch.await();
094 if (!shouldContinue)
095 {
096 throw new RuntimeException("This server instance is going to stop abnormally because of a fatal ID mismatch");
097 }
098 }
099 catch (InterruptedException e)
100 {
101 }
102 }
103
104 public static void writeItemData(NBTTagList itemList)
105 {
106 for (ItemData dat : idMap.values())
107 {
108 itemList.appendTag(dat.toNBT());
109 }
110 }
111
112 /**
113 * Initialize the server gate
114 * @param gateCount the countdown amount. If it's 2 we're on the client and the client and server
115 * will wait at the latch. 1 is a server and the server will proceed
116 */
117 public static void initializeServerGate(int gateCount)
118 {
119 serverValidationLatch = new CountDownLatch(gateCount - 1);
120 clientValidationLatch = new CountDownLatch(gateCount - 1);
121 }
122
123 public static MapDifference<Integer, ItemData> gateWorldLoadingForValidation()
124 {
125 try
126 {
127 serverValidationLatch.await();
128 if (!isSaveValid)
129 {
130 return difference;
131 }
132 }
133 catch (InterruptedException e)
134 {
135 }
136 difference = null;
137 return null;
138 }
139
140
141 public static void releaseGate(boolean carryOn)
142 {
143 shouldContinue = carryOn;
144 clientValidationLatch.countDown();
145 }
146
147 public static Set<ItemData> buildWorldItemData(NBTTagList modList)
148 {
149 Set<ItemData> worldSaveItems = Sets.newHashSet();
150 for (int i = 0; i < modList.tagCount(); i++)
151 {
152 NBTTagCompound mod = (NBTTagCompound) modList.tagAt(i);
153 ItemData dat = new ItemData(mod);
154 worldSaveItems.add(dat);
155 }
156 return worldSaveItems;
157 }
158
159 static void setName(Item item, String name, String modId)
160 {
161 int id = item.shiftedIndex;
162 ItemData itemData = idMap.get(id);
163 itemData.setName(name,modId);
164 }
165
166
167
168 }