2013-03-08 00:25:48 +00:00
/ *
* Forge Mod Loader
* Copyright ( c ) 2012 - 2013 cpw .
* All rights reserved . This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2 . 1
* which accompanies this distribution , and is available at
* http : //www.gnu.org/licenses/old-licenses/gpl-2.0.html
2013-12-04 05:11:08 +00:00
*
2013-03-08 00:25:48 +00:00
* Contributors :
* cpw - implementation
* /
2012-08-06 13:52:42 +00:00
package cpw.mods.fml.common.network ;
2013-12-06 16:17:40 +00:00
import io.netty.channel.ChannelHandler ;
2013-12-06 08:11:03 +00:00
import io.netty.channel.ChannelHandlerContext ;
import io.netty.channel.embedded.EmbeddedChannel ;
2013-12-06 16:17:40 +00:00
import io.netty.handler.codec.MessageToMessageCodec ;
2013-12-06 08:11:03 +00:00
import io.netty.util.AttributeKey ;
import java.util.EnumMap ;
2012-08-06 13:52:42 +00:00
import java.util.Map ;
2013-12-10 19:31:50 +00:00
import java.util.Set ;
2013-12-16 16:47:48 +00:00
import org.apache.logging.log4j.Level ;
2012-08-06 13:52:42 +00:00
2012-12-09 04:21:03 +00:00
import net.minecraft.entity.player.EntityPlayer ;
import net.minecraft.world.World ;
2012-08-06 13:52:42 +00:00
2013-12-12 00:07:59 +00:00
import com.google.common.collect.ImmutableMap ;
2012-08-06 13:52:42 +00:00
import com.google.common.collect.Maps ;
2012-08-08 04:31:24 +00:00
import cpw.mods.fml.common.FMLCommonHandler ;
2012-08-06 13:52:42 +00:00
import cpw.mods.fml.common.FMLLog ;
2013-12-09 06:22:24 +00:00
import cpw.mods.fml.common.Loader ;
2012-08-06 13:52:42 +00:00
import cpw.mods.fml.common.ModContainer ;
2013-12-06 19:52:33 +00:00
import cpw.mods.fml.common.discovery.ASMDataTable ;
2013-12-12 00:07:59 +00:00
import cpw.mods.fml.common.network.internal.FMLProxyPacket ;
import cpw.mods.fml.common.network.internal.NetworkModHolder ;
2012-12-16 04:21:26 +00:00
import cpw.mods.fml.relauncher.Side ;
2012-08-06 13:52:42 +00:00
2012-09-04 16:37:59 +00:00
/ * *
* @author cpw
*
* /
2013-12-04 05:11:08 +00:00
public enum NetworkRegistry
2012-08-06 13:52:42 +00:00
{
2013-12-04 05:11:08 +00:00
INSTANCE ;
2013-12-06 16:17:40 +00:00
private EnumMap < Side , Map < String , FMLEmbeddedChannel > > channels = Maps . newEnumMap ( Side . class ) ;
2013-12-06 19:52:33 +00:00
private Map < ModContainer , NetworkModHolder > registry = Maps . newHashMap ( ) ;
2012-08-08 04:31:24 +00:00
private Map < ModContainer , IGuiHandler > serverGuiHandlers = Maps . newHashMap ( ) ;
private Map < ModContainer , IGuiHandler > clientGuiHandlers = Maps . newHashMap ( ) ;
2013-12-06 08:11:03 +00:00
2012-08-06 13:52:42 +00:00
/ * *
2013-12-06 08:11:03 +00:00
* Set in the { @link ChannelHandlerContext }
* /
public static final AttributeKey < String > FML_CHANNEL = new AttributeKey < String > ( " fml:channelName " ) ;
2013-12-06 16:17:40 +00:00
public static final AttributeKey < Side > CHANNEL_SOURCE = new AttributeKey < Side > ( " fml:channelSource " ) ;
2013-12-09 06:22:24 +00:00
public static final AttributeKey < ModContainer > MOD_CONTAINER = new AttributeKey < ModContainer > ( " fml:modContainer " ) ;
2013-12-06 08:11:03 +00:00
public static final byte FML_PROTOCOL = 1 ;
private NetworkRegistry ( )
{
2013-12-06 16:17:40 +00:00
channels . put ( Side . CLIENT , Maps . < String , FMLEmbeddedChannel > newConcurrentMap ( ) ) ;
channels . put ( Side . SERVER , Maps . < String , FMLEmbeddedChannel > newConcurrentMap ( ) ) ;
2013-12-06 08:11:03 +00:00
}
2013-12-06 16:17:40 +00:00
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 ;
}
2013-12-06 08:11:03 +00:00
static class FMLEmbeddedChannel extends EmbeddedChannel {
2013-12-06 16:17:40 +00:00
public FMLEmbeddedChannel ( String channelName , Side source , ChannelHandler . . . handlers )
2013-12-09 06:22:24 +00:00
{
this ( Loader . instance ( ) . activeModContainer ( ) , channelName , source , handlers ) ;
}
public FMLEmbeddedChannel ( ModContainer container , String channelName , Side source , ChannelHandler . . . handlers )
2013-12-06 08:11:03 +00:00
{
2013-12-06 16:17:40 +00:00
super ( handlers ) ;
this . attr ( FML_CHANNEL ) . set ( channelName ) ;
this . attr ( CHANNEL_SOURCE ) . set ( source ) ;
2013-12-09 06:22:24 +00:00
this . attr ( MOD_CONTAINER ) . setIfAbsent ( container ) ;
2013-12-11 23:46:25 +00:00
this . pipeline ( ) . addFirst ( " fml:outbound " , new FMLOutboundHandler ( ) ) ;
2013-12-06 08:11:03 +00:00
}
}
2013-12-06 16:17:40 +00:00
/ * *
* 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 )
2013-12-06 08:11:03 +00:00
{
2013-12-06 16:17:40 +00:00
if ( channels . containsKey ( name ) | | name . startsWith ( " MC| " ) | | name . startsWith ( " \ u0001 " ) | | name . startsWith ( " FML " ) )
2013-12-06 08:11:03 +00:00
{
throw new RuntimeException ( " That channel is already registered " ) ;
}
2013-12-06 16:17:40 +00:00
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 ;
2013-12-06 08:11:03 +00:00
}
2013-12-09 06:22:24 +00:00
public EnumMap < Side , EmbeddedChannel > newChannel ( ModContainer container , String name , ChannelHandler . . . handlers )
{
if ( channels . containsKey ( name ) | | name . startsWith ( " MC| " ) | | name . startsWith ( " \ u0001 " ) | | ( name . startsWith ( " FML " ) & & ! ( " FML " . equals ( container . getModId ( ) ) ) ) )
{
throw new RuntimeException ( " That channel is already registered " ) ;
}
EnumMap < Side , EmbeddedChannel > result = Maps . newEnumMap ( Side . class ) ;
for ( Side side : Side . values ( ) )
{
FMLEmbeddedChannel channel = new FMLEmbeddedChannel ( container , name , side , handlers ) ;
channels . get ( side ) . put ( name , channel ) ;
result . put ( side , channel ) ;
}
return result ;
}
2013-12-06 16:17:40 +00:00
public EmbeddedChannel getChannel ( String name , Side source )
2013-12-06 08:11:03 +00:00
{
2013-12-06 16:17:40 +00:00
return channels . get ( source ) . get ( name ) ;
2013-12-06 08:11:03 +00:00
}
/ *
* //**
2012-08-06 13:52:42 +00:00
* Get the packet 250 channel registration string
2013-01-22 02:37:29 +00:00
* @return the { @link Packet250CustomPayload } channel registration string
2013-12-06 08:11:03 +00:00
* //*
2012-08-13 02:23:03 +00:00
byte [ ] getPacketRegistry ( Side side )
2012-08-06 13:52:42 +00:00
{
2012-08-13 02:23:03 +00:00
return Joiner . on ( '\0' ) . join ( Iterables . concat ( Arrays . asList ( " FML " ) , universalPacketHandlers . keySet ( ) , side . isClient ( ) ? clientPacketHandlers . keySet ( ) : serverPacketHandlers . keySet ( ) ) ) . getBytes ( Charsets . UTF_8 ) ;
2012-08-06 13:52:42 +00:00
}
2013-12-06 08:11:03 +00:00
* //**
2012-08-06 13:52:42 +00:00
* Is the specified channel active for the player ?
* @param channel
* @param player
2013-12-06 08:11:03 +00:00
* //*
2012-08-06 13:52:42 +00:00
public boolean isChannelActive ( String channel , Player player )
{
return activeChannels . containsEntry ( player , channel ) ;
}
2013-12-06 08:11:03 +00:00
* //**
2012-08-06 13:52:42 +00:00
* register a channel to a mod
2013-01-22 02:37:29 +00:00
* @param handler the packet handler
* @param channelName the channel name to register it with
2013-12-06 08:11:03 +00:00
* //*
2012-08-06 20:12:50 +00:00
public void registerChannel ( IPacketHandler handler , String channelName )
2012-08-06 13:52:42 +00:00
{
2012-08-17 20:59:48 +00:00
if ( Strings . isNullOrEmpty ( channelName ) | | ( channelName ! = null & & channelName . length ( ) > 16 ) )
{
FMLLog . severe ( " Invalid channel name '%s' : %s " , channelName , Strings . isNullOrEmpty ( channelName ) ? " Channel name is empty " : " Channel name is too long (16 chars is maximum) " ) ;
throw new RuntimeException ( " Channel name is invalid " ) ;
}
2012-08-13 02:23:03 +00:00
universalPacketHandlers . put ( channelName , handler ) ;
}
public void registerChannel ( IPacketHandler handler , String channelName , Side side )
{
2012-08-18 13:28:36 +00:00
if ( side = = null )
{
registerChannel ( handler , channelName ) ;
return ;
}
2012-08-17 20:59:48 +00:00
if ( Strings . isNullOrEmpty ( channelName ) | | ( channelName ! = null & & channelName . length ( ) > 16 ) )
{
FMLLog . severe ( " Invalid channel name '%s' : %s " , channelName , Strings . isNullOrEmpty ( channelName ) ? " Channel name is empty " : " Channel name is too long (16 chars is maximum) " ) ;
throw new RuntimeException ( " Channel name is invalid " ) ;
}
2012-08-13 02:23:03 +00:00
if ( side . isClient ( ) )
{
clientPacketHandlers . put ( channelName , handler ) ;
}
else
{
serverPacketHandlers . put ( channelName , handler ) ;
}
2012-08-06 13:52:42 +00:00
}
2013-12-06 08:11:03 +00:00
* //**
2012-08-06 13:52:42 +00:00
* Activate the channel for the player
* @param player
2013-12-06 08:11:03 +00:00
* //*
2012-08-06 20:12:50 +00:00
void activateChannel ( Player player , String channel )
2012-08-06 13:52:42 +00:00
{
activeChannels . put ( player , channel ) ;
}
2013-12-06 08:11:03 +00:00
* //**
2012-08-06 13:52:42 +00:00
* Deactivate the channel for the player
* @param player
* @param channel
2013-12-06 08:11:03 +00:00
* //*
2012-08-06 20:12:50 +00:00
void deactivateChannel ( Player player , String channel )
2012-08-06 13:52:42 +00:00
{
activeChannels . remove ( player , channel ) ;
}
2013-12-06 08:11:03 +00:00
* //**
2012-08-06 20:12:50 +00:00
* Register a connection handler
*
* @param handler
2013-12-06 08:11:03 +00:00
* //*
2012-08-06 20:12:50 +00:00
public void registerConnectionHandler ( IConnectionHandler handler )
{
connectionHandlers . add ( handler ) ;
}
2013-12-06 08:11:03 +00:00
* //**
2012-09-04 16:37:59 +00:00
* Register a chat listener
* @param listener
2013-12-06 08:11:03 +00:00
* //*
2012-09-04 16:37:59 +00:00
public void registerChatListener ( IChatListener listener )
{
chatListeners . add ( listener ) ;
}
2012-10-20 21:07:59 +00:00
void playerLoggedIn ( EntityPlayerMP player , NetServerHandler netHandler , INetworkManager manager )
2012-08-06 20:12:50 +00:00
{
2012-08-13 15:58:44 +00:00
generateChannelRegistration ( player , netHandler , manager ) ;
2012-08-06 20:12:50 +00:00
for ( IConnectionHandler handler : connectionHandlers )
{
handler . playerLoggedIn ( ( Player ) player , netHandler , manager ) ;
}
}
2012-10-20 21:07:59 +00:00
String connectionReceived ( NetLoginHandler netHandler , INetworkManager manager )
2012-08-08 00:10:36 +00:00
{
for ( IConnectionHandler handler : connectionHandlers )
{
String kick = handler . connectionReceived ( netHandler , manager ) ;
if ( ! Strings . isNullOrEmpty ( kick ) )
{
return kick ;
}
}
return null ;
}
2012-10-20 21:07:59 +00:00
void connectionOpened ( NetHandler netClientHandler , String server , int port , INetworkManager networkManager )
2012-08-08 00:41:37 +00:00
{
for ( IConnectionHandler handler : connectionHandlers )
{
handler . connectionOpened ( netClientHandler , server , port , networkManager ) ;
}
}
2012-10-20 21:07:59 +00:00
void connectionOpened ( NetHandler netClientHandler , MinecraftServer server , INetworkManager networkManager )
2012-08-08 00:41:37 +00:00
{
for ( IConnectionHandler handler : connectionHandlers )
{
handler . connectionOpened ( netClientHandler , server , networkManager ) ;
}
}
2012-10-20 21:07:59 +00:00
void clientLoggedIn ( NetHandler clientHandler , INetworkManager manager , Packet1Login login )
2012-08-08 00:41:37 +00:00
{
2012-08-13 15:58:44 +00:00
generateChannelRegistration ( clientHandler . getPlayer ( ) , clientHandler , manager ) ;
2012-08-08 00:41:37 +00:00
for ( IConnectionHandler handler : connectionHandlers )
{
2012-08-11 15:01:18 +00:00
handler . clientLoggedIn ( clientHandler , manager , login ) ;
2012-08-08 00:41:37 +00:00
}
}
2012-10-20 21:07:59 +00:00
void connectionClosed ( INetworkManager manager , EntityPlayer player )
2012-08-08 00:41:37 +00:00
{
for ( IConnectionHandler handler : connectionHandlers )
{
handler . connectionClosed ( manager ) ;
}
2012-09-15 19:36:26 +00:00
activeChannels . removeAll ( player ) ;
2012-08-08 00:41:37 +00:00
}
2012-08-10 15:12:57 +00:00
2012-10-20 21:07:59 +00:00
void generateChannelRegistration ( EntityPlayer player , NetHandler netHandler , INetworkManager manager )
2012-08-06 20:12:50 +00:00
{
Packet250CustomPayload pkt = new Packet250CustomPayload ( ) ;
pkt . field_73630_a = " REGISTER " ;
2012-08-13 02:23:03 +00:00
pkt . field_73629_c = getPacketRegistry ( player instanceof EntityPlayerMP ? Side . SERVER : Side . CLIENT ) ;
2012-08-06 20:12:50 +00:00
pkt . field_73628_b = pkt . field_73629_c . length ;
manager . func_74429_a ( pkt ) ;
}
2012-10-20 21:07:59 +00:00
void handleCustomPacket ( Packet250CustomPayload packet , INetworkManager network , NetHandler handler )
2012-08-06 20:12:50 +00:00
{
if ( " REGISTER " . equals ( packet . field_73630_a ) )
{
handleRegistrationPacket ( packet , ( Player ) handler . getPlayer ( ) ) ;
}
else if ( " UNREGISTER " . equals ( packet . field_73630_a ) )
{
handleUnregistrationPacket ( packet , ( Player ) handler . getPlayer ( ) ) ;
}
else
{
handlePacket ( packet , network , ( Player ) handler . getPlayer ( ) ) ;
}
}
2012-10-20 21:07:59 +00:00
private void handlePacket ( Packet250CustomPayload packet , INetworkManager network , Player player )
2012-08-06 20:12:50 +00:00
{
2012-08-15 23:54:56 +00:00
String channel = packet . field_73630_a ;
2012-08-27 21:46:08 +00:00
for ( IPacketHandler handler : Iterables . concat ( universalPacketHandlers . get ( channel ) , player instanceof EntityPlayerMP ? serverPacketHandlers . get ( channel ) : clientPacketHandlers . get ( channel ) ) )
2012-08-06 20:12:50 +00:00
{
2012-08-27 21:46:08 +00:00
handler . onPacketData ( network , packet , player ) ;
2012-08-06 20:12:50 +00:00
}
}
private void handleRegistrationPacket ( Packet250CustomPayload packet , Player player )
{
List < String > channels = extractChannelList ( packet ) ;
for ( String channel : channels )
{
activateChannel ( player , channel ) ;
}
}
private void handleUnregistrationPacket ( Packet250CustomPayload packet , Player player )
{
List < String > channels = extractChannelList ( packet ) ;
for ( String channel : channels )
{
deactivateChannel ( player , channel ) ;
}
}
2012-09-16 01:04:56 +00:00
2012-08-06 20:12:50 +00:00
private List < String > extractChannelList ( Packet250CustomPayload packet )
{
String request = new String ( packet . field_73629_c , Charsets . UTF_8 ) ;
List < String > channels = Lists . newArrayList ( Splitter . on ( '\0' ) . split ( request ) ) ;
return channels ;
}
2013-12-06 08:11:03 +00:00
* /
2012-08-10 15:12:57 +00:00
public void registerGuiHandler ( Object mod , IGuiHandler handler )
{
ModContainer mc = FMLCommonHandler . instance ( ) . findContainerFor ( mod ) ;
2012-09-04 01:14:11 +00:00
if ( mc = = null )
{
2013-12-16 16:47:48 +00:00
FMLLog . log ( Level . ERROR , " Mod of type %s attempted to register a gui network handler during a construction phase " , mod . getClass ( ) . getName ( ) ) ;
2013-12-04 05:11:08 +00:00
throw new RuntimeException ( " Invalid attempt to create a GUI during mod construction. Use an EventHandler instead " ) ;
2012-09-04 01:14:11 +00:00
}
2013-12-06 16:17:40 +00:00
serverGuiHandlers . put ( mc , handler ) ;
2012-08-10 15:12:57 +00:00
clientGuiHandlers . put ( mc , handler ) ;
}
2013-12-06 08:11:03 +00:00
/ * void openRemoteGui ( ModContainer mc , EntityPlayerMP player , int modGuiId , World world , int x , int y , int z )
2012-08-08 04:31:24 +00:00
{
2012-08-10 15:12:57 +00:00
IGuiHandler handler = serverGuiHandlers . get ( mc ) ;
2013-12-04 05:11:08 +00:00
NetworkModHolder nmh = mc . getNetworkModHolder ( ) ;
2012-08-10 15:12:57 +00:00
if ( handler ! = null & & nmh ! = null )
2012-08-08 04:31:24 +00:00
{
Container container = ( Container ) handler . getServerGuiElement ( modGuiId , player , world , x , y , z ) ;
if ( container ! = null )
{
player . func_71117_bO ( ) ;
player . func_71128_l ( ) ;
int windowId = player . field_71139_cq ;
2013-12-04 05:11:08 +00:00
Packet pkt = PacketManager . INSTANCE . makeGuiPacket ( windowId , nmh . getNetworkId ( ) , modGuiId , x , y , z ) ;
2012-08-08 04:31:24 +00:00
player . field_71135_a . func_72567_b ( pkt ) ;
2012-08-10 15:12:57 +00:00
player . field_71070_bA = container ;
2012-08-08 04:31:24 +00:00
player . field_71070_bA . field_75152_c = windowId ;
player . field_71070_bA . func_75132_a ( player ) ;
}
}
}
2013-12-06 08:11:03 +00:00
* / void openLocalGui ( ModContainer mc , EntityPlayer player , int modGuiId , World world , int x , int y , int z )
2012-08-08 04:31:24 +00:00
{
2012-08-10 15:12:57 +00:00
IGuiHandler handler = clientGuiHandlers . get ( mc ) ;
2012-08-08 04:31:24 +00:00
FMLCommonHandler . instance ( ) . showGuiScreen ( handler . getClientGuiElement ( modGuiId , player , world , x , y , z ) ) ;
}
2013-12-06 08:11:03 +00:00
2013-12-06 16:17:40 +00:00
public boolean hasChannel ( String channelName , Side source )
2013-12-06 08:11:03 +00:00
{
2013-12-06 16:17:40 +00:00
return channels . get ( source ) . containsKey ( channelName ) ;
2013-12-06 08:11:03 +00:00
}
2013-12-06 19:52:33 +00:00
public void register ( ModContainer fmlModContainer , Class < ? > clazz , String remoteVersionRange , ASMDataTable asmHarvestedData )
{
NetworkModHolder networkModHolder = new NetworkModHolder ( fmlModContainer , clazz , remoteVersionRange , asmHarvestedData ) ;
registry . put ( fmlModContainer , networkModHolder ) ;
}
2013-12-12 00:07:59 +00:00
public Map < ModContainer , NetworkModHolder > registry ( )
2013-12-06 19:52:33 +00:00
{
2013-12-12 00:07:59 +00:00
return ImmutableMap . copyOf ( registry ) ;
2013-12-06 19:52:33 +00:00
}
2013-12-10 19:31:50 +00:00
public Set < String > channelNamesFor ( Side side )
{
return channels . get ( side ) . keySet ( ) ;
}
2012-08-06 13:52:42 +00:00
}