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.itemId, id.itemType, id.modId, 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.itemId; 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 160 161 }