mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 08:17:35 +00:00
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.
This commit is contained in:
1 parent
54731a2acc
commit
41e930a4b9
12 files changed
+195
-28
No files matched your search
@@ -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();
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
+17
-6
@@ -57,14 +57,14 @@ enum FMLHandshakeClientState implements IHandshakeState<FMLHandshakeClientState>
|
||||
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<FMLHandshakeClientState>
|
||||
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<FMLHandshakeClientState>
|
||||
@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;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -110,6 +110,12 @@ public abstract class FMLHandshakeMessage {
|
||||
{
|
||||
return modTags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Class<? extends Enum<?>> 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<? extends Enum<?>> 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<? extends Enum<?>> side)
|
||||
{
|
||||
return super.toString(side) + ":{"+phase+"}";
|
||||
}
|
||||
}
|
||||
public void fromBytes(ByteBuf buffer)
|
||||
{
|
||||
@@ -162,5 +194,8 @@ public abstract class FMLHandshakeMessage {
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public String toString(Class<? extends Enum<?>> side)
|
||||
{
|
||||
return getClass().getName().substring(getClass().getName().lastIndexOf('$'));
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -57,7 +57,7 @@ enum FMLHandshakeServerState implements IHandshakeState<FMLHandshakeServerState>
|
||||
{
|
||||
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<FMLHandshakeServerState>
|
||||
@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;
|
||||
|
||||
@@ -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<S extends Enum<S> & IHandshakeState<S>> ext
|
||||
private static final AttributeKey<IHandshakeState<?>> STATE = new AttributeKey<IHandshakeState<?>>("fml:handshake-state");
|
||||
private final AttributeKey<S> fmlHandshakeState;
|
||||
private S initialState;
|
||||
private Class<S> stateType;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public HandshakeMessageHandler(Class<S> stateType)
|
||||
{
|
||||
fmlHandshakeState = (AttributeKey<S>) ((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);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import cpw.mods.fml.relauncher.Side;
|
||||
|
||||
public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> 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<Packet> 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<Packet> 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<Packet> imple
|
||||
completeServerSideConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public void completeClientHandshake()
|
||||
{
|
||||
state = ConnectionState.HANDSHAKECOMPLETE;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
+48
-5
@@ -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<I> extends RegistryNamespaced {
|
||||
static class FMLObjectIntIdentityMap extends ObjectIntIdentityMap {
|
||||
private TIntIntHashMap frozenMap;
|
||||
private TIntIntHashMap oldMap;
|
||||
private TIntIntHashMap newMap;
|
||||
private ArrayList<Integer> frozenIndex;
|
||||
private ArrayList<Integer> oldIndex;
|
||||
private ArrayList<Integer> newIndex;
|
||||
|
||||
@@ -54,6 +53,18 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
|
||||
newIndex = new ArrayList<Integer>(oldIndex.size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void freezeMap()
|
||||
{
|
||||
frozenMap = new TIntIntHashMap(field_148749_a);
|
||||
frozenIndex = new ArrayList<Integer>(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<I> extends RegistryNamespaced {
|
||||
private I optionalDefaultObject;
|
||||
|
||||
private BiMap<String,Integer> namedIds = HashBiMap.create();
|
||||
private BiMap<String,Integer> frozenIds;
|
||||
private Map<String,Integer> transactionalNamedIds;
|
||||
private BitSet availabilityMap;
|
||||
private int maxId;
|
||||
@@ -114,6 +126,11 @@ public class FMLControlledNamespacedRegistry<I> 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<I> 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<String,Integer> getMissingMappings()
|
||||
{
|
||||
return Maps.difference(frozenIds, transactionalNamedIds).entriesOnlyOnLeft();
|
||||
}
|
||||
void completeIdSwap()
|
||||
{
|
||||
idMap().completeSwap();
|
||||
@@ -232,7 +268,14 @@ public class FMLControlledNamespacedRegistry<I> 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)
|
||||
|
||||
@@ -214,7 +214,7 @@ public class GameData {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean injectWorldIDMap(Map<String, Integer> dataList)
|
||||
public static boolean injectWorldIDMap(Map<String, Integer> dataList, boolean injectFrozenData)
|
||||
{
|
||||
Map<String, Integer[]> remaps = Maps.newHashMap();
|
||||
ArrayListMultimap<String,String> 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<String, Integer> missingBlocks = Maps.newHashMap(blockRegistry.getMissingMappings());
|
||||
Map<String, Integer> missingItems = Maps.newHashMap(itemRegistry.getMissingMappings());
|
||||
|
||||
for (Entry<String, Integer> 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<String, Integer> 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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user