Registry: Block IDs after failing to find a mapping for them
This commit is contained in:
parent
ff6083e77b
commit
e9ca678ab3
4 changed files with 166 additions and 42 deletions
|
@ -15,19 +15,25 @@ package cpw.mods.fml.common;
|
|||
import java.io.File;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.world.storage.SaveHandler;
|
||||
import net.minecraft.world.storage.WorldInfo;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import cpw.mods.fml.client.FMLFileResourcePack;
|
||||
import cpw.mods.fml.client.FMLFolderResourcePack;
|
||||
import cpw.mods.fml.common.asm.FMLSanityChecker;
|
||||
|
@ -106,6 +112,7 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai
|
|||
dataList.func_74742_a(tag);
|
||||
}
|
||||
fmlData.func_74782_a("ItemData", dataList);
|
||||
fmlData.func_74783_a("BlockedIds", GameData.getBlockedIds());
|
||||
return fmlData;
|
||||
}
|
||||
|
||||
|
@ -180,6 +187,7 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai
|
|||
}
|
||||
else if (tag.func_74764_b("ItemData"))
|
||||
{
|
||||
// read name <-> id mappings
|
||||
NBTTagList list = tag.func_150295_c("ItemData", (byte)10);
|
||||
Map<String,Integer> dataList = Maps.newLinkedHashMap();
|
||||
for (int i = 0; i < list.func_74745_c(); i++)
|
||||
|
@ -187,7 +195,10 @@ public class FMLContainer extends DummyModContainer implements WorldAccessContai
|
|||
NBTTagCompound dataTag = list.func_150305_b(i);
|
||||
dataList.put(dataTag.func_74779_i("K"), dataTag.func_74762_e("V"));
|
||||
}
|
||||
List<String> failedElements = GameData.injectWorldIDMap(dataList, true, true);
|
||||
// read blocked ids
|
||||
int[] blockedIds = tag.func_74759_k("BlockedIds");
|
||||
|
||||
List<String> failedElements = GameData.injectWorldIDMap(dataList, blockedIds, true, true);
|
||||
if (!failedElements.isEmpty())
|
||||
{
|
||||
throw new GameRegistryException("Failed to load the world - there are fatal block and item id issues", failedElements);
|
||||
|
|
|
@ -17,13 +17,17 @@ import java.io.FileReader;
|
|||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
|
@ -45,6 +49,7 @@ import com.google.common.collect.Ordering;
|
|||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
|
||||
import cpw.mods.fml.common.LoaderState.ModState;
|
||||
import cpw.mods.fml.common.ModContainer.Disableable;
|
||||
import cpw.mods.fml.common.discovery.ModDiscoverer;
|
||||
|
@ -56,6 +61,7 @@ import cpw.mods.fml.common.event.FMLModIdMappingEvent;
|
|||
import cpw.mods.fml.common.functions.ArtifactVersionNameFunction;
|
||||
import cpw.mods.fml.common.functions.ModIdFunction;
|
||||
import cpw.mods.fml.common.registry.GameData;
|
||||
import cpw.mods.fml.common.registry.GameRegistry.Type;
|
||||
import cpw.mods.fml.common.toposort.ModSorter;
|
||||
import cpw.mods.fml.common.toposort.ModSortingException;
|
||||
import cpw.mods.fml.common.toposort.ModSortingException.SortingExceptionData;
|
||||
|
@ -841,41 +847,75 @@ public class Loader
|
|||
return true;
|
||||
}
|
||||
|
||||
public List<String> fireMissingMappingEvent(ArrayListMultimap<String,String> missing, boolean isLocalWorld)
|
||||
/**
|
||||
* Fire a FMLMissingMappingsEvent to let mods determine how blocks/items defined in the world
|
||||
* save, but missing from the runtime, are to be handled.
|
||||
*
|
||||
* @param missing Map containing missing names with their associated id, blocks need to come before items for remapping.
|
||||
* @param isLocalWorld Whether this is executing for a world load (local/server) or a client.
|
||||
* @param gameData GameData instance where the new map's config is to be loaded into.
|
||||
* @return List with the mapping results.
|
||||
*/
|
||||
public List<String> fireMissingMappingEvent(LinkedHashMap<String, Integer> missing, boolean isLocalWorld, GameData gameData)
|
||||
{
|
||||
if (!missing.isEmpty())
|
||||
if (missing.isEmpty()) // nothing to do
|
||||
{
|
||||
FMLLog.fine("There are %d mappings missing - attempting a mod remap", missing.size());
|
||||
ArrayListMultimap<String,MissingMapping> missingMappings = ArrayListMultimap.create();
|
||||
List<MissingMapping> remaps = Lists.newArrayList();
|
||||
for (Map.Entry<String, String> mapping : missing.entries())
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
FMLLog.fine("There are %d mappings missing - attempting a mod remap", missing.size());
|
||||
ArrayListMultimap<String, MissingMapping> missingMappings = ArrayListMultimap.create();
|
||||
|
||||
for (Map.Entry<String, Integer> mapping : missing.entrySet())
|
||||
{
|
||||
String itemName = mapping.getKey();
|
||||
int id = mapping.getValue();
|
||||
MissingMapping m = new MissingMapping(itemName, id);
|
||||
missingMappings.put(itemName.substring(0, itemName.indexOf(':')), m);
|
||||
}
|
||||
|
||||
FMLMissingMappingsEvent missingEvent = new FMLMissingMappingsEvent(missingMappings);
|
||||
modController.propogateStateMessage(missingEvent);
|
||||
|
||||
if (isLocalWorld) // local world, warn about entries still being set to the default action
|
||||
{
|
||||
boolean didWarn = false;
|
||||
|
||||
for (MissingMapping mapping : missingMappings.values())
|
||||
{
|
||||
MissingMapping m = new MissingMapping(mapping.getValue(), remaps);
|
||||
missingMappings.put(mapping.getKey(), m);
|
||||
}
|
||||
FMLMissingMappingsEvent missingEvent = new FMLMissingMappingsEvent(missingMappings);
|
||||
modController.propogateStateMessage(missingEvent);
|
||||
if (!missingMappings.isEmpty() && isLocalWorld)
|
||||
{
|
||||
FMLLog.severe("There are unidentified mappings in this world - we are going to attempt to process anyway");
|
||||
for (java.util.Map.Entry<String, MissingMapping> missed : missingMappings.entries())
|
||||
if (mapping.getAction() == FMLMissingMappingsEvent.Action.DEFAULT)
|
||||
{
|
||||
remaps.add(missed.getValue());
|
||||
if (!didWarn)
|
||||
{
|
||||
FMLLog.severe("There are unidentified mappings in this world - we are going to attempt to process anyway");
|
||||
didWarn = true;
|
||||
}
|
||||
|
||||
FMLLog.severe("Unidentified %s: %s, id %d", mapping.type == Type.BLOCK ? "block" : "item", mapping.name, mapping.id);
|
||||
}
|
||||
}
|
||||
else if (!missingMappings.isEmpty() && !isLocalWorld)
|
||||
}
|
||||
else // remote world, fail on entries with the default action
|
||||
{
|
||||
List<String> missedMapping = new ArrayList<String>();
|
||||
|
||||
for (MissingMapping mapping : missingMappings.values())
|
||||
{
|
||||
List<String> missedMapping = Lists.newArrayList();
|
||||
for (java.util.Map.Entry<String, MissingMapping> missed : missingMappings.entries())
|
||||
if (mapping.getAction() == FMLMissingMappingsEvent.Action.DEFAULT)
|
||||
{
|
||||
missedMapping.add(missed.getKey()+ ":" + missed.getValue().name);
|
||||
missedMapping.add(mapping.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!missedMapping.isEmpty())
|
||||
{
|
||||
return ImmutableList.copyOf(missedMapping);
|
||||
}
|
||||
return GameData.processIdRematches(remaps, isLocalWorld);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
|
||||
return GameData.processIdRematches(missingMappings.values(), isLocalWorld, gameData);
|
||||
}
|
||||
|
||||
public void fireRemapEvent(Map<String, Integer[]> remaps)
|
||||
{
|
||||
if (remaps.isEmpty())
|
||||
|
|
|
@ -28,23 +28,43 @@ public class FMLMissingMappingsEvent extends FMLEvent {
|
|||
* @author cpw
|
||||
*
|
||||
*/
|
||||
public static enum Action { IGNORE, WARN, FAIL }
|
||||
public static enum Action { DEFAULT, IGNORE, WARN, FAIL }
|
||||
public static class MissingMapping {
|
||||
public final GameRegistry.Type type;
|
||||
public final String name;
|
||||
private Action action;
|
||||
private List<MissingMapping> remaps;
|
||||
public MissingMapping(String name, List<MissingMapping> remaps)
|
||||
public final int id;
|
||||
private Action action = Action.DEFAULT;
|
||||
|
||||
public MissingMapping(String name, int id)
|
||||
{
|
||||
this.type = name.charAt(0) == '\u0001' ? GameRegistry.Type.BLOCK : GameRegistry.Type.ITEM;
|
||||
this.name = name;
|
||||
this.remaps = remaps;
|
||||
this.action = FMLCommonHandler.instance().getDefaultMissingAction();
|
||||
this.id = id;
|
||||
}
|
||||
/**
|
||||
* @deprecated use ignore(), warn() or fail() instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setAction(Action target)
|
||||
{
|
||||
if (target == Action.DEFAULT) throw new IllegalArgumentException();
|
||||
|
||||
this.action = target;
|
||||
remaps.add(this);
|
||||
}
|
||||
|
||||
public void ignore()
|
||||
{
|
||||
this.action = Action.IGNORE;
|
||||
}
|
||||
|
||||
public void warn()
|
||||
{
|
||||
this.action = Action.WARN;
|
||||
}
|
||||
|
||||
public void fail()
|
||||
{
|
||||
this.action = Action.FAIL;
|
||||
}
|
||||
|
||||
public Action getAction()
|
||||
|
|
|
@ -15,23 +15,24 @@ package cpw.mods.fml.common.registry;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import java.util.Set;
|
||||
|
||||
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 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.ArrayListMultimap;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
|
@ -96,6 +97,20 @@ public class GameData {
|
|||
return idMapping;
|
||||
}
|
||||
|
||||
public static int[] getBlockedIds()
|
||||
{
|
||||
int[] ret = new int[getMain().blockedIds.size()];
|
||||
int index = 0;
|
||||
|
||||
for (int id : getMain().blockedIds)
|
||||
{
|
||||
ret[index] = id;
|
||||
index++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void dumpRegistry(File minecraftDir)
|
||||
{
|
||||
if (customItemStacks == null)
|
||||
|
@ -189,16 +204,26 @@ public class GameData {
|
|||
}
|
||||
|
||||
public static List<String> injectWorldIDMap(Map<String, Integer> dataList, boolean injectFrozenData, boolean isLocalWorld)
|
||||
{
|
||||
return injectWorldIDMap(dataList, new int[0], injectFrozenData, isLocalWorld);
|
||||
}
|
||||
|
||||
public static List<String> injectWorldIDMap(Map<String, Integer> dataList, int[] blockedIds, 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();
|
||||
ArrayListMultimap<String,String> missingMappings = ArrayListMultimap.create();
|
||||
LinkedHashMap<String, Integer> missingMappings = new LinkedHashMap<String, Integer>();
|
||||
getMain().testConsistency();
|
||||
getMain().iBlockRegistry.dump();
|
||||
getMain().iItemRegistry.dump();
|
||||
|
||||
GameData newData = new GameData();
|
||||
|
||||
for (int id : blockedIds)
|
||||
{
|
||||
newData.block(id);
|
||||
}
|
||||
|
||||
// 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++)
|
||||
|
@ -219,7 +244,7 @@ public class GameData {
|
|||
if (currId == -1)
|
||||
{
|
||||
FMLLog.info("Found a missing id from the world %s", itemName);
|
||||
missingMappings.put(itemName.substring(0, itemName.indexOf(':')), itemName);
|
||||
missingMappings.put(itemName, newId);
|
||||
continue; // no block/item -> nothing to add
|
||||
}
|
||||
else if (currId != newId)
|
||||
|
@ -254,7 +279,7 @@ public class GameData {
|
|||
}
|
||||
}
|
||||
|
||||
List<String> missedMappings = Loader.instance().fireMissingMappingEvent(missingMappings, isLocalWorld);
|
||||
List<String> missedMappings = Loader.instance().fireMissingMappingEvent(missingMappings, isLocalWorld, newData);
|
||||
if (!missedMappings.isEmpty()) return missedMappings;
|
||||
|
||||
if (injectFrozenData) // add blocks + items missing from the map
|
||||
|
@ -302,7 +327,7 @@ public class GameData {
|
|||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public static List<String> processIdRematches(List<MissingMapping> remaps, boolean isLocalWorld)
|
||||
public static List<String> processIdRematches(Iterable<MissingMapping> remaps, boolean isLocalWorld, GameData gameData)
|
||||
{
|
||||
List<String> failed = Lists.newArrayList();
|
||||
List<String> ignored = Lists.newArrayList();
|
||||
|
@ -311,6 +336,11 @@ public class GameData {
|
|||
for (MissingMapping remap : remaps)
|
||||
{
|
||||
FMLMissingMappingsEvent.Action action = remap.getAction();
|
||||
if (action == FMLMissingMappingsEvent.Action.DEFAULT)
|
||||
{
|
||||
action = FMLCommonHandler.instance().getDefaultMissingAction();
|
||||
}
|
||||
|
||||
if (action == FMLMissingMappingsEvent.Action.IGNORE)
|
||||
{
|
||||
ignored.add(remap.name);
|
||||
|
@ -319,10 +349,12 @@ public class GameData {
|
|||
{
|
||||
failed.add(remap.name);
|
||||
}
|
||||
else
|
||||
else if (action == FMLMissingMappingsEvent.Action.WARN)
|
||||
{
|
||||
warned.add(remap.name);
|
||||
}
|
||||
|
||||
gameData.block(remap.id); // prevent the id from being reused later
|
||||
}
|
||||
if (!failed.isEmpty())
|
||||
{
|
||||
|
@ -373,12 +405,15 @@ public class GameData {
|
|||
private final FMLControlledNamespacedRegistry<Item> iItemRegistry;
|
||||
// bit set marking ids as occupied
|
||||
private final BitSet availabilityMap;
|
||||
// IDs previously allocated in a world, but now unmapped/dangling; prevents the IDs from being reused
|
||||
private final Set<Integer> blockedIds;
|
||||
|
||||
private GameData()
|
||||
{
|
||||
iBlockRegistry = new FMLControlledNamespacedRegistry<Block>("air", 4095, 0, Block.class,'\u0001');
|
||||
iItemRegistry = new FMLControlledNamespacedRegistry<Item>(null, 32000, 4096, Item.class,'\u0002');
|
||||
availabilityMap = new BitSet(32000);
|
||||
blockedIds = new HashSet<Integer>();
|
||||
}
|
||||
|
||||
private GameData(GameData data)
|
||||
|
@ -393,6 +428,7 @@ public class GameData {
|
|||
iItemRegistry.set(data.iItemRegistry);
|
||||
availabilityMap.clear();
|
||||
availabilityMap.or(data.availabilityMap);
|
||||
blockedIds.addAll(data.blockedIds);
|
||||
}
|
||||
|
||||
void register(Object obj, String name, int idHint)
|
||||
|
@ -485,6 +521,15 @@ public class GameData {
|
|||
return blockId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block the specified id from being reused.
|
||||
*/
|
||||
private void block(int id)
|
||||
{
|
||||
blockedIds.add(id);
|
||||
useSlot(id);
|
||||
}
|
||||
|
||||
private boolean useSlot(int id)
|
||||
{
|
||||
boolean oldValue = availabilityMap.get(id);
|
||||
|
@ -503,13 +548,13 @@ public class GameData {
|
|||
// 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)
|
||||
if (iBlockRegistry.getRaw(i) == null && iItemRegistry.getRaw(i) == null && !blockedIds.contains(i))
|
||||
{
|
||||
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
|
||||
// test if there's a bit in availabilityMap set for every entry in the block registry, make sure it's not a blocked id
|
||||
for (Iterator<Object> it = iBlockRegistry.iterator(); it.hasNext(); )
|
||||
{
|
||||
Block block = (Block) it.next();
|
||||
|
@ -519,9 +564,13 @@ public class GameData {
|
|||
{
|
||||
throw new IllegalStateException(String.format("Registry entry for block %s, id %d, marked as empty.", block, id));
|
||||
}
|
||||
if (blockedIds.contains(id))
|
||||
{
|
||||
throw new IllegalStateException(String.format("Registry entry for block %s, id %d, marked as dangling.", block, id));
|
||||
}
|
||||
}
|
||||
|
||||
// test if there's a bit in availabilityMap set for every entry in the item registry,
|
||||
// test if there's a bit in availabilityMap set for every entry in the item registry, make sure it's not a blocked id,
|
||||
// check if ItemBlocks have blocks with matching ids in the block registry
|
||||
for (Iterator<Object> it = iItemRegistry.iterator(); it.hasNext(); )
|
||||
{
|
||||
|
@ -532,6 +581,10 @@ public class GameData {
|
|||
{
|
||||
throw new IllegalStateException(String.format("Registry entry for item %s, id %d, marked as empty.", item, id));
|
||||
}
|
||||
if (blockedIds.contains(id))
|
||||
{
|
||||
throw new IllegalStateException(String.format("Registry entry for item %s, id %d, marked as dangling.", item, id));
|
||||
}
|
||||
|
||||
if (item instanceof ItemBlock)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue