Project164/eaglerbungee/src/main/java/net/md_5/bungee/UserConnection.java

442 lines
13 KiB
Java

package net.md_5.bungee;
import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.internal.PlatformDependent;
import java.beans.ConstructorProperties;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.Level;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PermissionCheckEvent;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.score.Scoreboard;
import net.md_5.bungee.api.tab.TabListHandler;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.eaglercraft.RedirectServerInfo;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PacketWrapper;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.packet.DefinedPacket;
import net.md_5.bungee.protocol.packet.Packet3Chat;
import net.md_5.bungee.protocol.packet.PacketCCSettings;
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
import net.md_5.bungee.protocol.packet.PacketFFKick;
import net.md_5.bungee.util.CaseInsensitiveSet;
public final class UserConnection implements ProxiedPlayer
{
/*========================================================================*/
private final ProxyServer bungee;
private final ChannelWrapper ch;
private final String name;
private final InitialHandler pendingConnection;
/*========================================================================*/
private ServerConnection server;
private Object switchMutex = new Object();
private Collection<ServerInfo> pendingConnects = new HashSet<>();
/*========================================================================*/
private TabListHandler tabList;
private int sentPingId;
private long sentPingTime;
private int ping = 100;
private ServerInfo reconnectServer;
/*========================================================================*/
private Collection<String> groups = new CaseInsensitiveSet();
private Collection<String> permissions = new CaseInsensitiveSet();
/*========================================================================*/
private int serverEntityId;
private PacketCCSettings settings;
private Scoreboard serverSentScoreboard = new Scoreboard();
/*========================================================================*/
private String displayName;
/*========================================================================*/
private int clientEntityId;
private final Map<String, Object> attachment = new WeakHashMap();
public void setServer(ServerConnection server) {
this.server = server;
}
public void setSettings(PacketCCSettings settings){
this.settings = settings;
}
public void setServerEntityId(int serverEntityId) {
this.serverEntityId = serverEntityId;
}
public void setSentPingId(int sentPingId) {
this.sentPingId = sentPingId;
}
public void setSentPingTime(long sentPingTime) {
this.sentPingTime = sentPingTime;
}
public void setPing(int ping) {
this.ping = ping;
}
@Override
public void setReconnectServer(ServerInfo reconnectServer) {
this.reconnectServer = reconnectServer;
}
public void setClientEntityId(int clientEntityId) {
this.clientEntityId = clientEntityId;
}
@Override
public String getName() {
return name;
}
@Override
public InitialHandler getPendingConnection() {
return pendingConnection;
}
@Override
public TabListHandler getTabList() {
return tabList;
}
public Collection<ServerInfo> getPendingConnects() {
return pendingConnects;
}
public Object getSwitchMutex() {
return switchMutex;
}
@Override
public ServerConnection getServer() {
return server;
}
public int getSentPingId() {
return sentPingId;
}
public long getSentPingTime() {
return sentPingTime;
}
@Override
public int getPing() {
return ping;
}
@Override
public ServerInfo getReconnectServer() {
return reconnectServer;
}
public Collection<String> getPermissions() {
return permissions;
}
public int getClientEntityId() {
return clientEntityId;
}
public int getServerEntityId() {
return serverEntityId;
}
public PacketCCSettings getSettings() {
return settings;
}
public Scoreboard getServerSentScoreboard() {
return serverSentScoreboard;
}
@Override
public String getDisplayName() {
return displayName;
}
private final Connection.Unsafe unsafe;
@ConstructorProperties({ "bungee", "ch", "name", "pendingConnection" })
public UserConnection(final ProxyServer bungee, final ChannelWrapper ch, final String name, final InitialHandler pendingConnection) {
this.switchMutex = new Object();
this.pendingConnects = new HashSet<ServerInfo>();
this.ping = 100;
this.groups = (Collection<String>) new CaseInsensitiveSet();
this.permissions = (Collection<String>) new CaseInsensitiveSet();
this.serverSentScoreboard = new Scoreboard();
this.unsafe = new Connection.Unsafe() {
@Override
public void sendPacket(final DefinedPacket packet) {
UserConnection.this.ch.write(packet);
}
};
if (bungee == null) {
throw new NullPointerException("bungee");
}
if (ch == null) {
throw new NullPointerException("ch");
}
if (name == null) {
throw new NullPointerException("name");
}
this.bungee = bungee;
this.ch = ch;
this.name = name;
this.pendingConnection = pendingConnection;
}
public void init()
{
this.displayName = name;
try
{
this.tabList = getPendingConnection().getListener().getTabList().getDeclaredConstructor().newInstance();
} catch ( ReflectiveOperationException ex )
{
throw new RuntimeException( ex );
}
this.tabList.init( this );
Collection<String> g = bungee.getConfigurationAdapter().getGroups( name );
for ( String s : g )
{
addGroups( s );
}
}
@Override
public void setTabList(TabListHandler tabList)
{
tabList.init( this );
this.tabList = tabList;
}
public void sendPacket(final byte[] b) {
this.ch.write(b);
}
@Deprecated
public boolean isActive()
{
return !ch.isClosed();
}
@Override
public void setDisplayName(String name)
{
Preconditions.checkNotNull( name, "displayName" );
Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" );
getTabList().onDisconnect();
displayName = name;
getTabList().onConnect();
}
@Override
public void connect(ServerInfo target)
{
connect( target, false );
}
void sendDimensionSwitch()
{
unsafe().sendPacket( PacketConstants.DIM1_SWITCH );
unsafe().sendPacket( PacketConstants.DIM2_SWITCH );
}
public void connectNow(ServerInfo target)
{
sendDimensionSwitch();
connect( target );
}
public void connect(final ServerInfo info, final boolean retry) {
if(info instanceof RedirectServerInfo) {
sendData("EAG|Reconnect", ((RedirectServerInfo)info).getRedirect().getBytes(StandardCharsets.UTF_8));
return;
}
final ServerConnectEvent event = new ServerConnectEvent(this, info);
if (this.bungee.getPluginManager().callEvent(event).isCancelled()) {
return;
}
final BungeeServerInfo target = (BungeeServerInfo) event.getTarget();
if (this.getServer() != null && this.getServer().getInfo() != null && Objects.equals(this.getServer().getInfo(), target)) {
//this.sendMessage(ChatColor.RED + "Cannot connect to server you are already on!");
return;
}
if (this.pendingConnects.contains(target)) {
this.sendMessage(ChatColor.RED + "Already connecting to this server!");
return;
}
this.pendingConnects.add(target);
final ChannelInitializer initializer = new ChannelInitializer() {
protected void initChannel(final Channel ch) throws Exception {
PipelineUtils.BASE.initChannel(ch);
((HandlerBoss) ch.pipeline().get((Class) HandlerBoss.class)).setHandler(new ServerConnector(UserConnection.this.bungee, UserConnection.this, target));
}
};
final ChannelFutureListener listener = (ChannelFutureListener) new ChannelFutureListener() {
public void operationComplete(final ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
future.channel().close();
UserConnection.this.pendingConnects.remove(target);
final ServerInfo def = ProxyServer.getInstance().getServers().get(UserConnection.this.getPendingConnection().getListener().getFallbackServer());
if ((retry & target != def) && ((UserConnection.this.getServer() == null || UserConnection.this.getServer().getInfo() == null) || def != UserConnection.this.getServer().getInfo())) {
UserConnection.this.sendMessage(UserConnection.this.bungee.getTranslation("fallback_lobby"));
UserConnection.this.connect(def, false);
} else if (UserConnection.this.getServer() == null || UserConnection.this.getServer().getInfo() == null) {
UserConnection.this.disconnect(UserConnection.this.bungee.getTranslation("fallback_kick") + future.cause().getClass().getName());
} else {
UserConnection.this.sendMessage(UserConnection.this.bungee.getTranslation("fallback_kick") + future.cause().getClass().getName());
}
}
}
};
final Bootstrap b = ((Bootstrap) ((Bootstrap) ((Bootstrap) ((Bootstrap) new Bootstrap().channel((Class) NioSocketChannel.class)).group((EventLoopGroup) BungeeCord.getInstance().eventLoops)).handler((ChannelHandler) initializer))
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.valueOf(5000))).remoteAddress((SocketAddress) target.getAddress());
if (!PlatformDependent.isWindows()) {
b.localAddress(this.getPendingConnection().getListener().getHost().getHostString(), 0);
}
b.connect().addListener((GenericFutureListener) listener);
}
@Override
public synchronized void disconnect(String reason)
{
if ( ch.getHandle().isActive() )
{
bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason );
unsafe().sendPacket( new PacketFFKick( reason ) );
ch.close();
if ( server != null )
{
server.disconnect( "Quitting" );
}
}
}
@Override
public void chat(String message)
{
Preconditions.checkState( server != null, "Not connected to server" );
server.getCh().write( new Packet3Chat( message ) );
}
@Override
public void sendMessage(String message)
{
this.unsafe().sendPacket(new Packet3Chat(message));
}
@Override
public void sendMessages(String... messages)
{
for ( String message : messages )
{
sendMessage( message );
}
}
@Override
public void sendData(String channel, byte[] data)
{
unsafe().sendPacket( new PacketFAPluginMessage( channel, data ) );
}
@Override
public InetSocketAddress getAddress()
{
return (InetSocketAddress) ch.getHandle().remoteAddress();
}
@Override
public Collection<String> getGroups()
{
return Collections.unmodifiableCollection( groups );
}
@Override
public void addGroups(String... groups)
{
for ( String group : groups )
{
this.groups.add( group );
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
{
setPermission( permission, true );
}
}
}
@Override
public void removeGroups(String... groups)
{
for ( String group : groups )
{
this.groups.remove( group );
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
{
setPermission( permission, false );
}
}
}
@Override
public boolean hasPermission(String permission)
{
return bungee.getPluginManager().callEvent( new PermissionCheckEvent( this, permission, permissions.contains( permission ) ) ).hasPermission();
}
@Override
public void setPermission(String permission, boolean value)
{
if ( value )
{
permissions.add( permission );
} else
{
permissions.remove( permission );
}
}
@Override
public Map<String, Object> getAttachment() { // fix this (maybe)
return attachment;
}
@Override
public String toString()
{
return name;
}
@Override
public Unsafe unsafe()
{
return unsafe;
}
}