More cleanup. Bidirectional server <-> client network works for mods as well as FML|HS now.

This commit is contained in:
Christian 2013-12-06 11:17:40 -05:00
parent add83f145a
commit 89c7a02146
15 changed files with 291 additions and 63 deletions

View File

@ -81,3 +81,19 @@
this.field_71424_I.func_76319_b();
this.field_71423_H = func_71386_F();
}
@@ -2046,6 +2063,7 @@
networkmanager.func_150725_a(new C00Handshake(4, socketaddress.toString(), 0, EnumConnectionState.LOGIN), new GenericFutureListener[0]);
networkmanager.func_150725_a(new C00PacketLoginStart(this.func_110432_I().func_148256_e()), new GenericFutureListener[0]);
this.field_71453_ak = networkmanager;
+ FMLClientHandler.instance().currentConnection(networkmanager, true);
}
public void func_71403_a(WorldClient p_71403_1_)
@@ -2076,6 +2094,7 @@
this.field_71451_h = null;
this.field_71453_ak = null;
+ FMLClientHandler.instance().currentConnection(null, p_71353_1_ == null);
if (this.field_71461_s != null)
{

View File

@ -30,6 +30,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
@ -122,6 +123,8 @@ public class FMLClientHandler implements IFMLSidedHandler
private Map<String, IResourcePack> resourcePackMap;
private NetworkManager networkConnection;
/**
* Called to start the whole game off
*
@ -564,4 +567,15 @@ public class FMLClientHandler implements IFMLSidedHandler
ObfuscationReflectionHelper.setPrivateValue(MinecraftServer.class, server, true, "field_71296"+"_Q","serverIs"+"Running");
}
}
@Override
public NetworkManager getClientToServerNetworkManager()
{
return this.networkConnection;
}
public void currentConnection(NetworkManager networkManager, boolean shouldChange)
{
if (shouldChange) this.networkConnection = networkManager;
}
}

View File

@ -26,6 +26,7 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
import net.minecraft.world.storage.SaveHandler;
@ -490,4 +491,9 @@ public class FMLCommonHandler
public void bootstrap()
{
}
public NetworkManager getClientToServerNetworkManager()
{
return sidedDelegate.getClientToServerNetworkManager();
}
}

View File

@ -494,7 +494,6 @@ public class FMLModContainer implements ModContainer
Method factoryMethod = gatherAnnotations(clazz);
modInstance = getLanguageAdapter().getNewInstance(this,clazz, modClassLoader, factoryMethod);
isNetworkMod = FMLNetworkHandler.instance().registerNetworkMod(this, clazz, event.getASMHarvestedData());
if (fingerprintNotPresent)
{
eventBus.post(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints), expectedFingerprint));

View File

@ -15,6 +15,7 @@ package cpw.mods.fml.common;
import java.util.List;
import net.minecraft.entity.Entity;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer;
import cpw.mods.fml.common.network.packet.EntitySpawnAdjustmentPacket;
import cpw.mods.fml.common.network.packet.EntitySpawnPacket;
@ -53,4 +54,6 @@ public interface IFMLSidedHandler
String getCurrentLanguage();
void serverStopped();
NetworkManager getClientToServerNetworkManager();
}

View File

@ -564,9 +564,9 @@ public class Loader
return "";
}
StringBuilder ret = new StringBuilder();
List<String> branding = FMLCommonHandler.instance().getBrandings();
List<String> branding = FMLCommonHandler.instance().getBrandings(false);
Joiner.on(' ').skipNulls().appendTo(ret, branding.subList(1, branding.size()));
Joiner.on(' ').skipNulls().appendTo(ret, branding);
if (modController != null)
{
modController.printModStates(ret);

View File

@ -141,6 +141,4 @@ public interface ModContainer
public Class<?> getCustomResourcePackClass();
Map<String, String> getSharedModDescriptor();
NetworkModHolder getNetworkModHolder();
}

View File

@ -7,8 +7,10 @@ import gnu.trove.map.hash.TObjectByteHashMap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.handler.codec.MessageToMessageCodec;
@Sharable
public abstract class FMLIndexedMessageToMessageCodec<A> extends MessageToMessageCodec<FMLProxyPacket, A> {
private TByteObjectHashMap<Class<? extends A>> discriminators = new TByteObjectHashMap<Class<? extends A>>();
private TObjectByteHashMap<Class<? extends A>> types = new TObjectByteHashMap<Class<? extends A>>();

View File

@ -0,0 +1,42 @@
package cpw.mods.fml.common.network;
import java.util.List;
import cpw.mods.fml.common.network.NetworkRegistry.OutboundTarget;
import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
import cpw.mods.fml.relauncher.Side;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
public class FMLOutboundHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception
{
if (!(msg instanceof FMLProxyPacket))
{
return;
}
OutboundTarget outboundTarget;
Object args = null;
if (ctx.channel().attr(NetworkRegistry.CHANNEL_SOURCE).get() == Side.CLIENT)
{
outboundTarget = OutboundTarget.TOSERVER;
}
else
{
outboundTarget = ctx.channel().attr(NetworkRegistry.FML_MESSAGETARGET).get();
args = ctx.channel().attr(NetworkRegistry.FML_MESSAGETARGETARGS).get();
outboundTarget.validateArgs(args);
}
List<NetworkDispatcher> dispatchers = outboundTarget.selectNetworks(args);
for (NetworkDispatcher dispatcher : dispatchers)
{
dispatcher.sendProxy((FMLProxyPacket) msg);
}
}
}

View File

@ -6,6 +6,8 @@ import io.netty.channel.embedded.EmbeddedChannel;
import java.io.IOException;
import cpw.mods.fml.relauncher.Side;
import net.minecraft.network.INetHandler;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
@ -14,6 +16,7 @@ import net.minecraft.network.play.server.S3FPacketCustomPayload;
public class FMLProxyPacket extends Packet {
final String channel;
private Side target;
private final ByteBuf payload;
private INetHandler netHandler;
@ -25,11 +28,13 @@ public class FMLProxyPacket extends Packet {
public FMLProxyPacket(S3FPacketCustomPayload original)
{
this(original.func_149168_d(), original.func_149169_c());
this.target = Side.CLIENT;
}
public FMLProxyPacket(C17PacketCustomPayload original)
{
this(original.func_149558_e(), original.func_149559_c());
this.target = Side.SERVER;
}
public FMLProxyPacket(ByteBuf payload, String channel)
@ -53,7 +58,7 @@ public class FMLProxyPacket extends Packet {
public void func_148833_a(INetHandler inethandler)
{
this.netHandler = inethandler;
EmbeddedChannel internalChannel = NetworkRegistry.INSTANCE.getChannel(this.channel);
EmbeddedChannel internalChannel = NetworkRegistry.INSTANCE.getChannel(this.channel, this.target);
if (internalChannel != null)
{
internalChannel.writeInbound(this);

View File

@ -12,23 +12,30 @@
package cpw.mods.fml.common.network;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.util.AttributeKey;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.NetworkManager;
import net.minecraft.world.World;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
import cpw.mods.fml.relauncher.Side;
/**
@ -38,7 +45,7 @@ import cpw.mods.fml.relauncher.Side;
public enum NetworkRegistry
{
INSTANCE;
private Map<String,FMLEmbeddedChannel> channels = Maps.newConcurrentMap();
private EnumMap<Side,Map<String,FMLEmbeddedChannel>> channels = Maps.newEnumMap(Side.class);
private Map<ModContainer, IGuiHandler> serverGuiHandlers = Maps.newHashMap();
private Map<ModContainer, IGuiHandler> clientGuiHandlers = Maps.newHashMap();
@ -46,6 +53,7 @@ public enum NetworkRegistry
* Set in the {@link ChannelHandlerContext}
*/
public static final AttributeKey<String> FML_CHANNEL = new AttributeKey<String>("fml:channelName");
public static final AttributeKey<Side> CHANNEL_SOURCE = new AttributeKey<Side>("fml:channelSource");
public static final AttributeKey<OutboundTarget> FML_MESSAGETARGET = new AttributeKey<OutboundTarget>("fml:outboundTarget");
public static final AttributeKey<Object> FML_MESSAGETARGETARGS = new AttributeKey<Object>("fml:outboundTargetArgs");
@ -53,35 +61,200 @@ public enum NetworkRegistry
private NetworkRegistry()
{
channels.put(Side.CLIENT, Maps.<String,FMLEmbeddedChannel>newConcurrentMap());
channels.put(Side.SERVER, Maps.<String,FMLEmbeddedChannel>newConcurrentMap());
}
public class TargetPoint {
public TargetPoint(int dimension, double x, double y, double z, double range)
{
this.x = x;
this.y = y;
this.z = z;
this.range = range;
this.dimension = dimension;
}
public final double x;
public final double y;
public final double z;
public final double range;
public final int dimension;
}
public enum OutboundTarget {
PLAYER, ALL, DIMENSION, ALLAROUNDPOINT;
PLAYER
{
@Override
public void validateArgs(Object args)
{
if (!(args instanceof EntityPlayerMP))
{
throw new RuntimeException("PLAYER target expects a Player arg");
}
}
@Override
public List<NetworkDispatcher> selectNetworks(Object args)
{
EntityPlayerMP player = (EntityPlayerMP) args;
NetworkDispatcher dispatcher = player.field_71135_a.field_147371_a.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
return ImmutableList.of(dispatcher);
}
},
ALL
{
@Override
public void validateArgs(Object args)
{
}
@SuppressWarnings("unchecked")
@Override
public List<NetworkDispatcher> selectNetworks(Object args)
{
ImmutableList.Builder<NetworkDispatcher> builder = ImmutableList.<NetworkDispatcher>builder();
for (EntityPlayerMP player : (List<EntityPlayerMP>)FMLCommonHandler.instance().getMinecraftServerInstance().func_71203_ab().field_72404_b)
{
NetworkDispatcher dispatcher = player.field_71135_a.field_147371_a.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
builder.add(dispatcher);
}
return builder.build();
}
},
DIMENSION
{
@Override
public void validateArgs(Object args)
{
if (!(args instanceof Integer))
{
throw new RuntimeException("DIMENSION expects an integer argument");
}
}
@SuppressWarnings("unchecked")
@Override
public List<NetworkDispatcher> selectNetworks(Object args)
{
int dimension = (Integer)args;
ImmutableList.Builder<NetworkDispatcher> builder = ImmutableList.<NetworkDispatcher>builder();
for (EntityPlayerMP player : (List<EntityPlayerMP>)FMLCommonHandler.instance().getMinecraftServerInstance().func_71203_ab().field_72404_b)
{
if (dimension == player.field_71093_bK)
{
NetworkDispatcher dispatcher = player.field_71135_a.field_147371_a.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
builder.add(dispatcher);
}
}
return builder.build();
}
},
ALLAROUNDPOINT
{
@Override
public void validateArgs(Object args)
{
if (!(args instanceof TargetPoint))
{
throw new RuntimeException("ALLAROUNDPOINT expects a TargetPoint argument");
}
}
@SuppressWarnings("unchecked")
@Override
public List<NetworkDispatcher> selectNetworks(Object args)
{
TargetPoint tp = (TargetPoint)args;
ImmutableList.Builder<NetworkDispatcher> builder = ImmutableList.<NetworkDispatcher>builder();
for (EntityPlayerMP player : (List<EntityPlayerMP>)FMLCommonHandler.instance().getMinecraftServerInstance().func_71203_ab().field_72404_b)
{
if (player.field_71093_bK == tp.dimension)
{
double d4 = tp.x - player.field_70165_t;
double d5 = tp.y - player.field_70163_u;
double d6 = tp.z - player.field_70161_v;
if (d4 * d4 + d5 * d5 + d6 * d6 < tp.range * tp.range)
{
NetworkDispatcher dispatcher = player.field_71135_a.field_147371_a.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
builder.add(dispatcher);
}
}
}
return builder.build();
}
},
TOSERVER
{
@Override
public void validateArgs(Object args)
{
throw new RuntimeException("Cannot set TOSERVER as a target on the server");
}
@Override
public List<NetworkDispatcher> selectNetworks(Object args)
{
NetworkManager clientConnection = FMLCommonHandler.instance().getClientToServerNetworkManager();
return clientConnection == null ? ImmutableList.<NetworkDispatcher>of() : ImmutableList.of(clientConnection.channel().attr(NetworkDispatcher.FML_DISPATCHER).get());
}
};
public abstract void validateArgs(Object args);
public abstract List<NetworkDispatcher> selectNetworks(Object args);
}
static class FMLEmbeddedChannel extends EmbeddedChannel {
private final String channelName;
public FMLEmbeddedChannel(String channelName)
public FMLEmbeddedChannel(String channelName, Side source, ChannelHandler... handlers)
{
super();
this.channelName = channelName;
super(handlers);
this.attr(FML_CHANNEL).set(channelName);
this.attr(CHANNEL_SOURCE).set(source);
this.pipeline().addFirst(new FMLOutboundHandler());
}
}
public EmbeddedChannel newChannel(String name)
/**
* Create a new synchronous message channel pair based on netty.
* There are two channels created : one for each logical side (considered as the source of an outbound message)
* The returned map will contain a value for each logical side, though both will only be working in the
* integrated server case.
*
* The channel expects to read and write using {@link FMLProxyPacket}. All operation is synchronous, as the
* asynchronous behaviour occurs at a lower level in netty.
*
* The first handler in the pipeline is special and should not be removed or moved from the head - it transforms
* packets from the outbound of this pipeline into custom packets, based on the current {@link AttributeKey} value
* {@link NetworkRegistry#FML_MESSAGETARGET} and {@link NetworkRegistry#FML_MESSAGETARGETARGS} set on the channel.
* For the client to server channel (source side : CLIENT) this is fixed as "TOSERVER". For SERVER to CLIENT packets,
* several possible values exist.
*
* Mod Messages should be transformed using a something akin to a {@link MessageToMessageCodec}. FML provides
* a utility codec, {@link FMLIndexedMessageToMessageCodec} that transforms from {@link FMLProxyPacket} to a mod
* message using a message discriminator byte. This is optional, but highly recommended for use.
*
* Note also that the handlers supplied need to be {@link ChannelHandler.Shareable} - they are injected into two
* channels.
*
* @param name
* @param handlers
* @return
*/
public EnumMap<Side,EmbeddedChannel> newChannel(String name, ChannelHandler... handlers)
{
if (channels.containsKey(name) || name.startsWith("MC|") || name.startsWith("\u0001"))
if (channels.containsKey(name) || name.startsWith("MC|") || name.startsWith("\u0001") || name.startsWith("FML"))
{
throw new RuntimeException("That channel is already registered");
}
FMLEmbeddedChannel channel = new FMLEmbeddedChannel(name);
channels.put(name,channel);
return channel;
EnumMap<Side,EmbeddedChannel> result = Maps.newEnumMap(Side.class);
for (Side side : Side.values())
{
FMLEmbeddedChannel channel = new FMLEmbeddedChannel(name, side, handlers);
channels.get(side).put(name,channel);
result.put(side, channel);
}
return result;
}
public EmbeddedChannel getChannel(String name)
public EmbeddedChannel getChannel(String name, Side source)
{
return channels.get(name);
return channels.get(source).get(name);
}
/*
*//**
@ -295,18 +468,10 @@ public enum NetworkRegistry
ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
if (mc == null)
{
FMLLog.log(Level.SEVERE, "Mod %s attempted to register a gui network handler during a construction phase", mc.getModId());
FMLLog.log(Level.SEVERE, "Mod of type %s attempted to register a gui network handler during a construction phase", mod.getClass().getName());
throw new RuntimeException("Invalid attempt to create a GUI during mod construction. Use an EventHandler instead");
}
NetworkModHolder nmh = mc.getNetworkModHolder();
if (nmh == null)
{
FMLLog.log(Level.FINE, "The mod %s needs to be a @NetworkMod to register a Networked Gui Handler", mc.getModId());
}
else
{
serverGuiHandlers.put(mc, handler);
}
serverGuiHandlers.put(mc, handler);
clientGuiHandlers.put(mc, handler);
}
/* void openRemoteGui(ModContainer mc, EntityPlayerMP player, int modGuiId, World world, int x, int y, int z)
@ -335,8 +500,8 @@ public enum NetworkRegistry
FMLCommonHandler.instance().showGuiScreen(handler.getClientGuiElement(modGuiId, player, world, x, y, z));
}
public boolean hasChannel(String channelName)
public boolean hasChannel(String channelName, Side source)
{
return channels.containsKey(channelName);
return channels.get(source).containsKey(channelName);
}
}

View File

@ -1,26 +0,0 @@
package cpw.mods.fml.common.network;
import cpw.mods.fml.relauncher.Side;
public abstract class NetworkSide {
public static final ServerSide SERVER = new ServerSide();
public static final ClientSide CLIENT = new ClientSide();
public final Side side;
NetworkSide(Side side) {
this.side = side;
}
public static final class ServerSide extends NetworkSide {
private ServerSide()
{
super(Side.SERVER);
}
}
public static final class ClientSide extends NetworkSide {
private ClientSide()
{
super(Side.CLIENT);
}
}
}

View File

@ -1,17 +1,15 @@
package cpw.mods.fml.common.network.handshake;
import io.netty.buffer.ByteBuf;
import java.util.List;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.network.NetworkRegistry;
import io.netty.buffer.ByteBuf;
public abstract class FMLHandshakeMessage {
public static class ServerHello extends FMLHandshakeMessage {
private byte serverProtocolVersion;

View File

@ -207,7 +207,7 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> {
handshakeChannel.writeInbound(proxy);
return true;
}
else if (NetworkRegistry.INSTANCE.hasChannel(channelName))
else if (NetworkRegistry.INSTANCE.hasChannel(channelName, Side.CLIENT))
{
FMLProxyPacket proxy = new FMLProxyPacket(msg);
context.fireChannelRead(proxy);
@ -230,7 +230,7 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> {
handshakeChannel.writeInbound(proxy);
return true;
}
else if (NetworkRegistry.INSTANCE.hasChannel(channelName))
else if (NetworkRegistry.INSTANCE.hasChannel(channelName, Side.SERVER))
{
FMLProxyPacket proxy = new FMLProxyPacket(msg);
context.fireChannelRead(proxy);

View File

@ -23,6 +23,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import net.minecraft.entity.Entity;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.StringTranslate;
@ -234,4 +235,9 @@ public class FMLServerHandler implements IFMLSidedHandler
{
// NOOP
}
@Override
public NetworkManager getClientToServerNetworkManager()
{
throw new RuntimeException("Missing");
}
}