2016-06-23 03:49:47 +00:00
/ *
* Minecraft Forge
2018-07-01 21:17:28 +00:00
* Copyright ( c ) 2016 - 2018 .
2016-06-23 03:49:47 +00:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2 . 1
* of the License .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
* /
2018-06-21 19:37:32 +00:00
package net.minecraftforge.fml.network ;
2014-07-31 19:31:17 +00:00
2018-09-15 19:44:48 +00:00
import net.minecraft.nbt.NBTTagCompound ;
import net.minecraft.nbt.NBTTagList ;
2018-09-17 00:54:03 +00:00
import net.minecraft.network.NetworkManager ;
import net.minecraft.network.PacketBuffer ;
2018-06-22 04:43:25 +00:00
import net.minecraft.util.ResourceLocation ;
2018-06-22 18:38:53 +00:00
import net.minecraftforge.fml.network.event.EventNetworkChannel ;
2018-06-22 04:43:25 +00:00
import net.minecraftforge.fml.network.simple.SimpleChannel ;
2018-09-15 19:44:48 +00:00
import org.apache.commons.lang3.tuple.Pair ;
2018-06-22 04:43:25 +00:00
import org.apache.logging.log4j.LogManager ;
import org.apache.logging.log4j.Logger ;
import org.apache.logging.log4j.Marker ;
import org.apache.logging.log4j.MarkerManager ;
2018-09-17 00:54:03 +00:00
import java.util.* ;
import java.util.function.BiFunction ;
2018-06-22 04:43:25 +00:00
import java.util.function.Predicate ;
import java.util.function.Supplier ;
2018-09-15 19:44:48 +00:00
import java.util.stream.Collectors ;
2018-06-21 19:37:32 +00:00
2018-09-17 00:54:03 +00:00
/ * *
* The network registry . Tracks channels on behalf of mods .
* /
2018-06-21 19:37:32 +00:00
public class NetworkRegistry
2017-06-19 22:02:18 +00:00
{
2018-06-22 04:43:25 +00:00
private static final Logger LOGGER = LogManager . getLogger ( ) ;
private static final Marker NETREGISTRY = MarkerManager . getMarker ( " NETREGISTRY " ) ;
private static Map < ResourceLocation , NetworkInstance > instances = new HashMap < > ( ) ;
2018-09-17 00:54:03 +00:00
/ * *
* Special value for clientAcceptedVersions and serverAcceptedVersions predicates indicating the other side lacks
* this channel .
* /
@SuppressWarnings ( " RedundantStringConstructorCall " )
public static String ABSENT = new String ( " ABSENT \ uD83E \ uDD14 " ) ;
2018-06-21 19:37:32 +00:00
public static List < String > getNonVanillaNetworkMods ( )
2017-06-19 22:02:18 +00:00
{
2018-09-17 00:54:03 +00:00
return instances . keySet ( ) . stream ( ) . map ( Object : : toString ) . collect ( Collectors . toList ( ) ) ;
2014-07-31 19:31:17 +00:00
}
2018-06-22 04:43:25 +00:00
2018-09-17 00:54:03 +00:00
static boolean acceptsVanillaConnections ( ) {
return instances . isEmpty ( ) ;
}
2018-06-22 04:43:25 +00:00
2018-09-17 00:54:03 +00:00
/ * *
* Create a new { @link SimpleChannel } .
*
* @param name The registry name for this channel . Must be unique
* @param networkProtocolVersion The network protocol version string that will be offered to the remote side { @link ChannelBuilder # networkProtocolVersion ( Supplier ) }
* @param clientAcceptedVersions Called on the client with the networkProtocolVersion string from the server { @link ChannelBuilder # clientAcceptedVersions ( Predicate ) }
* @param serverAcceptedVersions Called on the server with the networkProtocolVersion string from the client { @link ChannelBuilder # serverAcceptedVersions ( Predicate ) }
* @return A new { @link SimpleChannel }
*
* @see ChannelBuilder # newSimpleChannel ( ResourceLocation , Supplier , Predicate , Predicate )
* /
2018-06-22 04:43:25 +00:00
public static SimpleChannel newSimpleChannel ( final ResourceLocation name , Supplier < String > networkProtocolVersion , Predicate < String > clientAcceptedVersions , Predicate < String > serverAcceptedVersions ) {
2018-06-22 18:38:53 +00:00
return new SimpleChannel ( createInstance ( name , networkProtocolVersion , clientAcceptedVersions , serverAcceptedVersions ) ) ;
}
2018-09-17 00:54:03 +00:00
/ * *
* Create a new { @link EventNetworkChannel } .
*
* @param name The registry name for this channel . Must be unique
* @param networkProtocolVersion The network protocol version string that will be offered to the remote side { @link ChannelBuilder # networkProtocolVersion ( Supplier ) }
* @param clientAcceptedVersions Called on the client with the networkProtocolVersion string from the server { @link ChannelBuilder # clientAcceptedVersions ( Predicate ) }
* @param serverAcceptedVersions Called on the server with the networkProtocolVersion string from the client { @link ChannelBuilder # serverAcceptedVersions ( Predicate ) }
* @return A new { @link EventNetworkChannel }
*
* @see ChannelBuilder # newEventChannel ( ResourceLocation , Supplier , Predicate , Predicate )
* /
2018-06-22 18:38:53 +00:00
public static EventNetworkChannel newEventChannel ( final ResourceLocation name , Supplier < String > networkProtocolVersion , Predicate < String > clientAcceptedVersions , Predicate < String > serverAcceptedVersions ) {
return new EventNetworkChannel ( createInstance ( name , networkProtocolVersion , clientAcceptedVersions , serverAcceptedVersions ) ) ;
}
2018-09-17 00:54:03 +00:00
/ * *
* Creates the internal { @link NetworkInstance } that tracks the channel data .
* @param name registry name
* @param networkProtocolVersion The protocol version string
* @param clientAcceptedVersions The client accepted predicate
* @param serverAcceptedVersions The server accepted predicate
* @return The { @link NetworkInstance }
* @throws IllegalArgumentException if the name already exists
* /
2018-06-22 18:38:53 +00:00
private static NetworkInstance createInstance ( ResourceLocation name , Supplier < String > networkProtocolVersion , Predicate < String > clientAcceptedVersions , Predicate < String > serverAcceptedVersions )
{
2018-06-22 04:43:25 +00:00
final NetworkInstance networkInstance = new NetworkInstance ( name , networkProtocolVersion , clientAcceptedVersions , serverAcceptedVersions ) ;
if ( instances . containsKey ( name ) ) {
2018-06-23 02:45:01 +00:00
LOGGER . error ( NETREGISTRY , " NetworkDirection channel {} already registered. " , name ) ;
throw new IllegalArgumentException ( " NetworkDirection Channel { " + name + " } already registered " ) ;
2018-06-22 04:43:25 +00:00
}
instances . put ( name , networkInstance ) ;
2018-06-22 18:38:53 +00:00
return networkInstance ;
2018-06-22 04:43:25 +00:00
}
2018-09-17 00:54:03 +00:00
/ * *
* Find the { @link NetworkInstance } , if possible
*
* @param resourceLocation The network instance to lookup
* @return The { @link Optional } { @link NetworkInstance }
* /
2018-06-22 04:43:25 +00:00
static Optional < NetworkInstance > findTarget ( ResourceLocation resourceLocation )
{
return Optional . ofNullable ( instances . get ( resourceLocation ) ) ;
}
2018-06-23 02:45:01 +00:00
2018-09-17 00:54:03 +00:00
/ * *
* Construct the NBT representation of the channel list , for use during login handshaking
*
* @see FMLHandshakeMessages . S2CModList
* @see FMLHandshakeMessages . C2SModListReply
*
* @return An nbt tag list
* /
2018-09-15 19:44:48 +00:00
static NBTTagList buildChannelVersions ( ) {
return instances . entrySet ( ) . stream ( ) . map ( e - > {
final NBTTagCompound tag = new NBTTagCompound ( ) ;
tag . setString ( " name " , e . getKey ( ) . toString ( ) ) ;
tag . setString ( " version " , e . getValue ( ) . getNetworkProtocolVersion ( ) ) ;
return tag ;
} ) . collect ( Collectors . toCollection ( NBTTagList : : new ) ) ;
}
2018-09-17 00:54:03 +00:00
/ * *
* Validate the channels from the server on the client . Tests the client predicates against the server
* supplied network protocol version .
*
* @param channels An @ { @link NBTTagList } of name - > version pairs for testing
* @return true if all channels accept themselves
* /
2018-09-15 19:44:48 +00:00
static boolean validateClientChannels ( final NBTTagList channels ) {
2018-09-17 00:54:03 +00:00
return validateChannels ( channels , " server " , NetworkInstance : : tryServerVersionOnClient ) ;
}
/ * *
* Validate the channels from the client on the server . Tests the server predicates against the client
* supplied network protocol version .
* @param channels An @ { @link NBTTagList } of name - > version pairs for testing
* @return true if all channels accept themselves
* /
static boolean validateServerChannels ( final NBTTagList channels ) {
return validateChannels ( channels , " client " , NetworkInstance : : tryClientVersionOnServer ) ;
}
/ * *
* Tests if the nbt list matches with the supplied predicate tester
*
* @param channels An @ { @link NBTTagList } of name - > version pairs for testing
* @param originName A label for use in logging ( where the version pairs came from )
* @param testFunction The test function to use for testing
* @return true if all channels accept themselves
* /
private static boolean validateChannels ( final NBTTagList channels , final String originName , BiFunction < NetworkInstance , String , Boolean > testFunction ) {
Map < ResourceLocation , String > incoming = channels . stream ( ) . map ( NBTTagCompound . class : : cast ) . collect ( Collectors . toMap ( tag - > new ResourceLocation ( tag . getString ( " name " ) ) , tag - > tag . getString ( " version " ) ) ) ;
final List < Pair < ResourceLocation , Boolean > > results = instances . values ( ) . stream ( ) .
map ( ni - > {
final String incomingVersion = incoming . getOrDefault ( ni . getChannelName ( ) , ABSENT ) ;
final boolean test = testFunction . apply ( ni , incomingVersion ) ;
LOGGER . debug ( NETREGISTRY , " Channel '{}' : Version test of '{}' from {} : {} " , ni . getChannelName ( ) , incomingVersion , originName , test ? " ACCEPTED " : " REJECTED " ) ;
return Pair . of ( ni . getChannelName ( ) , test ) ;
} ) . filter ( p - > ! p . getRight ( ) ) . collect ( Collectors . toList ( ) ) ;
2018-09-15 19:44:48 +00:00
if ( ! results . isEmpty ( ) ) {
2018-09-17 00:54:03 +00:00
LOGGER . error ( NETREGISTRY , " Channels [{}] rejected their {} side version number " ,
results . stream ( ) . map ( Pair : : getLeft ) . map ( Object : : toString ) . collect ( Collectors . joining ( " , " ) ) ,
originName ) ;
2018-09-15 19:44:48 +00:00
return false ;
}
2018-09-17 00:54:03 +00:00
LOGGER . debug ( NETREGISTRY , " Accepting channel list from {} " , originName ) ;
2018-09-15 19:44:48 +00:00
return true ;
}
2018-09-17 00:54:03 +00:00
/ * *
* Retrieve the { @link LoginPayload } list for dispatch during { @link FMLHandshakeHandler # tickLogin ( NetworkManager ) } handling .
* Dispatches { @link net . minecraftforge . fml . network . NetworkEvent . GatherLoginPayloadsEvent } to each { @link NetworkInstance } .
*
* @return The { @link LoginPayload } list
* /
static List < LoginPayload > gatherLoginPayloads ( ) {
List < LoginPayload > gatheredPayloads = new ArrayList < > ( ) ;
instances . values ( ) . forEach ( ni - > ni . dispatchGatherLogin ( gatheredPayloads ) ) ;
return gatheredPayloads ;
}
2018-09-15 19:44:48 +00:00
2018-09-17 00:54:03 +00:00
/ * *
* Tracks individual outbound messages for dispatch to clients during login handling . Gathered by dispatching
* { @link net . minecraftforge . fml . network . NetworkEvent . GatherLoginPayloadsEvent } during early connection handling .
* /
public static class LoginPayload {
/ * *
* The data for sending
* /
private final PacketBuffer data ;
/ * *
* A channel which will receive a { @link NetworkEvent . LoginPayloadEvent } from the { @link FMLLoginWrapper }
* /
private final ResourceLocation channelName ;
/ * *
* Some context for logging purposes
* /
private final String messageContext ;
public LoginPayload ( final PacketBuffer buffer , final ResourceLocation channelName , final String messageContext ) {
this . data = buffer ;
this . channelName = channelName ;
this . messageContext = messageContext ;
}
public PacketBuffer getData ( ) {
return data ;
}
public ResourceLocation getChannelName ( ) {
return channelName ;
}
public String getMessageContext ( ) {
return messageContext ;
2018-09-15 19:44:48 +00:00
}
}
2018-09-17 00:54:03 +00:00
/ * *
* Builder for constructing network channels using a builder style API .
* /
2018-06-23 02:45:01 +00:00
public static class ChannelBuilder {
private ResourceLocation channelName ;
private Supplier < String > networkProtocolVersion ;
private Predicate < String > clientAcceptedVersions ;
private Predicate < String > serverAcceptedVersions ;
2018-09-17 00:54:03 +00:00
/ * *
* The name of the channel . Must be unique .
* @param channelName The name of the channel
* @return the channel builder
* /
2018-06-23 02:45:01 +00:00
public static ChannelBuilder named ( ResourceLocation channelName )
{
ChannelBuilder builder = new ChannelBuilder ( ) ;
builder . channelName = channelName ;
return builder ;
}
2018-09-17 00:54:03 +00:00
/ * *
* The network protocol string for this channel . This will be gathered during login and sent to
* the remote partner , where it will be tested with against the relevant predicate .
*
* @see # serverAcceptedVersions ( Predicate )
* @see # clientAcceptedVersions ( Predicate )
* @param networkProtocolVersion A supplier of strings for network protocol version testing
* @return the channel builder
* /
2018-06-23 02:45:01 +00:00
public ChannelBuilder networkProtocolVersion ( Supplier < String > networkProtocolVersion )
{
this . networkProtocolVersion = networkProtocolVersion ;
return this ;
}
2018-09-17 00:54:03 +00:00
/ * *
* A predicate run on the client , with the { @link # networkProtocolVersion ( Supplier ) } string from
* the server , or the special value { @link NetworkRegistry # ABSENT } indicating the absence of
* the channel on the remote side .
* @param clientAcceptedVersions A predicate for testing
* @return the channel builder
* /
2018-06-23 02:45:01 +00:00
public ChannelBuilder clientAcceptedVersions ( Predicate < String > clientAcceptedVersions )
{
this . clientAcceptedVersions = clientAcceptedVersions ;
return this ;
}
2018-09-17 00:54:03 +00:00
/ * *
* A predicate run on the server , with the { @link # networkProtocolVersion ( Supplier ) } string from
* the server , or the special value { @link NetworkRegistry # ABSENT } indicating the absence of
* the channel on the remote side .
* @param serverAcceptedVersions A predicate for testing
* @return the channel builder
* /
2018-06-23 02:45:01 +00:00
public ChannelBuilder serverAcceptedVersions ( Predicate < String > serverAcceptedVersions )
{
this . serverAcceptedVersions = serverAcceptedVersions ;
return this ;
}
2018-09-17 00:54:03 +00:00
/ * *
* Create the network instance
* @return the { @link NetworkInstance }
* /
2018-06-23 02:45:01 +00:00
private NetworkInstance createNetworkInstance ( ) {
return createInstance ( channelName , networkProtocolVersion , clientAcceptedVersions , serverAcceptedVersions ) ;
}
2018-09-17 00:54:03 +00:00
/ * *
* Build a new { @link SimpleChannel } with this builder ' s configuration .
*
* @return A new { @link SimpleChannel }
* /
2018-06-23 02:45:01 +00:00
public SimpleChannel simpleChannel ( ) {
return new SimpleChannel ( createNetworkInstance ( ) ) ;
}
2018-09-17 00:54:03 +00:00
/ * *
* Build a new { @link EventNetworkChannel } with this builder ' s configuration .
* @return A new { @link EventNetworkChannel }
* /
2018-06-23 02:45:01 +00:00
public EventNetworkChannel eventNetworkChannel ( ) {
return new EventNetworkChannel ( createNetworkInstance ( ) ) ;
}
}
2014-07-31 19:31:17 +00:00
}