Fix issue #7 by adding Tab List Support. Thanks @ranzdo

This commit is contained in:
Björn Sundahl 2012-10-28 02:05:08 +02:00 committed by md_5
parent e4522c7ab4
commit 2c1d7b34e6
10 changed files with 301 additions and 2 deletions

View File

@ -22,7 +22,12 @@ import net.md_5.bungee.command.CommandMotd;
import net.md_5.bungee.command.CommandSender; import net.md_5.bungee.command.CommandSender;
import net.md_5.bungee.command.CommandServer; import net.md_5.bungee.command.CommandServer;
import net.md_5.bungee.command.ConsoleCommandSender; import net.md_5.bungee.command.ConsoleCommandSender;
import net.md_5.bungee.packet.DefinedPacket;
import net.md_5.bungee.plugin.JavaPluginManager; import net.md_5.bungee.plugin.JavaPluginManager;
import net.md_5.bungee.tablist.GlobalPingTabList;
import net.md_5.bungee.tablist.GlobalTabList;
import net.md_5.bungee.tablist.ServerUnqiueTabList;
import net.md_5.bungee.tablist.TabListHandler;
/** /**
* Main BungeeCord proxy class. * Main BungeeCord proxy class.
@ -66,6 +71,10 @@ public class BungeeCord
* Registered commands. * Registered commands.
*/ */
public Map<String, Command> commandMap = new HashMap<>(); public Map<String, Command> commandMap = new HashMap<>();
/**
* Tab list handler
*/
public TabListHandler tabListHandler;
/** /**
* Plugin manager. * Plugin manager.
*/ */
@ -154,6 +163,19 @@ public class BungeeCord
pluginManager.loadPlugins(); pluginManager.loadPlugins();
switch (config.tabList)
{
case 1:
tabListHandler = new GlobalPingTabList();
break;
case 2:
tabListHandler = new GlobalTabList();
break;
case 3:
tabListHandler = new ServerUnqiueTabList();
break;
}
InetSocketAddress addr = Util.getAddr(config.bindHost); InetSocketAddress addr = Util.getAddr(config.bindHost);
listener = new ListenThread(addr); listener = new ListenThread(addr);
listener.start(); listener.start();
@ -219,4 +241,17 @@ public class BungeeCord
socket.setTrafficClass(0x18); socket.setTrafficClass(0x18);
socket.setTcpNoDelay(true); socket.setTcpNoDelay(true);
} }
/**
* Broadcasts a packet to all clients that is connected to this instance.
*
* @param packet the packet to send
*/
public void broadcast(DefinedPacket packet)
{
for (UserConnection con : connections.values())
{
con.packetQueue.add(packet);
}
}
} }

View File

@ -70,6 +70,13 @@ public class Configuration
* Max players as displayed in list ping. Soft limit. * Max players as displayed in list ping. Soft limit.
*/ */
public int maxPlayers = 1; public int maxPlayers = 1;
/**
* Tab list 1: For a tab list that is global over all server (using their
* minecraft name) and updating their ping frequently 2: Same as 1 but does
* not update their ping frequently, just once. 3: Makes the individual
* servers handle the tab list (server unique)
*/
public int tabList = 1;
/** /**
* Socket timeout. * Socket timeout.
*/ */

View File

@ -9,10 +9,12 @@ import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import net.md_5.bungee.command.CommandSender; import net.md_5.bungee.command.CommandSender;
import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.packet.DefinedPacket;
import net.md_5.bungee.packet.Packet0KeepAlive;
import net.md_5.bungee.packet.Packet1Login; import net.md_5.bungee.packet.Packet1Login;
import net.md_5.bungee.packet.Packet2Handshake; import net.md_5.bungee.packet.Packet2Handshake;
import net.md_5.bungee.packet.Packet3Chat; import net.md_5.bungee.packet.Packet3Chat;
import net.md_5.bungee.packet.Packet9Respawn; import net.md_5.bungee.packet.Packet9Respawn;
import net.md_5.bungee.packet.PacketC9PlayerListItem;
import net.md_5.bungee.packet.PacketFAPluginMessage; import net.md_5.bungee.packet.PacketFAPluginMessage;
import net.md_5.bungee.packet.PacketInputStream; import net.md_5.bungee.packet.PacketInputStream;
import net.md_5.bungee.plugin.ServerConnectEvent; import net.md_5.bungee.plugin.ServerConnectEvent;
@ -29,6 +31,10 @@ public class UserConnection extends GenericConnection implements CommandSender
private int clientEntityId; private int clientEntityId;
private int serverEntityId; private int serverEntityId;
private volatile boolean reconnecting; private volatile boolean reconnecting;
// ping stuff
private int trackingPingId;
private long pingTime;
private int ping;
public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake) public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake)
{ {
@ -36,6 +42,7 @@ public class UserConnection extends GenericConnection implements CommandSender
this.handshake = handshake; this.handshake = handshake;
username = handshake.username; username = handshake.username;
BungeeCord.instance.connections.put(username, this); BungeeCord.instance.connections.put(username, this);
BungeeCord.instance.tabListHandler.onJoin(this);
} }
public void connect(String server) public void connect(String server)
@ -63,6 +70,7 @@ public class UserConnection extends GenericConnection implements CommandSender
private void connect(String name, InetSocketAddress serverAddr) private void connect(String name, InetSocketAddress serverAddr)
{ {
BungeeCord.instance.tabListHandler.onServerChange(this);
try try
{ {
reconnecting = true; reconnecting = true;
@ -115,6 +123,17 @@ public class UserConnection extends GenericConnection implements CommandSender
return socket.getRemoteSocketAddress(); return socket.getRemoteSocketAddress();
} }
public int getPing()
{
return ping;
}
private void setPing(int ping)
{
BungeeCord.instance.tabListHandler.onPingChange(this, ping);
this.ping = ping;
}
private void destroySelf(String reason) private void destroySelf(String reason)
{ {
if (BungeeCord.instance.isRunning) if (BungeeCord.instance.isRunning)
@ -129,6 +148,13 @@ public class UserConnection extends GenericConnection implements CommandSender
} }
} }
@Override
public void disconnect(String reason)
{
BungeeCord.instance.tabListHandler.onDisconnect(this);
super.disconnect(reason);
}
@Override @Override
public void sendMessage(String message) public void sendMessage(String message)
{ {
@ -168,6 +194,12 @@ public class UserConnection extends GenericConnection implements CommandSender
{ {
sendPacket = !BungeeCord.instance.dispatchCommand(message.substring(1), UserConnection.this); sendPacket = !BungeeCord.instance.dispatchCommand(message.substring(1), UserConnection.this);
} }
} else if (id == 0x00)
{
if (trackingPingId == new Packet0KeepAlive(packet).id)
{
setPing((int) (System.currentTimeMillis() - pingTime));
}
} }
EntityMap.rewrite(packet, clientEntityId, serverEntityId); EntityMap.rewrite(packet, clientEntityId, serverEntityId);
@ -213,6 +245,16 @@ public class UserConnection extends GenericConnection implements CommandSender
connect(server); connect(server);
break; break;
} }
} else if (id == 0x00)
{
trackingPingId = new Packet0KeepAlive(packet).id;
pingTime = System.currentTimeMillis();
} else if (id == 0xC9)
{
if (!BungeeCord.instance.tabListHandler.onPacketC9(UserConnection.this, new PacketC9PlayerListItem(packet)))
{
continue;
}
} }
while (!packetQueue.isEmpty()) while (!packetQueue.isEmpty())

View File

@ -0,0 +1,18 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class Packet0KeepAlive extends DefinedPacket
{
public int id;
public Packet0KeepAlive(byte[] buffer)
{
super(0x00, buffer);
id = readInt();
}
}

View File

@ -0,0 +1,30 @@
package net.md_5.bungee.packet;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ToString
@EqualsAndHashCode(callSuper = false)
public class PacketC9PlayerListItem extends DefinedPacket
{
public String username;
public boolean online;
public int ping;
public PacketC9PlayerListItem(byte[] packet)
{
super(0xC9, packet);
username = readUTF();
online = readBoolean();
ping = readShort();
}
public PacketC9PlayerListItem(String username, boolean online, int ping)
{
super(0xC9);
writeUTF(username);
writeBoolean(online);
writeShort(ping);
}
}

View File

@ -15,8 +15,7 @@ public class ServerConnectEvent
*/ */
private final boolean firstTime; private final boolean firstTime;
/** /**
* Message to send just before the change. * Message to send just before the change. null for no message
* null for no message
*/ */
private String message; private String message;
/** /**

View File

@ -0,0 +1,28 @@
package net.md_5.bungee.tablist;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.packet.PacketC9PlayerListItem;
public class GlobalPingTabList extends GlobalTabList
{
public static final int PING_THRESHOLD = 20;
private Map<UserConnection, Integer> lastPings =
Collections.synchronizedMap(new WeakHashMap<UserConnection, Integer>());
@Override
public void onPingChange(final UserConnection con, final int ping)
{
Integer lastPing = lastPings.get(con);
if (lastPing == null || (ping - PING_THRESHOLD > lastPing && ping + PING_THRESHOLD < lastPing))
{
BungeeCord.instance.broadcast(new PacketC9PlayerListItem(con.username, true, ping));
lastPings.put(con, ping);
}
}
}

View File

@ -0,0 +1,52 @@
package net.md_5.bungee.tablist;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.packet.PacketC9PlayerListItem;
public class GlobalTabList implements TabListHandler
{
private Set<UserConnection> sentPings = Collections.synchronizedSet(new HashSet<UserConnection>());
@Override
public void onJoin(UserConnection con)
{
for (UserConnection con2 : BungeeCord.instance.connections.values())
{
con.packetQueue.add(new PacketC9PlayerListItem(con2.username, true, con2.getPing()));
}
}
@Override
public void onServerChange(UserConnection con)
{
}
@Override
public void onPingChange(final UserConnection con, final int ping)
{
if (!sentPings.contains(con))
{
BungeeCord.instance.broadcast(new PacketC9PlayerListItem(con.username, true, con.getPing()));
sentPings.add(con);
}
}
@Override
public void onDisconnect(final UserConnection con)
{
BungeeCord.instance.broadcast(new PacketC9PlayerListItem(con.username, false, 9999));
sentPings.remove(con);
}
@Override
public boolean onPacketC9(UserConnection con, PacketC9PlayerListItem packet)
{
return false;
}
}

View File

@ -0,0 +1,70 @@
package net.md_5.bungee.tablist;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.packet.PacketC9PlayerListItem;
public class ServerUnqiueTabList implements TabListHandler
{
private Map<UserConnection, Set<String>> sentUsernames =
Collections.synchronizedMap(new WeakHashMap<UserConnection, Set<String>>());
@Override
public void onJoin(UserConnection con)
{
}
@Override
public void onServerChange(UserConnection con)
{
Set<String> usernames = sentUsernames.get(con);
synchronized (usernames)
{
if (usernames != null)
{
for (String username : usernames)
{
con.packetQueue.add(new PacketC9PlayerListItem(username, false, 9999));
}
}
usernames.clear();
}
}
@Override
public void onPingChange(UserConnection con, int ping)
{
}
@Override
public void onDisconnect(UserConnection con)
{
}
@Override
public boolean onPacketC9(final UserConnection con, final PacketC9PlayerListItem packet)
{
Set<String> usernames = sentUsernames.get(con);
if (usernames == null)
{
usernames = new LinkedHashSet<>();
sentUsernames.put(con, usernames);
}
if (packet.online)
{
usernames.add(packet.username);
} else
{
usernames.remove(packet.username);
}
return true;
}
}

View File

@ -0,0 +1,18 @@
package net.md_5.bungee.tablist;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.packet.PacketC9PlayerListItem;
public interface TabListHandler
{
public void onJoin(UserConnection con);
public void onServerChange(UserConnection con);
public void onPingChange(UserConnection con, int ping);
public void onDisconnect(UserConnection con);
public boolean onPacketC9(UserConnection con, PacketC9PlayerListItem packet);
}