diff --git a/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch b/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch index ffa2859f90..80ffafed92 100644 --- a/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch +++ b/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch @@ -44,7 +44,7 @@ Display.setVSyncEnabled(this.field_71474_y.field_74352_v); } -@@ -916,9 +923,11 @@ +@@ -915,9 +922,11 @@ if (!this.field_71454_w) { @@ -56,7 +56,7 @@ } GL11.glFlush(); -@@ -1496,6 +1505,8 @@ +@@ -1495,6 +1504,8 @@ --this.field_71467_ac; } @@ -65,7 +65,7 @@ this.field_71424_I.func_76320_a("gui"); if (!this.field_71445_n) -@@ -1646,6 +1657,7 @@ +@@ -1645,6 +1656,7 @@ this.field_71462_r.func_146274_d(); } } @@ -73,7 +73,7 @@ } if (this.field_71429_W > 0) -@@ -1787,6 +1799,7 @@ +@@ -1786,6 +1798,7 @@ } } } @@ -81,7 +81,7 @@ } } -@@ -1978,6 +1991,8 @@ +@@ -1977,6 +1990,8 @@ this.field_71453_ak.func_74428_b(); } @@ -90,3 +90,11 @@ this.field_71424_I.func_76319_b(); this.field_71423_H = func_71386_F(); } +@@ -2093,6 +2108,7 @@ + this.field_110448_aq.func_148529_f(); + this.func_71351_a((ServerData)null); + this.field_71455_al = false; ++ FMLClientHandler.instance().handleClientWorldClosing(this.field_71441_e); + } + + this.field_147127_av.func_147690_c(); diff --git a/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java b/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java index f2ee6384d9..d23a3bde4f 100644 --- a/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java +++ b/fml/src/main/java/cpw/mods/fml/client/FMLClientHandler.java @@ -54,6 +54,7 @@ import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.ObfuscationReflectionHelper; import cpw.mods.fml.common.WrongMinecraftVersionException; +import cpw.mods.fml.common.registry.GameData; import cpw.mods.fml.common.toposort.ModSortingException; import cpw.mods.fml.relauncher.Side; @@ -488,4 +489,9 @@ public class FMLClientHandler implements IFMLSidedHandler { return this.client.func_147114_u()!=null ? this.client.func_147114_u().func_147298_b() : null; } + + public void handleClientWorldClosing(WorldClient field_71441_e) + { + GameData.revertToFrozen(); + } } diff --git a/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java b/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java index d39c2f2543..29e15e5749 100644 --- a/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java +++ b/fml/src/main/java/cpw/mods/fml/common/FMLContainer.java @@ -146,7 +146,7 @@ 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")); } - boolean successfullyInjected = GameData.injectWorldIDMap(dataList); + boolean successfullyInjected = GameData.injectWorldIDMap(dataList, true); if (!successfullyInjected) { throw new RuntimeException("Failed to load the world - there are fatal block and item id issues"); diff --git a/fml/src/main/java/cpw/mods/fml/common/Loader.java b/fml/src/main/java/cpw/mods/fml/common/Loader.java index ab208de379..9878bab16a 100644 --- a/fml/src/main/java/cpw/mods/fml/common/Loader.java +++ b/fml/src/main/java/cpw/mods/fml/common/Loader.java @@ -496,6 +496,7 @@ public class Loader } modController.transition(LoaderState.PREINITIALIZATION, false); modController.distributeStateMessage(LoaderState.PREINITIALIZATION, disc.getASMTable(), canonicalConfigDir); + GameData.freezeData(); modController.transition(LoaderState.INITIALIZATION, false); } @@ -778,6 +779,7 @@ public class Loader public void serverStopped() { + GameData.revertToFrozen(); modController.distributeStateMessage(LoaderState.SERVER_STOPPED); modController.transition(LoaderState.SERVER_STOPPED, true); modController.transition(LoaderState.AVAILABLE, true); diff --git a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java index 9079525113..30940037ad 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java @@ -57,14 +57,14 @@ enum FMLHandshakeClientState implements IHandshakeState dispatcher.rejectHandshake(result); return ERROR; } - ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck()); + ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck(ordinal())); if (!ctx.channel().attr(NetworkDispatcher.IS_LOCAL).get()) { return WAITINGSERVERCOMPLETE; } else { - return COMPLETE; + return PENDINGCOMPLETE; } } }, @@ -74,13 +74,22 @@ enum FMLHandshakeClientState implements IHandshakeState public FMLHandshakeClientState accept(ChannelHandlerContext ctx, FMLHandshakeMessage msg) { FMLHandshakeMessage.ModIdData modIds = (FMLHandshakeMessage.ModIdData)msg; - if (!GameData.injectWorldIDMap(modIds.dataList())) + if (!GameData.injectWorldIDMap(modIds.dataList(), false)) { NetworkDispatcher dispatcher = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); dispatcher.rejectHandshake("Fatally missing blocks and items"); return ERROR; } - ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck()); + ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck(ordinal())); + return PENDINGCOMPLETE; + } + }, + PENDINGCOMPLETE + { + @Override + public FMLHandshakeClientState accept(ChannelHandlerContext ctx, FMLHandshakeMessage msg) + { + ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck(ordinal())); return COMPLETE; } }, @@ -89,9 +98,11 @@ enum FMLHandshakeClientState implements IHandshakeState @Override public FMLHandshakeClientState accept(ChannelHandlerContext ctx, FMLHandshakeMessage msg) { - FMLMessage.CompleteHandshake complete = new FMLMessage.CompleteHandshake(Side.SERVER); - ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck()); + NetworkDispatcher dispatcher = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); + dispatcher.completeClientHandshake(); + FMLMessage.CompleteHandshake complete = new FMLMessage.CompleteHandshake(Side.CLIENT); ctx.fireChannelRead(complete); + ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck(ordinal())); return DONE; } }, diff --git a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java index a9e8d475c5..75b4b7aee6 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java @@ -110,6 +110,12 @@ public abstract class FMLHandshakeMessage { { return modTags; } + + @Override + public String toString(Class> side) + { + return super.toString(side)+":"+modTags.size()+" mods"; + } } public static class ModIdData extends FMLHandshakeMessage { @@ -150,9 +156,35 @@ public abstract class FMLHandshakeMessage { { return modIds; } + @Override + public String toString(Class> side) + { + return super.toString(side) + ":"+modIds.size()+" mappings"; + } } public static class HandshakeAck extends FMLHandshakeMessage { + int phase; + public HandshakeAck() {} + HandshakeAck(int phase) + { + this.phase = phase; + } + @Override + public void fromBytes(ByteBuf buffer) + { + phase = buffer.readByte(); + } + @Override + public void toBytes(ByteBuf buffer) + { + buffer.writeByte(phase); + } + @Override + public String toString(Class> side) + { + return super.toString(side) + ":{"+phase+"}"; + } } public void fromBytes(ByteBuf buffer) { @@ -162,5 +194,8 @@ public abstract class FMLHandshakeMessage { { } - + public String toString(Class> side) + { + return getClass().getName().substring(getClass().getName().lastIndexOf('$')); + } } diff --git a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java index de01c8b4ee..79315fd288 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java @@ -57,7 +57,7 @@ enum FMLHandshakeServerState implements IHandshakeState { ctx.writeAndFlush(new FMLHandshakeMessage.ModIdData(GameData.buildItemDataList())); } - ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck()); + ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck(ordinal())); NetworkRegistry.INSTANCE.fireNetworkHandshake(ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(), Side.SERVER); return COMPLETE; } @@ -67,6 +67,8 @@ enum FMLHandshakeServerState implements IHandshakeState @Override public FMLHandshakeServerState accept(ChannelHandlerContext ctx, FMLHandshakeMessage msg) { + // Poke the client + ctx.writeAndFlush(new FMLHandshakeMessage.HandshakeAck(ordinal())); FMLMessage.CompleteHandshake complete = new FMLMessage.CompleteHandshake(Side.SERVER); ctx.fireChannelRead(complete); return DONE; diff --git a/fml/src/main/java/cpw/mods/fml/common/network/handshake/HandshakeMessageHandler.java b/fml/src/main/java/cpw/mods/fml/common/network/handshake/HandshakeMessageHandler.java index c7074a79a9..008ae53512 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/handshake/HandshakeMessageHandler.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/handshake/HandshakeMessageHandler.java @@ -1,5 +1,6 @@ package cpw.mods.fml.common.network.handshake; +import cpw.mods.fml.common.FMLLog; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.AttributeKey; @@ -8,17 +9,20 @@ public class HandshakeMessageHandler & IHandshakeState> ext private static final AttributeKey> STATE = new AttributeKey>("fml:handshake-state"); private final AttributeKey fmlHandshakeState; private S initialState; + private Class stateType; @SuppressWarnings("unchecked") public HandshakeMessageHandler(Class stateType) { fmlHandshakeState = (AttributeKey) ((Object)STATE); initialState = Enum.valueOf(stateType, "START"); + this.stateType = stateType; } @Override protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception { S state = ctx.attr(fmlHandshakeState).get(); + FMLLog.info(msg.toString(stateType) + "->" + state.getClass().getName().substring(state.getClass().getName().lastIndexOf('.')+1)+":"+state); S newState = state.accept(ctx, msg); ctx.attr(fmlHandshakeState).set(newState); } diff --git a/fml/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java b/fml/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java index 7e0b6bd5c1..0c4b7e9e36 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java @@ -36,7 +36,7 @@ import cpw.mods.fml.relauncher.Side; public class NetworkDispatcher extends SimpleChannelInboundHandler implements ChannelOutboundHandler { private static enum ConnectionState { - OPENING, AWAITING_HANDSHAKE, HANDSHAKING, CONNECTED; + OPENING, AWAITING_HANDSHAKE, HANDSHAKING, HANDSHAKECOMPLETE, CONNECTED; } private static enum ConnectionType { @@ -172,12 +172,7 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler imple { handled = handleClientSideCustomPacket((S3FPacketCustomPayload)msg, ctx); } - else if (msg instanceof S40PacketDisconnect && state != ConnectionState.CONNECTED) - { - // Switch to play state to handle the disconnect message - completeClientSideConnection(); - } - else if (state != ConnectionState.CONNECTED) + else if (state != ConnectionState.CONNECTED && state != ConnectionState.HANDSHAKECOMPLETE) { FMLLog.info("Unexpected packet during modded negotiation - assuming vanilla or keepalives : %s", msg.getClass().getName()); } @@ -379,6 +374,7 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler imple { if (state == ConnectionState.CONNECTED) { + FMLLog.severe("Attempt to double complete the network connection!"); throw new FMLNetworkException("Attempt to double complete!"); } if (side == Side.CLIENT) @@ -390,4 +386,9 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler imple completeServerSideConnection(); } } + + public void completeClientHandshake() + { + state = ConnectionState.HANDSHAKECOMPLETE; + } } diff --git a/fml/src/main/java/cpw/mods/fml/common/network/internal/FMLMessage.java b/fml/src/main/java/cpw/mods/fml/common/network/internal/FMLMessage.java index edce8a5c00..2efc50785d 100644 --- a/fml/src/main/java/cpw/mods/fml/common/network/internal/FMLMessage.java +++ b/fml/src/main/java/cpw/mods/fml/common/network/internal/FMLMessage.java @@ -21,7 +21,8 @@ import cpw.mods.fml.relauncher.Side; public abstract class FMLMessage { public static class CompleteHandshake extends FMLMessage { Side target; - public CompleteHandshake() {} + public CompleteHandshake() { + } public CompleteHandshake(Side target) { this.target = target; diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java b/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java index 9a28cf2dd8..83a38b5788 100644 --- a/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/fml/src/main/java/cpw/mods/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -1,28 +1,27 @@ package cpw.mods.fml.common.registry; import gnu.trove.map.hash.TIntIntHashMap; - import java.util.ArrayList; import java.util.BitSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; - import net.minecraft.util.ObjectIntIdentityMap; import net.minecraft.util.RegistryNamespaced; - import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.Maps; import com.google.common.primitives.Ints; - import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; public class FMLControlledNamespacedRegistry extends RegistryNamespaced { static class FMLObjectIntIdentityMap extends ObjectIntIdentityMap { + private TIntIntHashMap frozenMap; private TIntIntHashMap oldMap; private TIntIntHashMap newMap; + private ArrayList frozenIndex; private ArrayList oldIndex; private ArrayList newIndex; @@ -54,6 +53,18 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { newIndex = new ArrayList(oldIndex.size()); } + @SuppressWarnings("unchecked") + void freezeMap() + { + frozenMap = new TIntIntHashMap(field_148749_a); + frozenIndex = new ArrayList(field_148748_b); + } + + void revertToFrozen() + { + field_148749_a = frozenMap; + field_148748_b = frozenIndex; + } void completeSwap() { field_148749_a = newMap; @@ -91,6 +102,7 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { private I optionalDefaultObject; private BiMap namedIds = HashBiMap.create(); + private BiMap frozenIds; private Map transactionalNamedIds; private BitSet availabilityMap; private int maxId; @@ -114,6 +126,11 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { add(id, name, superType.cast(thing)); } + int swap(int id, String name, I thing) + { + field_82596_a.remove(name); + return add(id, name, thing); + } public int add(int id, String name, I thing) { if (name.equals(optionalDefaultName)) @@ -182,6 +199,25 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { transactionalNamedIds.put(name,newId); } + void freezeMap() + { + if (frozenIds == null) + { + frozenIds = ImmutableBiMap.copyOf(namedIds); + idMap().freezeMap(); + } + } + + void revertToFrozen() + { + namedIds = HashBiMap.create(frozenIds); + idMap().revertToFrozen(); + } + + Map getMissingMappings() + { + return Maps.difference(frozenIds, transactionalNamedIds).entriesOnlyOnLeft(); + } void completeIdSwap() { idMap().completeSwap(); @@ -232,7 +268,14 @@ public class FMLControlledNamespacedRegistry extends RegistryNamespaced { public int getId(String itemName) { - return namedIds.get(itemName); + if (namedIds.containsKey(itemName)) + { + return namedIds.get(itemName); + } + else + { + return -1; + } } public boolean contains(String itemName) diff --git a/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java b/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java index 4d63bea631..f25b805186 100644 --- a/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java +++ b/fml/src/main/java/cpw/mods/fml/common/registry/GameData.java @@ -214,7 +214,7 @@ public class GameData { } } - public static boolean injectWorldIDMap(Map dataList) + public static boolean injectWorldIDMap(Map dataList, boolean injectFrozenData) { Map remaps = Maps.newHashMap(); ArrayListMultimap missing = ArrayListMultimap.create(); @@ -265,6 +265,46 @@ public class GameData { return false; } + if (injectFrozenData) + { + FMLLog.info("Injecting new block and item data from this server instance"); + Map missingBlocks = Maps.newHashMap(blockRegistry.getMissingMappings()); + Map missingItems = Maps.newHashMap(itemRegistry.getMissingMappings()); + + for (Entry item: missingItems.entrySet()) + { + String itemName = item.getKey(); + if (missingBlocks.containsKey(itemName)) + { + FMLLog.info("Injecting new block/item %s", itemName); + int blockId = blockRegistry.swap(item.getValue(), itemName, blockRegistry.get(itemName)); + itemRegistry.swap(blockId, itemName, itemRegistry.get(itemName)); + missingBlocks.remove(itemName); + if (Integer.valueOf(blockId) != item.getValue()) + { + remaps.put(itemName, new Integer[] { item.getValue(), blockId }); + } + } + else + { + FMLLog.info("Injecting new item %s", itemName); + int itemId = itemRegistry.swap(item.getValue(), itemName, itemRegistry.get(itemName)); + if (Integer.valueOf(itemId) != item.getValue()) + { + remaps.put(itemName, new Integer[] { item.getValue(), itemId }); + } + } + } + for (Entry block : missingBlocks.entrySet()) + { + FMLLog.info("Injecting new block %s", block.getKey()); + int blockId = blockRegistry.swap(block.getValue(), block.getKey(), blockRegistry.get(block.getKey())); + if (Integer.valueOf(blockId) != block.getValue()) + { + remaps.put(block.getKey(), new Integer[] { block.getValue(), blockId }); + } + } + } blockRegistry.completeIdSwap(); itemRegistry.completeIdSwap(); Loader.instance().fireRemapEvent(remaps); @@ -298,4 +338,18 @@ public class GameData { } return true; } + + public static void freezeData() + { + FMLLog.fine("Freezing block and item id maps"); + blockRegistry.freezeMap(); + itemRegistry.freezeMap(); + } + + public static void revertToFrozen() + { + FMLLog.fine("Reverting to frozen data state"); + blockRegistry.revertToFrozen(); + itemRegistry.revertToFrozen(); + } }