From 41e930a4b9adfda0d64529998100dc2cecc48848 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 24 Dec 2013 00:47:10 -0500 Subject: [PATCH] Fix handshaking properly. Also, fix ID syncup. It now will completely freeze the idmap after preinit. This frozen map is used to inject "new" stuff into existing serverside worlds. Interesting sideeffect: remote servers lacking things will change the client, to also LACK ids for those things. watch for -1 ids in your remap events. Note: idmaps should be considered temporary per server instance. SERVERSTOPPED will restore "startup" state, as will client logout. --- .../net/minecraft/client/Minecraft.java.patch | 18 ++++-- .../cpw/mods/fml/client/FMLClientHandler.java | 6 ++ .../cpw/mods/fml/common/FMLContainer.java | 2 +- .../main/java/cpw/mods/fml/common/Loader.java | 2 + .../handshake/FMLHandshakeClientState.java | 23 ++++++-- .../handshake/FMLHandshakeMessage.java | 37 +++++++++++- .../handshake/FMLHandshakeServerState.java | 4 +- .../handshake/HandshakeMessageHandler.java | 4 ++ .../network/handshake/NetworkDispatcher.java | 15 ++--- .../common/network/internal/FMLMessage.java | 3 +- .../FMLControlledNamespacedRegistry.java | 53 ++++++++++++++++-- .../mods/fml/common/registry/GameData.java | 56 ++++++++++++++++++- 12 files changed, 195 insertions(+), 28 deletions(-) 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(); + } }