diff --git a/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java b/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java index 6777672..574285e 100644 --- a/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java +++ b/src/main/java/cpw/mods/fml/common/FMLCommonHandler.java @@ -17,6 +17,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; @@ -86,6 +88,7 @@ private Set handlerSet = Sets.newSetFromMap(new MapMaker().weakKeys().makeMap()); private WeakReference handlerToCheck; private EventBus eventBus = new EventBus(); + private volatile CountDownLatch exitLatch = null; /** * The FML event bus. Subscribe here for FML related events * @@ -428,6 +431,50 @@ return sidedDelegate.shouldServerShouldBeKilledQuietly(); } + /** + * Make handleExit() wait for handleServerStopped(). + * + * For internal use only! + */ + public void expectServerStopped() + { + exitLatch = new CountDownLatch(1); + } + + /** + * Delayed System.exit() until the server is actually stopped/done saving. + * + * For internal use only! + * + * @param retVal Exit code for System.exit() + */ + public void handleExit(int retVal) + { + CountDownLatch latch = exitLatch; + + if (latch != null) + { + try + { + FMLLog.info("Waiting for the server to terminate/save."); + if (!latch.await(10, TimeUnit.SECONDS)) + { + FMLLog.warning("The server didn't stop within 10 seconds, exiting anyway."); + } + else + { + FMLLog.info("Server terminated."); + } + } + catch (InterruptedException e) + { + FMLLog.warning("Interrupted wait, exiting."); + } + } + + System.exit(retVal); + } + public void handleServerStopped() { sidedDelegate.serverStopped(); @@ -435,6 +482,15 @@ Loader.instance().serverStopped(); // FORCE the internal server to stop: hello optifine workaround! if (server!=null) ObfuscationReflectionHelper.setPrivateValue(MinecraftServer.class, server, false, "field_71316"+"_v", "u", "serverStopped"); + + // allow any pending exit to continue, clear exitLatch + CountDownLatch latch = exitLatch; + + if (latch != null) + { + latch.countDown(); + exitLatch = null; + } } public String getModName() diff --git a/src/main/java/cpw/mods/fml/common/FMLContainer.java b/src/main/java/cpw/mods/fml/common/FMLContainer.java index ab3539c..8ab3c2b 100644 --- a/src/main/java/cpw/mods/fml/common/FMLContainer.java +++ b/src/main/java/cpw/mods/fml/common/FMLContainer.java @@ -59,7 +59,7 @@ meta.name="Forge Mod Loader"; meta.version=Loader.instance().getFMLVersionString(); meta.credits="Made possible with help from many people"; - meta.authorList=Arrays.asList("cpw", "LexManos"); + meta.authorList=Arrays.asList("cpw", "LexManos", "Player"); meta.description="The Forge Mod Loader provides the ability for systems to load mods " + "from the file system. It also provides key capabilities for mods to be able " + "to cooperate and provide a good modding environment. "; @@ -185,23 +185,8 @@ } else { - boolean isItem; - try { - Class clazz = Class.forName(itemType); - clazz.asSubclass(Item.class); - isItem = true; - } - catch (ClassNotFoundException cnfs) - { - FMLLog.warning("The old item %s is not present in this game, it's type cannot be inferred - it will be skipped", itemType); - // MISSING, skip - continue; - } - catch (ClassCastException ccs) - { - isItem = false; - } - String itemLabel = String.format("%c%s:%s", isItem ? '\u0002' : '\u0001', forcedModId != null ? forcedModId : modId, forcedName); + // 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); } } diff --git a/src/main/java/cpw/mods/fml/common/FMLLog.java b/src/main/java/cpw/mods/fml/common/FMLLog.java index ebbd3f3..5c08166 100644 --- a/src/main/java/cpw/mods/fml/common/FMLLog.java +++ b/src/main/java/cpw/mods/fml/common/FMLLog.java @@ -47,8 +47,13 @@ public static void bigWarning(String format, Object... data) { + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); log(Level.WARN, "****************************************"); log(Level.WARN, "* "+format, data); + for (int i = 2; i < 8 && i < trace.length; i++) + { + log(Level.WARN, "* at %s%s", trace[i].toString(), i == 7 ? "..." : ""); + } log(Level.WARN, "****************************************"); } diff --git a/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java b/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java index 5441b43..79ef0fc 100644 --- a/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java +++ b/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleChannelHandlerWrapper.java @@ -13,8 +13,9 @@ public class SimpleChannelHandlerWrapper extends SimpleChannelInboundHandler { private IMessageHandler messageHandler; private Side side; - public SimpleChannelHandlerWrapper(Class> handler, Side side) + public SimpleChannelHandlerWrapper(Class> handler, Side side, Class requestType) { + super(requestType); try { messageHandler = handler.newInstance(); diff --git a/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleNetworkWrapper.java b/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleNetworkWrapper.java index 49a0b6a..679e70b 100644 --- a/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleNetworkWrapper.java +++ b/src/main/java/cpw/mods/fml/common/network/simpleimpl/SimpleNetworkWrapper.java @@ -6,6 +6,7 @@ import net.minecraft.network.INetHandler; import net.minecraft.network.Packet; import net.minecraft.tileentity.TileEntity; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.network.FMLEmbeddedChannel; import cpw.mods.fml.common.network.FMLOutboundHandler; import cpw.mods.fml.common.network.NetworkRegistry; @@ -15,6 +16,8 @@ /** * This class is a simplified netty wrapper for those not wishing to deal with the full power of netty. * It provides a simple message driven system, based on a discriminator byte over the custom packet channel. + * It assumes that you have a series of unique message types with each having a unique handler. Generally, context should be + * derived at message handling time. * * Usage is simple:
    *
  • construct, and store, an instance of this class. It will automatically register and configure your underlying netty channel. @@ -25,10 +28,51 @@ * should be unique for this channelName - it is used to discriminate between different types of message that might * occur on this channel (a simple form of message channel multiplexing, if you will). *
  • To get a packet suitable for presenting to the rest of minecraft, you can call {@link #getPacketFrom(IMessage)}. The return result - * is suitable for returning from things like {@link TileEntity#getDescriptionPacket} for example. + * is suitable for returning from things like {@link TileEntity#getDescriptionPacket()} for example. *
  • Finally, use the sendXXX to send unsolicited messages to various classes of recipients. *
* + * Example + * + *
+ *  // Request message
+ *  public Message1 implements IMessage {
+ *  // message structure
+ *   public fromBytes(ByteBuf buf) {
+ *    // build message from byte array
+ *   }
+ *   public toBytes(ByteBuf buf) {
+ *    // put message content into byte array
+ *   }
+ *  }
+ *  // Reply message
+ *  public Message2 implements IMessage {
+ *   // stuff as before
+ *  }
+ *  // Message1Handler expects input of type Message1 and returns type Message2
+ *  public Message1Handler implements IMessageHandler {
+ *   public Message2 onMessage(Message1 message, MessageContext ctx) {
+ *    // do something and generate reply message
+ *    return aMessage2Object;
+ *   }
+ *  }
+ *  // Message2Handler expects input of type Message2 and returns no message (IMessage)
+ *  public Message2Handler implements IMessageHandler {
+ *   public IMessage onMessage(Message2 message, MessageContext ctx) {
+ *    // handle the message 2 response message at the other end
+ *    // no reply for this message - return null
+ *    return null;
+ *   }
+ *  }
+ *
+ *  // Code in a {@link FMLPreInitializationEvent} or {@link FMLInitializationEvent} handler
+ *  SimpleNetworkWrapper wrapper = NetworkRegistry.newSimpleChannel("MYCHANNEL");
+ *  // Message1 is handled by the Message1Handler class, it has discriminator id 1 and it's on the client
+ *  wrapper.registerMessage(Message1Handler.class, Message1.class, 1, Side.CLIENT);
+ *  // Message2 is handled by the Message2Handler class, it has discriminator id 2 and it's on the server
+ *  wrapper.registerMessage(Message2Handler.class, Message2.class, 2, Side.SERVER);
+ *  
+ *
* * * @author cpw @@ -49,40 +93,40 @@ * be registered on the supplied side (this is the side where you want the message to be processed and acted upon). * * @param messageHandler the message handler type - * @param message the message type + * @param requestMessageType the message type * @param discriminator a discriminator byte * @param side the side for the handler */ - public void registerMessage(Class> messageHandler, Class message, int discriminator, Side side) + public void registerMessage(Class> messageHandler, Class requestMessageType, int discriminator, Side side) { - packetCodec.addDiscriminator(discriminator, message); + packetCodec.addDiscriminator(discriminator, requestMessageType); FMLEmbeddedChannel channel = channels.get(side); String type = channel.findChannelHandlerNameForType(SimpleIndexedCodec.class); if (side == Side.SERVER) { - addServerHandlerAfter(channel, type, messageHandler); + addServerHandlerAfter(channel, type, messageHandler, requestMessageType); } else { - addClientHandlerAfter(channel, type, messageHandler); + addClientHandlerAfter(channel, type, messageHandler, requestMessageType); } } - private void addServerHandlerAfter(FMLEmbeddedChannel channel, String type, Class> messageHandler) + private void addServerHandlerAfter(FMLEmbeddedChannel channel, String type, Class> messageHandler, Class requestType) { - SimpleChannelHandlerWrapper handler = getHandlerWrapper(messageHandler, Side.SERVER); + SimpleChannelHandlerWrapper handler = getHandlerWrapper(messageHandler, Side.SERVER, requestType); channel.pipeline().addAfter(type, messageHandler.getName(), handler); } - private void addClientHandlerAfter(FMLEmbeddedChannel channel, String type, Class> messageHandler) + private void addClientHandlerAfter(FMLEmbeddedChannel channel, String type, Class> messageHandler, Class requestType) { - SimpleChannelHandlerWrapper handler = getHandlerWrapper(messageHandler, Side.CLIENT); + SimpleChannelHandlerWrapper handler = getHandlerWrapper(messageHandler, Side.CLIENT, requestType); channel.pipeline().addAfter(type, messageHandler.getName(), handler); } - private SimpleChannelHandlerWrapper getHandlerWrapper(Class> messageHandler, Side side) + private SimpleChannelHandlerWrapper getHandlerWrapper(Class> messageHandler, Side side, Class requestType) { - return new SimpleChannelHandlerWrapper(messageHandler, side); + return new SimpleChannelHandlerWrapper(messageHandler, side, requestType); } /** diff --git a/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java b/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java index 00f7390..5ee7cb4 100644 --- a/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -10,7 +10,9 @@ import net.minecraft.util.ObjectIntIdentityMap; import net.minecraft.util.RegistryNamespaced; +import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.Loader; @@ -50,7 +52,7 @@ underlyingIntegerMap = new ObjectIntIdentityMap(); registryObjects.clear(); - for (I thing : (Iterable) registry) + for (I thing : registry.typeSafeIterable()) { addObjectRaw(registry.getId(thing), registry.getNameForObject(thing), thing); } @@ -80,12 +82,13 @@ public void putObject(Object objName, Object obj) { String name = (String) objName; - I thing = (I) obj; + I thing = superType.cast(obj); if (name == null) throw new NullPointerException("Can't use a null-name for the registry."); if (name.isEmpty()) throw new IllegalArgumentException("Can't use an empty name for the registry."); if (thing == null) throw new NullPointerException("Can't add null-object to the registry."); + name = ensureNamespaced(name); String existingName = getNameForObject(thing); if (existingName == null) @@ -249,12 +252,17 @@ return containsKey(itemName); } + public Iterable typeSafeIterable() + { + return Iterables.transform(this, new TypeCastFunction()); + } + // internal @SuppressWarnings("unchecked") public void serializeInto(Map idMapping) // for saving { - for (I thing : (Iterable) this) + for (I thing : this.typeSafeIterable()) { idMapping.put(discriminator+getNameForObject(thing), getId(thing)); } @@ -276,29 +284,23 @@ */ int add(int id, String name, I thing, BitSet availabilityMap) { - if (name == null) throw new NullPointerException("Can't use a null-name for the registry."); - if (name.isEmpty()) throw new IllegalArgumentException("Can't use an empty name for the registry."); - if (thing == null) throw new NullPointerException("Can't add null-object to the registry."); + if (name == null) throw new NullPointerException(String.format("Can't use a null-name for the registry, object %s.", thing)); + if (name.isEmpty()) throw new IllegalArgumentException(String.format("Can't use an empty name for the registry, object %s.", thing)); + if (name.indexOf(':') == -1) throw new IllegalArgumentException(String.format("Can't add the name (%s) without a prefix, object %s", name, thing)); + if (thing == null) throw new NullPointerException(String.format("Can't add null-object to the registry, name %s.", name)); if (name.equals(optionalDefaultName)) { this.optionalDefaultObject = thing; } int idToUse = id; - if (id == 0 || availabilityMap.get(id)) + if (idToUse < 0 || availabilityMap.get(idToUse)) { idToUse = availabilityMap.nextClearBit(minId); } if (idToUse > maxId) { - throw new RuntimeException(String.format("Invalid id %d - maximum id range exceeded.", id)); - } - - ModContainer mc = Loader.instance().activeModContainer(); - if (mc != null) - { - String prefix = mc.getModId(); - name = prefix + ":"+ name; + throw new RuntimeException(String.format("Invalid id %d - maximum id range exceeded.", idToUse)); } if (getRaw(name) == thing) // already registered, return prev registration's id @@ -306,13 +308,13 @@ FMLLog.bigWarning("The object %s has been registered twice for the same name %s.", thing, name); return getId(thing); } - if (getRaw(name) != null) // duplicate name, will crash later due to the BiMap + if (getRaw(name) != null) // duplicate name { - FMLLog.bigWarning("The name %s has been registered twice, for %s and %s.", name, getRaw(name), thing); + 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, will crash later due to the BiMap + if (getId(thing) >= 0) // duplicate object { - FMLLog.bigWarning("The object %s has been registered twice, using the names %s and %s.", thing, getNameForObject(thing), name); + throw new IllegalArgumentException(String.format("The object %s has been registered twice, using the names %s and %s.", thing, getNameForObject(thing), name)); } if (GameData.isFrozen(this)) { @@ -321,7 +323,7 @@ addObjectRaw(idToUse, name, thing); - FMLLog.finer("Registry add: %s %d %s", name, idToUse, thing); + FMLLog.finer("Registry add: %s %d %s (req. id %d)", name, idToUse, thing, id); return idToUse; } @@ -336,7 +338,7 @@ { Map ret = new HashMap(); - for (I thing : (Iterable) this) + for (I thing : this.typeSafeIterable()) { if (!registry.field_148758_b.containsKey(thing)) ret.put(getNameForObject(thing), getId(thing)); } @@ -349,7 +351,7 @@ { List ids = new ArrayList(); - for (I thing : (Iterable) this) + for (I thing : this.typeSafeIterable()) { ids.add(getId(thing)); } @@ -369,10 +371,20 @@ */ private void addObjectRaw(int id, String name, I thing) { - if (name == null) throw new NullPointerException(); - if (thing == null) throw new NullPointerException(); + if (name == null) throw new NullPointerException("The name to be added to the registry is null. This can only happen with a corrupted registry state. Reflection/ASM hackery? Registry bug?"); + if (thing == null) throw new NullPointerException("The object to be added to the registry is null. This can only happen with a corrupted registry state. Reflection/ASM hackery? Registry bug?"); + if (!superType.isInstance(thing)) throw new IllegalArgumentException("The object to be added to the registry is not of the right type. Reflection/ASM hackery? Registry bug?"); underlyingIntegerMap.func_148746_a(thing, id); // obj <-> id - super.putObject(ensureNamespaced(name), thing); // name <-> obj + super.putObject(name, thing); // name <-> obj + } + + private class TypeCastFunction implements Function + { + @Override + public I apply(Object o) + { + return superType.cast(o); + } } } \ No newline at end of file diff --git a/src/main/java/cpw/mods/fml/common/registry/GameData.java b/src/main/java/cpw/mods/fml/common/registry/GameData.java index 128bbdc..5e93ad3 100644 --- a/src/main/java/cpw/mods/fml/common/registry/GameData.java +++ b/src/main/java/cpw/mods/fml/common/registry/GameData.java @@ -64,12 +64,12 @@ private static final GameData mainData = new GameData(); /** - * @deprecated use {@link getBlockRegistry()} instead. + * @deprecated use {@link #getBlockRegistry()} instead. */ @Deprecated public static final FMLControlledNamespacedRegistry blockRegistry = getBlockRegistry(); /** - * @deprecated use {@link getItemRegistry()} instead. + * @deprecated use {@link #getItemRegistry()} instead. */ @Deprecated public static final FMLControlledNamespacedRegistry itemRegistry = getItemRegistry(); @@ -455,13 +455,19 @@ } else if (currId != newId) { - FMLLog.fine("Found %s id mismatch %s : %d (was %d)", isBlock ? "block" : "item", itemName, currId, newId); + FMLLog.fine("Fixed %s id mismatch %s: %d (init) -> %d (map).", isBlock ? "block" : "item", itemName, currId, newId); remaps.put(itemName, new Integer[] { currId, newId }); } // register - FMLControlledNamespacedRegistry srcRegistry = isBlock ? getMain().iBlockRegistry : getMain().iItemRegistry; - currId = newData.register(srcRegistry.getRaw(itemName), itemName, newId); + if (isBlock) + { + currId = newData.registerBlock(getMain().iBlockRegistry.getRaw(itemName), itemName, newId); + } + else + { + currId = newData.registerItem(getMain().iItemRegistry.getRaw(itemName), itemName, newId); + } if (currId != newId) { @@ -486,7 +492,7 @@ if (!missingBlocks.isEmpty() || !missingItems.isEmpty()) { - FMLLog.info("Injecting new block and item data into this server instance"); + FMLLog.info("Injecting new block and item data into this server instance."); for (int pass = 0; pass < 2; pass++) { @@ -501,14 +507,14 @@ if (isBlock) { - newId = newData.registerBlock(frozen.iBlockRegistry.getRaw(itemName), itemName, null, currId); + newId = newData.registerBlock(frozen.iBlockRegistry.getRaw(itemName), itemName, currId); } else { - newId = newData.registerItem(frozen.iItemRegistry.getRaw(itemName), itemName, null, currId); + newId = newData.registerItem(frozen.iItemRegistry.getRaw(itemName), itemName, currId); } - FMLLog.info("Injected new block/item %s : %d (was %d)", itemName, newId, currId); + FMLLog.info("Injected new block/item %s: %d (init) -> %d (map).", itemName, currId, newId); if (newId != currId) // a new id was assigned { @@ -551,7 +557,7 @@ newName = getMain().iBlockRegistry.getNameForObject(remap.getTarget()); FMLLog.fine("The Block %s is being remapped to %s.", remap.name, newName); - newId = gameData.registerBlock((Block) remap.getTarget(), newName, null, remap.id); + newId = gameData.registerBlock((Block) remap.getTarget(), newName, remap.id); gameData.iBlockRegistry.addAlias(remap.name, newName); } else @@ -560,7 +566,7 @@ newName = getMain().iItemRegistry.getNameForObject(remap.getTarget()); FMLLog.fine("The Item %s is being remapped to %s.", remap.name, newName); - newId = gameData.registerItem((Item) remap.getTarget(), newName, null, remap.id); + newId = gameData.registerItem((Item) remap.getTarget(), newName, remap.id); gameData.iItemRegistry.addAlias(remap.name, newName); } @@ -568,7 +574,7 @@ if (currId != newId) { - FMLLog.info("Found %s id mismatch %s : %d (was %d)", remap.type == Type.BLOCK ? "block" : "item", newName, currId, newId); + FMLLog.info("Fixed %s id mismatch %s: %d (init) -> %d (map).", remap.type == Type.BLOCK ? "block" : "item", newName, currId, newId); remaps.put(newName, new Integer[] { currId, newId }); } } @@ -642,6 +648,7 @@ { FMLLog.fine("Freezing block and item id maps"); + getMain().testConsistency(); frozen = new GameData(getMain()); frozen.testConsistency(); } @@ -702,15 +709,18 @@ blockedIds.addAll(data.blockedIds); } - int register(Object obj, String name, int idHint) + int register(Object obj, String name, int idHint) // from FMLControlledNamespacedRegistry.addObject { + // tolerate extra name prefixes here since mc does it as well + name = addPrefix(name); + if (obj instanceof Block) { - return registerBlock((Block) obj, name, null, idHint); + return registerBlock((Block) obj, name, idHint); } else if (obj instanceof Item) { - return registerItem((Item) obj, name, null, idHint); + return registerItem((Item) obj, name, idHint); } else { @@ -718,40 +728,49 @@ } } - int registerItem(Item item, String name, String modId) + int registerItem(Item item, String name) // from GameRegistry { - return registerItem(item, name, modId, 0); + int index = name.indexOf(':'); + if (name.indexOf(':') != -1) FMLLog.bigWarning("Illegal extra prefix %s for name %s, invalid registry invocation/invalid name?", name.substring(0, index), name); + + name = addPrefix(name); + return registerItem(item, name, -1); } - int registerItem(Item item, String name, String modId, int idHint) + private int registerItem(Item item, String name, int idHint) { - if (modId != null) - { - ModContainer mc = Loader.instance().activeModContainer(); - customOwners.put(new UniqueIdentifier(modId, name), mc); - } if (item instanceof ItemBlock) // ItemBlock, adjust id and clear the slot already occupied by the corresponding block { Block block = ((ItemBlock) item).field_150939_a; - idHint = iBlockRegistry.getId(block); + int id = iBlockRegistry.getId(block); - if (idHint == -1) // ItemBlock before its Block + if (id == -1) // ItemBlock before its Block { - idHint = availabilityMap.nextClearBit(MIN_BLOCK_ID); // find suitable id here, iItemRegistry would search from MIN_ITEM_ID - if (idHint > MAX_BLOCK_ID) throw new RuntimeException(String.format("Invalid id %d - maximum id range exceeded.", idHint)); + if (idHint < 0 || availabilityMap.get(idHint) || idHint > MAX_BLOCK_ID) // non-suitable id, allocate one in the block id range, add would use the item id range otherwise + { + id = availabilityMap.nextClearBit(MIN_BLOCK_ID); // find suitable id here, iItemRegistry would search from MIN_ITEM_ID + if (id > MAX_BLOCK_ID) throw new RuntimeException(String.format("Invalid id %d - maximum id range exceeded.", id)); + FMLLog.fine("Allocated id %d for ItemBlock %s in the block id range, original id requested: %d.", id, name, idHint); + } + else // idHint is suitable without changes + { + id = idHint; + } } else // ItemBlock after its Block { - FMLLog.fine("Found matching Block %s for ItemBlock %s at id %d", block, item, idHint); - freeSlot(idHint, item); // temporarily free the slot occupied by the Block for the item registration + FMLLog.fine("Found matching Block %s for ItemBlock %s at id %d, original id requested: %d", block, item, id, idHint); + freeSlot(id, item); // temporarily free the slot occupied by the Block for the item registration } + + idHint = id; } int itemId = iItemRegistry.add(idHint, name, item, availabilityMap); if (item instanceof ItemBlock) // verify { - if (itemId != idHint) throw new IllegalStateException("Block -> ItemBlock insertion failed."); + if (itemId != idHint) throw new IllegalStateException(String.format("ItemBlock at block id %d insertion failed, got id %d.", idHint, itemId)); verifyItemBlockName((ItemBlock) item); } @@ -761,23 +780,21 @@ return itemId; } - int registerBlock(Block block, String name, String modId) + int registerBlock(Block block, String name) // from GameRegistry { - return registerBlock(block, name, modId, 0); + int index = name.indexOf(':'); + if (name.indexOf(':') != -1) FMLLog.bigWarning("Illegal extra prefix %s for name %s, invalid registry invocation/invalid name?", name.substring(0, index), name); + + name = addPrefix(name); + return registerBlock(block, name, -1); } - int registerBlock(Block block, String name, String modId, int idHint) + private int registerBlock(Block block, String name, int idHint) { - if (modId != null) - { - ModContainer mc = Loader.instance().activeModContainer(); - customOwners.put(new UniqueIdentifier(modId, name), mc); - } - // handle ItemBlock-before-Block registrations ItemBlock itemBlock = null; - for (Item item : (Iterable) iItemRegistry) // find matching ItemBlock + for (Item item : iItemRegistry.typeSafeIterable()) // find matching ItemBlock { if (item instanceof ItemBlock && ((ItemBlock) item).field_150939_a == block) { @@ -798,7 +815,7 @@ if (itemBlock != null) // verify { - if (blockId != idHint) throw new IllegalStateException("ItemBlock -> Block insertion failed."); + if (blockId != idHint) throw new IllegalStateException(String.format("Block at itemblock id %d insertion failed, got id %d.", idHint, blockId)); verifyItemBlockName(itemBlock); } @@ -843,6 +860,42 @@ availabilityMap.clear(id); } + /** + * Prefix the supplied name with the current mod id. + * + * If no mod id can be determined, minecraft will be assumed. + * The prefix is separated with a colon. + * + * If there's already a prefix, it'll be prefixed again if the new prefix + * doesn't match the old prefix, as used by vanilla calls to addObject. + * + * @param name name to prefix. + * @return prefixed name. + */ + private String addPrefix(String name) + { + int index = name.lastIndexOf(':'); + String oldPrefix = index == -1 ? "" : name.substring(0, index); + String prefix; + ModContainer mc = Loader.instance().activeModContainer(); + + if (mc != null) + { + prefix = mc.getModId(); + } + else // no mod container, assume minecraft + { + prefix = "minecraft"; + } + + if (!oldPrefix.equals(prefix)) + { + name = prefix + ":" + name; + } + + return name; + } + private void verifyItemBlockName(ItemBlock item) { String blockName = iBlockRegistry.getNameForObject(item.field_150939_a); @@ -865,53 +918,52 @@ } } - // 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 (Block block : (Iterable) iBlockRegistry) + for (int pass = 0; pass < 2; pass++) { - int id = iBlockRegistry.getId(block); - String name = iBlockRegistry.getNameForObject(block); + boolean isBlock = pass == 0; + String type = isBlock ? "block" : "item"; + FMLControlledNamespacedRegistry registry = isBlock ? iBlockRegistry : iItemRegistry; - if (id < 0) { - throw new IllegalStateException(String.format("Registry entry for block %s, name %s, doesn't yield an id", block, name)); - } - if (name == null) { - throw new IllegalStateException(String.format("Registry entry for block %s, id %d, doesn't yield a name", block, id)); - } - if (!availabilityMap.get(id)) { - throw new IllegalStateException(String.format("Registry entry for block %s, id %d, name %s, marked as empty.", block, id, name)); - } - if (blockedIds.contains(id)) { - throw new IllegalStateException(String.format("Registry entry for block %s, id %d, name %s, marked as dangling.", block, id, name)); - } - } - - // 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 (Item item : (Iterable) iItemRegistry) - { - int id = iItemRegistry.getId(item); - String name = iItemRegistry.getNameForObject(item); - - if (id < 0) { - throw new IllegalStateException(String.format("Registry entry for item %s, name %s, doesn't yield an id", item, name)); - } - if (name == null) { - throw new IllegalStateException(String.format("Registry entry for item %s, id %d, doesn't yield a name", item, id)); - } - if (!availabilityMap.get(id)) { - throw new IllegalStateException(String.format("Registry entry for item %s, id %d, name %s, marked as empty.", item, id, name)); - } - if (blockedIds.contains(id)) { - throw new IllegalStateException(String.format("Registry entry for item %s, id %d, name %s, marked as dangling.", item, id, name)); - } - - if (item instanceof ItemBlock) + // test if the registry is consistent + // test if there's a bit in availabilityMap set for every entry in the registry + // make sure it's not a blocked id + for (Object obj : registry) { - Block block = ((ItemBlock) item).field_150939_a; + int id = registry.getId(obj); + String name = registry.getNameForObject(obj); - if (iBlockRegistry.getId(block) != id) + // id lookup failed -> obj is not in the obj<->id map + if (id < 0) throw new IllegalStateException(String.format("Registry entry for %s %s, name %s, doesn't yield an id.", type, obj, name)); + // id is too high + if (id > (isBlock ? MAX_BLOCK_ID : MAX_ITEM_ID)) throw new IllegalStateException(String.format("Registry entry for %s %s, name %s uses the too large id %d.", type, obj, name)); + // name lookup failed -> obj is not in the obj<->name map + if (name == null) throw new IllegalStateException(String.format("Registry entry for %s %s, id %d, doesn't yield a name.", type, obj, id)); + // empty name + if (name.isEmpty()) throw new IllegalStateException(String.format("Registry entry for %s %s, id %d, yields an empty name.", type, obj, id)); + // non-prefixed name + if (name.indexOf(':') == -1) throw new IllegalStateException(String.format("Registry entry for %s %s, id %d, has the non-prefixed name %s.", type, obj, id, name)); + // id -> obj lookup is inconsistent + if (registry.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 (registry.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 (registry.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 + if (blockedIds.contains(id)) throw new IllegalStateException(String.format("Registry entry for %s %s, id %d, name %s, marked as dangling.", type, obj, id, name)); + + if (obj instanceof ItemBlock) { - throw new IllegalStateException(String.format("Registry entry for ItemBlock %s, id %d, is missing or uses the non-matching id %d.", item, id, iBlockRegistry.getId(block))); + Block block = ((ItemBlock) obj).field_150939_a; + + // verify matching block entry + if (iBlockRegistry.getId(block) != id) + { + throw new IllegalStateException(String.format("Registry entry for ItemBlock %s, id %d, is missing or uses the non-matching id %d.", obj, id, iBlockRegistry.getId(block))); + } + // verify id range + if (id > MAX_BLOCK_ID) throw new IllegalStateException(String.format("ItemBlock %s uses the id %d outside the block id range", name, id)); } } } diff --git a/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java b/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java index 7891db8..6a9e604 100644 --- a/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java +++ b/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java @@ -61,7 +61,7 @@ * Register a world generator - something that inserts new block types into the world * * @param generator the generator - * @param weight a weight to assign to this generator. Heavy weights tend to sink to the bottom of + * @param modGenerationWeight a weight to assign to this generator. Heavy weights tend to sink to the bottom of * list of world generators (i.e. they run later) */ public static void registerWorldGenerator(IWorldGenerator generator, int modGenerationWeight) @@ -131,12 +131,12 @@ * Register the specified Item with a mod specific name : overrides the standard type based name * @param item The item to register * @param name The mod-unique name to register it as - null will remove a custom name - * @param modId An optional modId that will "own" this block - generally used by multi-mod systems + * @param modId deprecated, unused * where one mod should "own" all the blocks of all the mods, null defaults to the active mod */ public static Item registerItem(Item item, String name, String modId) { - GameData.getMain().registerItem(item, name, modId); + GameData.getMain().registerItem(item, name); return item; } @@ -145,33 +145,45 @@ { } + /** * Register a block with the specified mod specific name * @param block The block to register - * @param name The mod-unique name to register it as + * @param name The mod-unique name to register it as, will get prefixed by your modid. */ public static Block registerBlock(Block block, String name) { return registerBlock(block, ItemBlock.class, name); } + /** * Register a block with the world, with the specified item class and block name * @param block The block to register * @param itemclass The item type to register with it : null registers a block without associated item. - * @param name The mod-unique name to register it with + * @param name The mod-unique name to register it as, will get prefixed by your modid. */ public static Block registerBlock(Block block, Class itemclass, String name) { - return registerBlock(block, itemclass, name, null); + return registerBlock(block, itemclass, name, new Object[]{}); } + + /** + * @deprecated Use the registerBlock version without the modId parameter instead. + */ + @Deprecated + public static Block registerBlock(Block block, Class itemclass, String name, String modId, Object... itemCtorArgs) + { + return registerBlock(block, itemclass, name, itemCtorArgs); + } + /** * Register a block with the world, with the specified item class, block name and owning modId * @param block The block to register * @param itemclass The item type to register with it : null registers a block without associated item. - * @param name The mod-unique name to register it with - * @param modId The modId that will own the block name. null defaults to the active modId + * @param name The mod-unique name to register it as, will get prefixed by your modid. + * @param itemCtorArgs Arguments to pass to the ItemBlock constructor (optional). */ - public static Block registerBlock(Block block, Class itemclass, String name, String modId, Object... itemCtorArgs) + public static Block registerBlock(Block block, Class itemclass, String name, Object... itemCtorArgs) { if (Loader.instance().isInState(LoaderState.CONSTRUCTING)) { @@ -193,10 +205,10 @@ i = itemCtor.newInstance(ObjectArrays.concat(block, itemCtorArgs)); } // block registration has to happen first - GameData.getMain().registerBlock(block, name, modId); + GameData.getMain().registerBlock(block, name); if (i != null) { - GameData.getMain().registerItem(i, name, modId); + GameData.getMain().registerItem(i, name); } return block; } diff --git a/src/main/java/net/minecraft/client/Minecraft.java b/src/main/java/net/minecraft/client/Minecraft.java index 03e3cfb..ea23832 100644 --- a/src/main/java/net/minecraft/client/Minecraft.java +++ b/src/main/java/net/minecraft/client/Minecraft.java @@ -347,21 +347,23 @@ File file2 = new File(file1, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt"); System.out.println(par1CrashReport.getCompleteReport()); + int retVal; if (par1CrashReport.getFile() != null) { System.out.println("#@!@# Game crashed! Crash report saved to: #@!@# " + par1CrashReport.getFile()); - System.exit(-1); + retVal = -1; } else if (par1CrashReport.saveToFile(file2)) { System.out.println("#@!@# Game crashed! Crash report saved to: #@!@# " + file2.getAbsolutePath()); - System.exit(-1); + retVal = -1; } else { System.out.println("#@?@# Game crashed! Crash report could not be saved. #@?@#"); - System.exit(-2); + retVal = -2; } + FMLCommonHandler.instance().handleExit(retVal); } public void setServer(String par1Str, int par2) diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 6558617..5308180 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -318,7 +318,7 @@ public void stopServer() { - if (!this.worldIsBeingDeleted && Loader.instance().hasReachedState(LoaderState.SERVER_STARTED)) + if (!this.worldIsBeingDeleted && Loader.instance().hasReachedState(LoaderState.SERVER_STARTED) && !serverStopped) // make sure the save is valid and we don't save twice { logger.info("Stopping server"); @@ -419,15 +419,18 @@ this.serverIsRunning = true; } FMLCommonHandler.instance().handleServerStopping(); + FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions } else { + FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions this.finalTick((CrashReport)null); } } catch (StartupQuery.AbortedException e) { // ignore silently + FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions } catch (Throwable throwable1) { @@ -454,6 +457,7 @@ logger.error("We were unable to save this crash report to disk."); } + FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions this.finalTick(crashreport); } finally diff --git a/src/main/resources/mcpmod.info b/src/main/resources/mcpmod.info index 67862bc..81930be 100644 --- a/src/main/resources/mcpmod.info +++ b/src/main/resources/mcpmod.info @@ -3,8 +3,8 @@ "modid": "mcp", "name": "Minecraft Coder Pack", "description": "Modding toolkit to decompile and deobfuscate the Minecraft client and server files.", - "version": "8.09", - "mcversion": "1.6.3", + "version": "9.03", + "mcversion": "1.7.2", "logoFile": "/mcplogo.png", "url": "http://mcp.ocean-labs.de/", "updateUrl": "",