Implement Support for MinecraftForge / FML 1.7.10
Additional implementation help provided by @jk-5 and @bloodmc.
This commit is contained in:
parent
8715c5fd82
commit
cfad2c65d4
@ -1,5 +1,7 @@
|
||||
package net.md_5.bungee.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@ -74,6 +76,27 @@ public class ServerPing
|
||||
private String description;
|
||||
private Favicon favicon;
|
||||
|
||||
@Data
|
||||
public static class ModInfo
|
||||
{
|
||||
|
||||
private String type = "FML";
|
||||
private List<ModItem> modList = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class ModItem
|
||||
{
|
||||
|
||||
private String modid;
|
||||
private String version;
|
||||
}
|
||||
|
||||
// Right now, we don't get the mods from the user, so we just use a stock ModInfo object to
|
||||
// create the server ping. Vanilla clients will ignore this.
|
||||
private final ModInfo modinfo = new ModInfo();
|
||||
|
||||
@Deprecated
|
||||
public ServerPing(Protocol version, Players players, String description, String favicon)
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.md_5.bungee.api.connection;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
@ -153,4 +154,28 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
||||
* @see Title
|
||||
*/
|
||||
void sendTitle(Title title);
|
||||
|
||||
/**
|
||||
* Gets this player's Forge Mod List, if the player has sent this
|
||||
* information during the lifetime of their connection to Bungee. There is
|
||||
* no guarantee that information is available at any time, as it is only
|
||||
* sent during a FML handshake. Therefore, this will only contain
|
||||
* information for a user that has attempted joined a Forge server.
|
||||
* <p>
|
||||
* Consumers of this API should be aware that an empty mod list does
|
||||
* <em>not</em> indicate that a user is not a Forge user, and so should not
|
||||
* use this API to check for this - there is no way to tell this reliably.
|
||||
* </p>
|
||||
* <p>
|
||||
* Calling this when handling a
|
||||
* {@link net.md_5.bungee.api.event.ServerConnectedEvent} may be the best
|
||||
* place to do so as this event occurs after a FML handshake has completed,
|
||||
* if any has occurred.
|
||||
* </p>
|
||||
*
|
||||
* @return A {@link Map} of mods, where the key is the name of the mod, and
|
||||
* the value is the version. Returns an empty list if the FML handshake has
|
||||
* not occurred for this {@link ProxiedPlayer} yet.
|
||||
*/
|
||||
Map<String, String> getModList();
|
||||
}
|
||||
|
@ -31,18 +31,30 @@ public abstract class DefinedPacket
|
||||
return new String( b, Charsets.UTF_8 );
|
||||
}
|
||||
|
||||
public static void writeArrayLegacy(byte[] b, ByteBuf buf)
|
||||
public static void writeArrayLegacy(byte[] b, ByteBuf buf, boolean allowExtended)
|
||||
{
|
||||
// (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit
|
||||
if ( allowExtended )
|
||||
{
|
||||
Preconditions.checkArgument( b.length <= ( Integer.MAX_VALUE & 0x1FFF9A ), "Cannot send array longer than 2097050 (got %s bytes)", b.length );
|
||||
} else
|
||||
{
|
||||
Preconditions.checkArgument( b.length <= Short.MAX_VALUE, "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length );
|
||||
|
||||
buf.writeShort( b.length );
|
||||
}
|
||||
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only)
|
||||
// No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour.
|
||||
writeVarShort( buf, b.length );
|
||||
buf.writeBytes( b );
|
||||
}
|
||||
|
||||
public static byte[] readArrayLegacy(ByteBuf buf)
|
||||
{
|
||||
short len = buf.readShort();
|
||||
Preconditions.checkArgument( len <= Short.MAX_VALUE, "Cannot receive array longer than Short.MAX_VALUE (got %s bytes)", len );
|
||||
// Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only)
|
||||
// No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour.
|
||||
int len = readVarShort( buf );
|
||||
|
||||
// (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit
|
||||
Preconditions.checkArgument( len <= ( Integer.MAX_VALUE & 0x1FFF9A ), "Cannot receive array longer than 2097050 (got %s bytes)", len );
|
||||
|
||||
byte[] ret = new byte[ len ];
|
||||
buf.readBytes( ret );
|
||||
@ -83,6 +95,11 @@ public abstract class DefinedPacket
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input)
|
||||
{
|
||||
return readVarInt( input, 5 );
|
||||
}
|
||||
|
||||
public static int readVarInt(ByteBuf input, int maxBytes)
|
||||
{
|
||||
int out = 0;
|
||||
int bytes = 0;
|
||||
@ -93,7 +110,7 @@ public abstract class DefinedPacket
|
||||
|
||||
out |= ( in & 0x7F ) << ( bytes++ * 7 );
|
||||
|
||||
if ( bytes > 5 )
|
||||
if ( bytes > maxBytes )
|
||||
{
|
||||
throw new RuntimeException( "VarInt too big" );
|
||||
}
|
||||
@ -129,6 +146,33 @@ public abstract class DefinedPacket
|
||||
}
|
||||
}
|
||||
|
||||
public static int readVarShort(ByteBuf buf)
|
||||
{
|
||||
int low = buf.readUnsignedShort();
|
||||
int high = 0;
|
||||
if ( ( low & 0x8000 ) != 0 )
|
||||
{
|
||||
low = low & 0x7FFF;
|
||||
high = buf.readUnsignedByte();
|
||||
}
|
||||
return ( ( high & 0xFF ) << 15 ) | low;
|
||||
}
|
||||
|
||||
public static void writeVarShort(ByteBuf buf, int toWrite)
|
||||
{
|
||||
int low = toWrite & 0x7FFF;
|
||||
int high = ( toWrite & 0x7F8000 ) >> 15;
|
||||
if ( high != 0 )
|
||||
{
|
||||
low = low | 0x8000;
|
||||
}
|
||||
buf.writeShort( low );
|
||||
if ( high != 0 )
|
||||
{
|
||||
buf.writeByte( high );
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeUUID(UUID value, ByteBuf output)
|
||||
{
|
||||
output.writeLong( value.getMostSignificantBits() );
|
||||
|
@ -41,8 +41,8 @@ public class EncryptionRequest extends DefinedPacket
|
||||
writeString( serverId, buf );
|
||||
if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT )
|
||||
{
|
||||
writeArrayLegacy( publicKey, buf );
|
||||
writeArrayLegacy( verifyToken, buf );
|
||||
writeArrayLegacy( publicKey, buf, false );
|
||||
writeArrayLegacy( verifyToken, buf, false );
|
||||
} else
|
||||
{
|
||||
writeArray( publicKey, buf );
|
||||
|
@ -38,8 +38,8 @@ public class EncryptionResponse extends DefinedPacket
|
||||
{
|
||||
if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT )
|
||||
{
|
||||
writeArrayLegacy( sharedSecret, buf );
|
||||
writeArrayLegacy( verifyToken, buf );
|
||||
writeArrayLegacy( sharedSecret, buf, false );
|
||||
writeArrayLegacy( verifyToken, buf, false );
|
||||
} else
|
||||
{
|
||||
writeArray( sharedSecret, buf );
|
||||
|
@ -24,6 +24,11 @@ public class PluginMessage extends DefinedPacket
|
||||
private String tag;
|
||||
private byte[] data;
|
||||
|
||||
/**
|
||||
* Allow this packet to be sent as an "extended" packet.
|
||||
*/
|
||||
private boolean allowExtendedPacket = false;
|
||||
|
||||
@Override
|
||||
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
|
||||
{
|
||||
@ -44,7 +49,7 @@ public class PluginMessage extends DefinedPacket
|
||||
writeString( tag, buf );
|
||||
if ( protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT )
|
||||
{
|
||||
writeArrayLegacy( data, buf );
|
||||
writeArrayLegacy( data, buf, allowExtendedPacket );
|
||||
} else
|
||||
{
|
||||
buf.writeBytes( data );
|
||||
|
@ -69,6 +69,7 @@ import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.command.*;
|
||||
import net.md_5.bungee.conf.YamlConfig;
|
||||
import net.md_5.bungee.forge.ForgeConstants;
|
||||
import net.md_5.bungee.log.LoggingOutputStream;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
@ -229,6 +230,10 @@ public class BungeeCord extends ProxyServer
|
||||
pluginManager.loadPlugins();
|
||||
config.load();
|
||||
|
||||
registerChannel( ForgeConstants.FML_TAG );
|
||||
registerChannel( ForgeConstants.FML_HANDSHAKE_TAG );
|
||||
registerChannel( ForgeConstants.FORGE_REGISTER );
|
||||
|
||||
isRunning = true;
|
||||
|
||||
pluginManager.enablePlugins();
|
||||
@ -538,7 +543,7 @@ public class BungeeCord extends ProxyServer
|
||||
|
||||
public PluginMessage registerChannels()
|
||||
{
|
||||
return new PluginMessage( "REGISTER", Util.format( pluginChannels, "\00" ).getBytes( Charsets.UTF_8 ) );
|
||||
return new PluginMessage( "REGISTER", Util.format( pluginChannels, "\00" ).getBytes( Charsets.UTF_8 ), false );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,7 +110,7 @@ public class BungeeServerInfo implements ServerInfo
|
||||
return true;
|
||||
} else if ( queue )
|
||||
{
|
||||
packetQueue.add( new PluginMessage( channel, data ) );
|
||||
packetQueue.add( new PluginMessage( channel, data, false ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import net.md_5.bungee.protocol.packet.Respawn;
|
||||
import net.md_5.bungee.protocol.packet.ClientStatus;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
import net.md_5.bungee.protocol.packet.Respawn;
|
||||
|
||||
public class PacketConstants
|
||||
{
|
||||
@ -13,6 +13,6 @@ public class PacketConstants
|
||||
public static final PluginMessage FORGE_MOD_REQUEST = new PluginMessage( "FML", new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0, 2
|
||||
} );
|
||||
public static final PluginMessage I_AM_BUNGEE = new PluginMessage( "BungeeCord", new byte[ 0 ] );
|
||||
}, false );
|
||||
public static final PluginMessage I_AM_BUNGEE = new PluginMessage( "BungeeCord", new byte[ 0 ], false );
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ public class ServerConnection implements Server
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean isObsolete;
|
||||
@Getter
|
||||
private final boolean forgeServer = false;
|
||||
|
||||
private final Unsafe unsafe = new Unsafe()
|
||||
{
|
||||
@Override
|
||||
@ -37,7 +40,7 @@ public class ServerConnection implements Server
|
||||
@Override
|
||||
public void sendData(String channel, byte[] data)
|
||||
{
|
||||
unsafe().sendPacket( new PluginMessage( channel, data ) );
|
||||
unsafe().sendPacket( new PluginMessage( channel, data, forgeServer ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,6 +5,8 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
@ -19,6 +21,9 @@ import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.md_5.bungee.connection.CancelSendSignal;
|
||||
import net.md_5.bungee.connection.DownstreamBridge;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.forge.ForgeConstants;
|
||||
import net.md_5.bungee.forge.ForgeServerHandler;
|
||||
import net.md_5.bungee.forge.ForgeUtils;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.PacketHandler;
|
||||
@ -45,6 +50,8 @@ public class ServerConnector extends PacketHandler
|
||||
private final UserConnection user;
|
||||
private final BungeeServerInfo target;
|
||||
private State thisState = State.LOGIN_SUCCESS;
|
||||
@Getter
|
||||
private ForgeServerHandler handshakeHandler;
|
||||
|
||||
private enum State
|
||||
{
|
||||
@ -70,6 +77,7 @@ public class ServerConnector extends PacketHandler
|
||||
{
|
||||
this.ch = channel;
|
||||
|
||||
this.handshakeHandler = new ForgeServerHandler( user, ch, target );
|
||||
Handshake originalHandshake = user.getPendingConnection().getHandshake();
|
||||
Handshake copiedHandshake = new Handshake( originalHandshake.getProtocolVersion(), originalHandshake.getHost(), originalHandshake.getPort(), 2 );
|
||||
|
||||
@ -102,6 +110,10 @@ public class ServerConnector extends PacketHandler
|
||||
Preconditions.checkState( thisState == State.LOGIN_SUCCESS, "Not expecting LOGIN_SUCCESS" );
|
||||
ch.setProtocol( Protocol.GAME );
|
||||
thisState = State.LOGIN;
|
||||
if ( user.getServer() != null && user.getForgeClientHandler().isHandshakeComplete() )
|
||||
{
|
||||
user.getForgeClientHandler().resetHandshake();
|
||||
}
|
||||
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
@ -142,6 +154,11 @@ public class ServerConnector extends PacketHandler
|
||||
ch.write( user.getSettings() );
|
||||
}
|
||||
|
||||
if ( user.getForgeClientHandler().getClientModList() == null && !user.getForgeClientHandler().isHandshakeComplete() ) // Vanilla
|
||||
{
|
||||
user.getForgeClientHandler().setHandshakeComplete();
|
||||
}
|
||||
|
||||
if ( user.getServer() == null )
|
||||
{
|
||||
// Once again, first connection
|
||||
@ -158,12 +175,12 @@ public class ServerConnector extends PacketHandler
|
||||
{
|
||||
MinecraftOutput out = new MinecraftOutput();
|
||||
out.writeStringUTF8WithoutLengthHeaderBecauseDinnerboneStuffedUpTheMCBrandPacket( ProxyServer.getInstance().getName() + " (" + ProxyServer.getInstance().getVersion() + ")" );
|
||||
user.unsafe().sendPacket( new PluginMessage( "MC|Brand", out.toArray() ) );
|
||||
user.unsafe().sendPacket( new PluginMessage( "MC|Brand", out.toArray(), handshakeHandler.isServerForge() ) );
|
||||
} else
|
||||
{
|
||||
ByteBuf brand = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
DefinedPacket.writeString( bungee.getName() + " (" + bungee.getVersion() + ")", brand );
|
||||
user.unsafe().sendPacket( new PluginMessage( "MC|Brand", brand.array().clone() ) );
|
||||
user.unsafe().sendPacket( new PluginMessage( "MC|Brand", brand.array().clone(), handshakeHandler.isServerForge() ) );
|
||||
brand.release();
|
||||
}
|
||||
} else
|
||||
@ -249,6 +266,49 @@ public class ServerConnector extends PacketHandler
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
if ( pluginMessage.getTag().equals( ForgeConstants.FML_REGISTER ) )
|
||||
{
|
||||
Set<String> channels = ForgeUtils.readRegisteredChannels( pluginMessage );
|
||||
boolean isForgeServer = false;
|
||||
for ( String channel : channels )
|
||||
{
|
||||
if ( channel.equals( ForgeConstants.FML_HANDSHAKE_TAG ) )
|
||||
{
|
||||
isForgeServer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isForgeServer && !this.handshakeHandler.isServerForge() )
|
||||
{
|
||||
// We now set the server-side handshake handler for the client to this.
|
||||
handshakeHandler.setServerAsForgeServer();
|
||||
user.setForgeServerHandler( handshakeHandler );
|
||||
}
|
||||
}
|
||||
|
||||
if ( pluginMessage.getTag().equals( ForgeConstants.FML_HANDSHAKE_TAG ) || pluginMessage.getTag().equals( ForgeConstants.FORGE_REGISTER ) )
|
||||
{
|
||||
this.handshakeHandler.handle( pluginMessage );
|
||||
if ( user.getForgeClientHandler().checkUserOutdated() )
|
||||
{
|
||||
ch.close();
|
||||
user.getPendingConnects().remove( target );
|
||||
}
|
||||
|
||||
// We send the message as part of the handler, so don't send it here.
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
} else
|
||||
{
|
||||
// We have to forward these to the user, especially with Forge as stuff might break
|
||||
// This includes any REGISTER messages we intercepted earlier.
|
||||
user.unsafe().sendPacket( pluginMessage );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@ -13,6 +14,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -34,6 +36,8 @@ import net.md_5.bungee.api.score.Scoreboard;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.entitymap.EntityMap;
|
||||
import net.md_5.bungee.forge.ForgeClientHandler;
|
||||
import net.md_5.bungee.forge.ForgeServerHandler;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
@ -117,6 +121,13 @@ public final class UserConnection implements ProxiedPlayer
|
||||
private EntityMap entityRewrite;
|
||||
private Locale locale;
|
||||
/*========================================================================*/
|
||||
@Getter
|
||||
@Setter
|
||||
private ForgeClientHandler forgeClientHandler;
|
||||
@Getter
|
||||
@Setter
|
||||
private ForgeServerHandler forgeServerHandler;
|
||||
/*========================================================================*/
|
||||
private final Unsafe unsafe = new Unsafe()
|
||||
{
|
||||
@Override
|
||||
@ -152,6 +163,8 @@ public final class UserConnection implements ProxiedPlayer
|
||||
{
|
||||
addGroups( s );
|
||||
}
|
||||
|
||||
forgeClientHandler = new ForgeClientHandler( this );
|
||||
}
|
||||
|
||||
public void sendPacket(PacketWrapper packet)
|
||||
@ -369,7 +382,7 @@ public final class UserConnection implements ProxiedPlayer
|
||||
@Override
|
||||
public void sendData(String channel, byte[] data)
|
||||
{
|
||||
unsafe().sendPacket( new PluginMessage( channel, data ) );
|
||||
unsafe().sendPacket( new PluginMessage( channel, data, forgeClientHandler.isForgeUser() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -470,6 +483,19 @@ public final class UserConnection implements ProxiedPlayer
|
||||
return ( locale == null && settings != null ) ? locale = Locale.forLanguageTag( settings.getLocale().replaceAll( "_", "-" ) ) : locale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getModList()
|
||||
{
|
||||
if ( forgeClientHandler.getClientModList() == null )
|
||||
{
|
||||
// Return an empty map, rather than a null, if the client hasn't got any mods,
|
||||
// or is yet to complete a handshake.
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
|
||||
return ImmutableMap.copyOf( forgeClientHandler.getClientModList() );
|
||||
}
|
||||
|
||||
private static final String EMPTY_TEXT = ComponentSerializer.toString( new TextComponent( "" ) );
|
||||
|
||||
@Override
|
||||
|
@ -288,9 +288,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Nuuuu Mojang why u do this
|
||||
// unsafe().sendPacket( PacketConstants.I_AM_BUNGEE );
|
||||
// unsafe().sendPacket( PacketConstants.FORGE_MOD_REQUEST );
|
||||
Callback<PreLoginEvent> callback = new Callback<PreLoginEvent>()
|
||||
{
|
||||
|
||||
|
@ -19,6 +19,7 @@ import net.md_5.bungee.protocol.packet.ClientSettings;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.md_5.bungee.forge.ForgeConstants;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
import net.md_5.bungee.protocol.packet.TabCompleteResponse;
|
||||
|
||||
@ -142,6 +143,21 @@ public class UpstreamBridge extends PacketHandler
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
|
||||
// We handle forge handshake messages if forge support is enabled.
|
||||
if ( pluginMessage.getTag().equals( ForgeConstants.FML_HANDSHAKE_TAG ) )
|
||||
{
|
||||
// Let our forge client handler deal with this packet.
|
||||
con.getForgeClientHandler().handle( pluginMessage );
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
|
||||
if ( con.getServer() != null && !con.getServer().isForgeServer() && pluginMessage.getData().length > Short.MAX_VALUE )
|
||||
{
|
||||
// Drop the packet if the server is not a Forge server and the message was > 32kiB (as suggested by @jk-5)
|
||||
// Do this AFTER the mod list, so we get that even if the intial server isn't modded.
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
|
||||
PluginMessageEvent event = new PluginMessageEvent( con, con.getServer(), pluginMessage.getTag(), pluginMessage.getData().clone() );
|
||||
if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
|
||||
{
|
||||
|
@ -0,0 +1,177 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Map;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
/**
|
||||
* Handles the Forge Client data and handshake procedure.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ForgeClientHandler
|
||||
{
|
||||
|
||||
@NonNull
|
||||
private final UserConnection con;
|
||||
|
||||
@Getter
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private boolean forgeOutdated = false;
|
||||
|
||||
/**
|
||||
* The users' mod list.
|
||||
*/
|
||||
@Getter
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private Map<String, String> clientModList = null;
|
||||
|
||||
private final ArrayDeque<PluginMessage> packetQueue = new ArrayDeque<PluginMessage>();
|
||||
|
||||
@NonNull
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private ForgeClientHandshakeState state = ForgeClientHandshakeState.HELLO;
|
||||
|
||||
private PluginMessage serverModList = null;
|
||||
private PluginMessage serverIdList = null;
|
||||
|
||||
/**
|
||||
* Handles the Forge packet.
|
||||
*
|
||||
* @param message The Forge Handshake packet to handle.
|
||||
*/
|
||||
public void handle(PluginMessage message) throws IllegalArgumentException
|
||||
{
|
||||
if ( !message.getTag().equalsIgnoreCase( ForgeConstants.FML_HANDSHAKE_TAG ) )
|
||||
{
|
||||
throw new IllegalArgumentException( "Expecting a Forge Handshake packet." );
|
||||
}
|
||||
|
||||
message.setAllowExtendedPacket( true ); // FML allows extended packets so this must be enabled
|
||||
ForgeClientHandshakeState prevState = state;
|
||||
packetQueue.add( message );
|
||||
state = state.send( message, con );
|
||||
if ( state != prevState ) // state finished, send packets
|
||||
{
|
||||
synchronized ( packetQueue )
|
||||
{
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
ForgeLogger.logClient( ForgeLogger.LogDirection.SENDING, prevState.name(), packetQueue.getFirst() );
|
||||
con.getForgeServerHandler().receive( packetQueue.removeFirst() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a {@link PluginMessage} from ForgeServer to pass to Client.
|
||||
*
|
||||
* @param message The message to being received.
|
||||
*/
|
||||
public void receive(PluginMessage message) throws IllegalArgumentException
|
||||
{
|
||||
state = state.handle( message, con );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the client handshake state to HELLO, and, if we know the handshake
|
||||
* has been completed before, send the reset packet.
|
||||
*/
|
||||
public void resetHandshake()
|
||||
{
|
||||
state = ForgeClientHandshakeState.HELLO;
|
||||
con.unsafe().sendPacket( ForgeConstants.FML_RESET_HANDSHAKE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the server mod list to the client, or stores it for sending later.
|
||||
*
|
||||
* @param modList The {@link PluginMessage} to send to the client containing
|
||||
* the mod list.
|
||||
* @throws IllegalArgumentException Thrown if the {@link PluginMessage} was
|
||||
* not as expected.
|
||||
*/
|
||||
public void setServerModList(PluginMessage modList) throws IllegalArgumentException
|
||||
{
|
||||
if ( !modList.getTag().equalsIgnoreCase( ForgeConstants.FML_HANDSHAKE_TAG ) || modList.getData()[0] != 2 )
|
||||
{
|
||||
throw new IllegalArgumentException( "modList" );
|
||||
}
|
||||
|
||||
this.serverModList = modList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the server ID list to the client, or stores it for sending later.
|
||||
*
|
||||
* @param idList The {@link PluginMessage} to send to the client containing
|
||||
* the ID list.
|
||||
* @throws IllegalArgumentException Thrown if the {@link PluginMessage} was
|
||||
* not as expected.
|
||||
*/
|
||||
public void setServerIdList(PluginMessage idList) throws IllegalArgumentException
|
||||
{
|
||||
if ( !idList.getTag().equalsIgnoreCase( ForgeConstants.FML_HANDSHAKE_TAG ) || idList.getData()[0] != 3 )
|
||||
{
|
||||
throw new IllegalArgumentException( "idList" );
|
||||
}
|
||||
|
||||
this.serverIdList = idList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the handshake is complete.
|
||||
*
|
||||
* @return <code>true</code> if the handshake has been completed.
|
||||
*/
|
||||
public boolean isHandshakeComplete()
|
||||
{
|
||||
return this.state == ForgeClientHandshakeState.DONE;
|
||||
}
|
||||
|
||||
public void setHandshakeComplete()
|
||||
{
|
||||
this.state = ForgeClientHandshakeState.DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we know if the user is a forge user.
|
||||
*
|
||||
* @return <code>true</code> if the user is a forge user.
|
||||
*/
|
||||
public boolean isForgeUser()
|
||||
{
|
||||
return clientModList != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a user is using an outdated FML build, and takes
|
||||
* appropriate action on the User side. This should only be called during a
|
||||
* server connection, by the ServerConnector
|
||||
*
|
||||
* @return <code>true</code> if the user's FML build is outdated, otherwise
|
||||
* <code>false</code>
|
||||
*/
|
||||
public boolean checkUserOutdated()
|
||||
{
|
||||
if ( forgeOutdated )
|
||||
{
|
||||
if ( con.isDimensionChange() )
|
||||
{
|
||||
con.disconnect( BungeeCord.getInstance().getTranslation( "connect_kick_outdated_forge" ) );
|
||||
} else
|
||||
{
|
||||
con.sendMessage( BungeeCord.getInstance().getTranslation( "connect_kick_outdated_forge" ) );
|
||||
}
|
||||
}
|
||||
|
||||
return forgeOutdated;
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import java.util.Map;
|
||||
import net.md_5.bungee.ServerConnector;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.forge.ForgeLogger.LogDirection;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
/**
|
||||
* Handshake sequence manager for the Bungee - Forge Client (Upstream) link.
|
||||
* Modelled after the Forge implementation. See
|
||||
* https://github.com/MinecraftForge/FML/blob/master/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java
|
||||
*/
|
||||
enum ForgeClientHandshakeState implements IForgeClientPacketHandler<ForgeClientHandshakeState>
|
||||
{
|
||||
|
||||
/**
|
||||
* Initiated at the start of a client handshake. This is a special case
|
||||
* where we don't want to use a {@link PluginMessage}, we're just sending
|
||||
* stuff out here.
|
||||
*
|
||||
* Transitions into the HELLO state upon completion.
|
||||
*
|
||||
* Requires: {@link UserConnection}.
|
||||
*/
|
||||
START
|
||||
{
|
||||
@Override
|
||||
public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
|
||||
{
|
||||
ForgeLogger.logClient( LogDirection.RECEIVED, this.name(), message );
|
||||
con.unsafe().sendPacket( message );
|
||||
con.getForgeClientHandler().setState( HELLO );
|
||||
return HELLO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
return HELLO;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Waiting to receive a client HELLO and the mod list. Upon receiving the
|
||||
* mod list, return the mod list of the server.
|
||||
*
|
||||
* We will be stuck in this state if we don't have a forge client. This is
|
||||
* OK.
|
||||
*
|
||||
* Transitions to the WAITINGCACK state upon completion.
|
||||
*
|
||||
* Requires:
|
||||
* {@link PluginMessage}, {@link UserConnection}, {@link ServerConnector}
|
||||
*/
|
||||
HELLO
|
||||
{
|
||||
@Override
|
||||
public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
|
||||
{
|
||||
ForgeLogger.logClient( LogDirection.RECEIVED, this.name(), message );
|
||||
// Server Hello.
|
||||
if ( message.getData()[0] == 0 )
|
||||
{
|
||||
con.unsafe().sendPacket( message );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Client Hello.
|
||||
if ( message.getData()[0] == 1 )
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
// Mod list.
|
||||
if ( message.getData()[0] == 2 )
|
||||
{
|
||||
if ( con.getForgeClientHandler().getClientModList() == null )
|
||||
{
|
||||
// This is the first Forge connection - so get the mods now.
|
||||
// Once we've done it, no point doing it again.
|
||||
Map<String, String> clientModList = ForgeUtils.readModList( message );
|
||||
con.getForgeClientHandler().setClientModList( clientModList );
|
||||
|
||||
// Get the version from the mod list.
|
||||
// TODO: Remove this once Bungee becomes 1.8 only.
|
||||
int buildNumber = ForgeUtils.getFmlBuildNumber( clientModList );
|
||||
|
||||
// If we get 0, we're probably using a testing build, so let it though. Otherwise, check the build number.
|
||||
if ( buildNumber < ForgeConstants.FML_MIN_BUILD_VERSION && buildNumber != 0 )
|
||||
{
|
||||
// Mark the user as an old Forge user. This will then cause any Forge ServerConnectors to cancel any
|
||||
// connections to it.
|
||||
con.getForgeClientHandler().setForgeOutdated( true );
|
||||
}
|
||||
}
|
||||
|
||||
return WAITINGSERVERDATA;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
},
|
||||
WAITINGSERVERDATA
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
|
||||
{
|
||||
ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
|
||||
// Mod list.
|
||||
if ( message.getData()[0] == 2 )
|
||||
{
|
||||
con.unsafe().sendPacket( message );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// ACK
|
||||
return WAITINGSERVERCOMPLETE;
|
||||
}
|
||||
},
|
||||
WAITINGSERVERCOMPLETE
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
|
||||
{
|
||||
ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
|
||||
// Mod ID's.
|
||||
if ( message.getData()[0] == 3 )
|
||||
{
|
||||
con.unsafe().sendPacket( message );
|
||||
return this;
|
||||
}
|
||||
|
||||
con.unsafe().sendPacket( message ); // pass everything else
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Send ACK.
|
||||
return PENDINGCOMPLETE;
|
||||
}
|
||||
},
|
||||
PENDINGCOMPLETE
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Ack.
|
||||
if ( message.getData()[0] == -1 )
|
||||
{
|
||||
ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
|
||||
con.unsafe().sendPacket( message );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Send an ACK
|
||||
return COMPLETE;
|
||||
}
|
||||
},
|
||||
COMPLETE
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Ack.
|
||||
if ( message.getData()[0] == -1 )
|
||||
{
|
||||
ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
|
||||
con.unsafe().sendPacket( message );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
return DONE;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Handshake has been completed. Ignores any future handshake packets.
|
||||
*/
|
||||
DONE
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
|
||||
{
|
||||
ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
public class ForgeConstants
|
||||
{
|
||||
|
||||
// Forge
|
||||
public static final String FORGE_REGISTER = "FORGE";
|
||||
|
||||
// FML
|
||||
public static final String FML_TAG = "FML";
|
||||
public static final String FML_HANDSHAKE_TAG = "FML|HS";
|
||||
public static final String FML_REGISTER = "REGISTER";
|
||||
|
||||
public static final PluginMessage FML_RESET_HANDSHAKE = new PluginMessage( FML_HANDSHAKE_TAG, new byte[]
|
||||
{
|
||||
-2, 0
|
||||
}, false );
|
||||
public static final PluginMessage FML_ACK = new PluginMessage( FML_HANDSHAKE_TAG, new byte[]
|
||||
{
|
||||
-1, 0
|
||||
}, false );
|
||||
public static final PluginMessage FML_START_CLIENT_HANDSHAKE = new PluginMessage( FML_HANDSHAKE_TAG, new byte[]
|
||||
{
|
||||
0, 1
|
||||
}, false );
|
||||
public static final PluginMessage FML_START_SERVER_HANDSHAKE = new PluginMessage( FML_HANDSHAKE_TAG, new byte[]
|
||||
{
|
||||
1, 1
|
||||
}, false );
|
||||
public static final PluginMessage FML_EMPTY_MOD_LIST = new PluginMessage( FML_HANDSHAKE_TAG, new byte[]
|
||||
{
|
||||
2, 0
|
||||
}, false );
|
||||
|
||||
/**
|
||||
* The minimum Forge version required to use Forge features. TODO: When the
|
||||
* FML branch gets pulled, update this number to be the build that includes
|
||||
* the change.
|
||||
*/
|
||||
public static final int FML_MIN_BUILD_VERSION = 1209;
|
||||
|
||||
/**
|
||||
* Regex to use to scrape the version information from a FML handshake.
|
||||
*/
|
||||
public static final Pattern FML_HANDSHAKE_VERSION_REGEX = Pattern.compile( "(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)" );
|
||||
}
|
71
proxy/src/main/java/net/md_5/bungee/forge/ForgeLogger.java
Normal file
71
proxy/src/main/java/net/md_5/bungee/forge/ForgeLogger.java
Normal file
@ -0,0 +1,71 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
class ForgeLogger
|
||||
{
|
||||
|
||||
static void logServer(LogDirection direction, String stateName, PluginMessage message)
|
||||
{
|
||||
String dir = direction == LogDirection.SENDING ? "Server -> Bungee" : "Server <- Bungee";
|
||||
String log = "[" + stateName + " " + dir + "][" + direction.name() + ": " + getNameFromDiscriminator( message.getTag(), message ) + "]";
|
||||
BungeeCord.getInstance().getLogger().log( Level.FINE, log );
|
||||
}
|
||||
|
||||
static void logClient(LogDirection direction, String stateName, PluginMessage message)
|
||||
{
|
||||
String dir = direction == LogDirection.SENDING ? "Client -> Bungee" : "Client <- Bungee";
|
||||
String log = "[" + stateName + " " + dir + "][" + direction.name() + ": " + getNameFromDiscriminator( message.getTag(), message ) + "]";
|
||||
BungeeCord.getInstance().getLogger().log( Level.FINE, log );
|
||||
}
|
||||
|
||||
private static String getNameFromDiscriminator(String channel, PluginMessage message)
|
||||
{
|
||||
byte discrim = message.getData()[0];
|
||||
if ( channel.equals( ForgeConstants.FML_HANDSHAKE_TAG ) )
|
||||
{
|
||||
switch ( discrim )
|
||||
{
|
||||
case -2:
|
||||
return "Reset";
|
||||
case -1:
|
||||
return "HandshakeAck";
|
||||
case 0:
|
||||
return "ServerHello";
|
||||
case 1:
|
||||
return "ClientHello";
|
||||
case 2:
|
||||
return "ModList";
|
||||
case 3:
|
||||
return "ModIdData";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
} else if ( channel.equals( ForgeConstants.FORGE_REGISTER ) )
|
||||
{
|
||||
switch ( discrim )
|
||||
{
|
||||
case 1:
|
||||
return "DimensionRegister";
|
||||
case 2:
|
||||
return "FluidIdMap";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
return "UnknownChannel";
|
||||
}
|
||||
|
||||
public enum LogDirection
|
||||
{
|
||||
|
||||
SENDING,
|
||||
RECEIVED
|
||||
}
|
||||
|
||||
private ForgeLogger()
|
||||
{ // Don't allow instantiations
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.forge.ForgeLogger.LogDirection;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
/**
|
||||
* Contains data about the Forge server, and handles the handshake.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ForgeServerHandler
|
||||
{
|
||||
|
||||
private final UserConnection con;
|
||||
@Getter
|
||||
private final ChannelWrapper ch;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final ServerInfo serverInfo;
|
||||
|
||||
@Getter
|
||||
private ForgeServerHandshakeState state = ForgeServerHandshakeState.START;
|
||||
|
||||
@Getter
|
||||
private boolean serverForge = false;
|
||||
|
||||
private final ArrayDeque<PluginMessage> packetQueue = new ArrayDeque<PluginMessage>();
|
||||
|
||||
/**
|
||||
* Handles any {@link PluginMessage} that contains a FML Handshake or Forge
|
||||
* Register.
|
||||
*
|
||||
* @param message The message to handle.
|
||||
* @throws IllegalArgumentException If the wrong packet is sent down.
|
||||
*/
|
||||
public void handle(PluginMessage message) throws IllegalArgumentException
|
||||
{
|
||||
if ( !message.getTag().equalsIgnoreCase( ForgeConstants.FML_HANDSHAKE_TAG ) && !message.getTag().equalsIgnoreCase( ForgeConstants.FORGE_REGISTER ) )
|
||||
{
|
||||
throw new IllegalArgumentException( "Expecting a Forge REGISTER or FML Handshake packet." );
|
||||
}
|
||||
|
||||
message.setAllowExtendedPacket( true ); // FML allows extended packets so this must be enabled
|
||||
ForgeServerHandshakeState prevState = state;
|
||||
packetQueue.add( message );
|
||||
state = state.send( message, con );
|
||||
if ( state != prevState ) // send packets
|
||||
{
|
||||
synchronized ( packetQueue )
|
||||
{
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
ForgeLogger.logServer( LogDirection.SENDING, prevState.name(), packetQueue.getFirst() );
|
||||
con.getForgeClientHandler().receive( packetQueue.removeFirst() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a {@link PluginMessage} from ForgeClientData to pass to Server.
|
||||
*
|
||||
* @param message The message to being received.
|
||||
*/
|
||||
public void receive(PluginMessage message) throws IllegalArgumentException
|
||||
{
|
||||
state = state.handle( message, ch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags the server as a Forge server. Cannot be used to set a server back
|
||||
* to vanilla (there would be no need)
|
||||
*/
|
||||
public void setServerAsForgeServer()
|
||||
{
|
||||
serverForge = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.forge.ForgeLogger.LogDirection;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
/**
|
||||
* Handshake sequence manager for the Bungee - Forge Server (Downstream/Server
|
||||
* Connector) link. Modelled after the Forge implementation.
|
||||
*/
|
||||
public enum ForgeServerHandshakeState implements IForgeServerPacketHandler<ForgeServerHandshakeState>
|
||||
{
|
||||
|
||||
/**
|
||||
* Start the handshake.
|
||||
*
|
||||
*/
|
||||
START
|
||||
{
|
||||
@Override
|
||||
public ForgeServerHandshakeState handle(PluginMessage message, ChannelWrapper ch)
|
||||
{
|
||||
ForgeLogger.logServer( LogDirection.RECEIVED, this.name(), message );
|
||||
ch.write( message );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Send custom channel registration. Send Hello.
|
||||
return HELLO;
|
||||
}
|
||||
},
|
||||
HELLO
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState handle(PluginMessage message, ChannelWrapper ch)
|
||||
{
|
||||
ForgeLogger.logServer( LogDirection.RECEIVED, this.name(), message );
|
||||
if ( message.getData()[0] == 1 ) // Client Hello
|
||||
{
|
||||
ch.write( message );
|
||||
}
|
||||
|
||||
if ( message.getData()[0] == 2 ) // Client ModList
|
||||
{
|
||||
ch.write( message );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Send Server Mod List.
|
||||
return WAITINGCACK;
|
||||
}
|
||||
},
|
||||
WAITINGCACK
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState handle(PluginMessage message, ChannelWrapper ch)
|
||||
{
|
||||
ForgeLogger.logServer( LogDirection.RECEIVED, this.name(), message );
|
||||
ch.write( message );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
if ( message.getData()[0] == 3 && message.getTag().equals( ForgeConstants.FML_HANDSHAKE_TAG ) )
|
||||
{
|
||||
con.getForgeClientHandler().setServerIdList( message );
|
||||
return this;
|
||||
}
|
||||
|
||||
if ( message.getData()[0] == -1 && message.getTag().equals( ForgeConstants.FML_HANDSHAKE_TAG ) ) // transition to COMPLETE after sending ACK
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
if ( message.getTag().equals( ForgeConstants.FORGE_REGISTER ) ) // wait for Forge channel registration
|
||||
{
|
||||
return COMPLETE;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
},
|
||||
COMPLETE
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState handle(PluginMessage message, ChannelWrapper ch)
|
||||
{
|
||||
// Wait for ACK
|
||||
ForgeLogger.logServer( LogDirection.RECEIVED, this.name(), message );
|
||||
ch.write( message );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
// Send ACK
|
||||
return DONE;
|
||||
}
|
||||
|
||||
},
|
||||
/**
|
||||
* Handshake has been completed. Do not respond to any more handshake
|
||||
* packets.
|
||||
*/
|
||||
DONE
|
||||
{
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState handle(PluginMessage message, ChannelWrapper ch)
|
||||
{
|
||||
// RECEIVE 2 ACKS
|
||||
ForgeLogger.logServer( LogDirection.RECEIVED, this.name(), message );
|
||||
ch.write( message );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeServerHandshakeState send(PluginMessage message, UserConnection con)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
74
proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java
Normal file
74
proxy/src/main/java/net/md_5/bungee/forge/ForgeUtils.java
Normal file
@ -0,0 +1,74 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
public class ForgeUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the registered FML channels from the packet.
|
||||
*
|
||||
* @param pluginMessage The packet representing FMLProxyPacket.
|
||||
* @return The registered channels.
|
||||
*/
|
||||
public static Set<String> readRegisteredChannels(PluginMessage pluginMessage)
|
||||
{
|
||||
String channels = new String( pluginMessage.getData(), Charsets.UTF_8 );
|
||||
String[] split = channels.split( "\0" );
|
||||
Set<String> channelSet = ImmutableSet.copyOf( split );
|
||||
return channelSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the modlist from the packet.
|
||||
*
|
||||
* @param pluginMessage The packet representing FMLProxyPacket.
|
||||
* @return The modlist.
|
||||
*/
|
||||
public static Map<String, String> readModList(PluginMessage pluginMessage)
|
||||
{
|
||||
Map<String, String> modTags = Maps.newHashMap();
|
||||
ByteBuf payload = Unpooled.wrappedBuffer( pluginMessage.getData() );
|
||||
byte discriminator = payload.readByte();
|
||||
if ( discriminator == 2 ) // ModList
|
||||
{
|
||||
ByteBuf buffer = payload.slice();
|
||||
int modCount = DefinedPacket.readVarInt( buffer, 2 );
|
||||
for ( int i = 0; i < modCount; i++ )
|
||||
{
|
||||
modTags.put( DefinedPacket.readString( buffer ), DefinedPacket.readString( buffer ) );
|
||||
}
|
||||
}
|
||||
return modTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the build number of FML from the packet.
|
||||
*
|
||||
* @param modList The modlist, as a Map.
|
||||
* @return The build number, or 0 if it failed.
|
||||
*/
|
||||
public static int getFmlBuildNumber(Map<String, String> modList)
|
||||
{
|
||||
if ( modList.containsKey( "FML" ) )
|
||||
{
|
||||
Matcher matcher = ForgeConstants.FML_HANDSHAKE_VERSION_REGEX.matcher( modList.get( "FML" ) );
|
||||
if ( matcher.find() )
|
||||
{
|
||||
// We know from the regex that we have an int.
|
||||
return Integer.parseInt( matcher.group( 4 ) );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
/**
|
||||
* An interface that defines a Forge Handshake Client packet.
|
||||
*
|
||||
* @param <S> The State to transition to.
|
||||
*/
|
||||
public interface IForgeClientPacketHandler<S>
|
||||
{
|
||||
|
||||
/**
|
||||
* Handles any {@link PluginMessage} packets.
|
||||
*
|
||||
* @param message The {@link PluginMessage} to handle.
|
||||
* @param con The {@link UserConnection} to send packets to.
|
||||
* @return The state to transition to.
|
||||
*/
|
||||
public S handle(PluginMessage message, UserConnection con);
|
||||
|
||||
/**
|
||||
* Sends any {@link PluginMessage} packets.
|
||||
*
|
||||
* @param message The {@link PluginMessage} to send.
|
||||
* @param con The {@link UserConnection} to set data.
|
||||
* @return The state to transition to.
|
||||
*/
|
||||
public S send(PluginMessage message, UserConnection con);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package net.md_5.bungee.forge;
|
||||
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
|
||||
/**
|
||||
* An interface that defines a Forge Handshake Server packet.
|
||||
*
|
||||
* @param <S> The State to transition to.
|
||||
*/
|
||||
public interface IForgeServerPacketHandler<S>
|
||||
{
|
||||
|
||||
/**
|
||||
* Handles any {@link net.md_5.bungee.protocol.packet.PluginMessage}
|
||||
* packets.
|
||||
*
|
||||
* @param message The {@link net.md_5.bungee.protocol.packet.PluginMessage}
|
||||
* to handle.
|
||||
* @param ch The {@link ChannelWrapper} to send packets to.
|
||||
* @return The state to transition to.
|
||||
*/
|
||||
public S handle(PluginMessage message, ChannelWrapper ch);
|
||||
|
||||
/**
|
||||
* Sends any {@link net.md_5.bungee.protocol.packet.PluginMessage} packets.
|
||||
*
|
||||
* @param message The {@link net.md_5.bungee.protocol.packet.PluginMessage}
|
||||
* to send.
|
||||
* @param con The {@link net.md_5.bungee.UserConnection} to send packets to
|
||||
* or read from.
|
||||
* @return The state to transition to.
|
||||
*/
|
||||
public S send(PluginMessage message, UserConnection con);
|
||||
}
|
@ -3,6 +3,7 @@ already_connected=\u00a7cYou are already connected to this server!
|
||||
already_connecting=\u00a7cAlready connecting to this server!
|
||||
command_list=\u00a7a[{0}] \u00a7e({1}): \u00a7r{2}
|
||||
connect_kick=\u00a7cKicked whilst connecting to {0}: {1}
|
||||
connect_kick_outdated_forge=\u00a7cYour version of Forge is outdated. Please update Forge and try again.
|
||||
current_server=\u00a76You are currently connected to {0}.
|
||||
fallback_kick=\u00a7cCould not connect to default or fallback server, please try again later: {0}
|
||||
fallback_lobby=\u00a7cCould not connect to target server, you have been moved to the fallback server.
|
||||
|
Loading…
Reference in New Issue
Block a user