diff --git a/src/de/steamwar/bungeecore/comms/PacketIdManager.java b/src/de/steamwar/bungeecore/comms/PacketIdManager.java index 8376d59..94c735b 100644 --- a/src/de/steamwar/bungeecore/comms/PacketIdManager.java +++ b/src/de/steamwar/bungeecore/comms/PacketIdManager.java @@ -30,4 +30,8 @@ public class PacketIdManager { public static final byte INVENTORY_PACKET = 0x10; public static final byte INVENTORY_CALLBACK_PACKET = 0x11; public static final byte INVENTORY_CLOSE_PACKET = 0x12; + + //0x2(X) Server Information System + public static final byte I_AM_A_LOBBY = 0x20; + public static final byte FIGHT_INFO = 0x21; } diff --git a/src/de/steamwar/bungeecore/comms/SpigotHandler.java b/src/de/steamwar/bungeecore/comms/SpigotHandler.java index 5c0f118..574e9a2 100644 --- a/src/de/steamwar/bungeecore/comms/SpigotHandler.java +++ b/src/de/steamwar/bungeecore/comms/SpigotHandler.java @@ -20,8 +20,9 @@ package de.steamwar.bungeecore.comms; import com.google.common.io.ByteArrayDataInput; +import net.md_5.bungee.api.config.ServerInfo; public interface SpigotHandler { - void handle(ByteArrayDataInput byteArrayDataInput); + void handle(ByteArrayDataInput in, ServerInfo info); } diff --git a/src/de/steamwar/bungeecore/comms/SpigotReceiver.java b/src/de/steamwar/bungeecore/comms/SpigotReceiver.java index f87646f..ab6d246 100644 --- a/src/de/steamwar/bungeecore/comms/SpigotReceiver.java +++ b/src/de/steamwar/bungeecore/comms/SpigotReceiver.java @@ -21,10 +21,9 @@ package de.steamwar.bungeecore.comms; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteStreams; -import de.steamwar.bungeecore.comms.handlers.InventoryCallbackHandler; -import de.steamwar.bungeecore.comms.handlers.PrepareSchemHandler; -import de.steamwar.bungeecore.comms.handlers.TablistNameHandler; +import de.steamwar.bungeecore.comms.handlers.*; import de.steamwar.bungeecore.listeners.BasicListener; +import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.event.PluginMessageEvent; import net.md_5.bungee.event.EventHandler; @@ -39,14 +38,14 @@ public class SpigotReceiver extends BasicListener { public void onPluginMessage(PluginMessageEvent event) { if(!event.getTag().equalsIgnoreCase("sw:bridge")) return; - if(!event.getSender().getAddress().getHostName().equals("localhost")){ - event.setCancelled(true); + + event.setCancelled(true); + if(!(event.getSender() instanceof Server)) return; - } + ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); Byte handler = in.readByte(); - handlerMap.get(handler).handle(in); - event.setCancelled(true); + handlerMap.get(handler).handle(in, ((Server) event.getSender()).getInfo()); } public static void registerHandler(Byte id, SpigotHandler handler) { @@ -55,7 +54,9 @@ public class SpigotReceiver extends BasicListener { static { registerHandler(PacketIdManager.INVENTORY_CALLBACK_PACKET, new InventoryCallbackHandler()); - registerHandler(PacketIdManager.TABLIST_NAME, new TablistNameHandler()); + registerHandler(PacketIdManager.TABLIST_NAME, (in, info) -> {}); registerHandler(PacketIdManager.PREPARE_SCHEM, new PrepareSchemHandler()); + registerHandler(PacketIdManager.I_AM_A_LOBBY, new ImALobbyHandler()); + registerHandler(PacketIdManager.FIGHT_INFO, new FightInfoHandler()); } } diff --git a/src/de/steamwar/bungeecore/comms/handlers/FightInfoHandler.java b/src/de/steamwar/bungeecore/comms/handlers/FightInfoHandler.java new file mode 100644 index 0000000..998e53e --- /dev/null +++ b/src/de/steamwar/bungeecore/comms/handlers/FightInfoHandler.java @@ -0,0 +1,60 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore.comms.handlers; + +import com.google.common.io.ByteArrayDataInput; +import de.steamwar.bungeecore.comms.SpigotHandler; +import de.steamwar.bungeecore.comms.packets.FightInfoPacket; +import de.steamwar.bungeecore.listeners.TablistManager; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public class FightInfoHandler implements SpigotHandler { + + private static final Set lobbys = new HashSet<>(); + + public static void addLobby(ServerInfo lobby) { + lobbys.add(lobby); + } + + @Override + public void handle(ByteArrayDataInput in, ServerInfo info) { + FightInfoPacket packet = new FightInfoPacket(in); + packet.setServerName(info.getName()); + + TablistManager.newFightInfo(info, packet); + + Iterator lobbyIt = lobbys.iterator(); + while(lobbyIt.hasNext()) { + ServerInfo lobby = lobbyIt.next(); + Iterator it = lobby.getPlayers().iterator(); + if(!it.hasNext()){ + lobbyIt.remove(); + continue; + } + + packet.send(it.next()); + } + } +} diff --git a/src/de/steamwar/bungeecore/comms/handlers/ImALobbyHandler.java b/src/de/steamwar/bungeecore/comms/handlers/ImALobbyHandler.java new file mode 100644 index 0000000..8d2013d --- /dev/null +++ b/src/de/steamwar/bungeecore/comms/handlers/ImALobbyHandler.java @@ -0,0 +1,32 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore.comms.handlers; + +import com.google.common.io.ByteArrayDataInput; +import de.steamwar.bungeecore.comms.SpigotHandler; +import net.md_5.bungee.api.config.ServerInfo; + +public class ImALobbyHandler implements SpigotHandler { + + @Override + public void handle(ByteArrayDataInput in, ServerInfo info) { + FightInfoHandler.addLobby(info); + } +} diff --git a/src/de/steamwar/bungeecore/comms/handlers/InventoryCallbackHandler.java b/src/de/steamwar/bungeecore/comms/handlers/InventoryCallbackHandler.java index 07d37b7..0fd90a6 100644 --- a/src/de/steamwar/bungeecore/comms/handlers/InventoryCallbackHandler.java +++ b/src/de/steamwar/bungeecore/comms/handlers/InventoryCallbackHandler.java @@ -27,6 +27,7 @@ import de.steamwar.bungeecore.inventory.InvCallback; import de.steamwar.bungeecore.inventory.SWInventory; import de.steamwar.bungeecore.sql.SteamwarUser; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; import java.util.HashMap; import java.util.Map; @@ -36,9 +37,9 @@ public class InventoryCallbackHandler implements SpigotHandler { public static final Map inventoryHashMap = new HashMap<>(); @Override - public void handle(ByteArrayDataInput byteArrayDataInput) { - SteamwarUser owner = SteamwarUser.get(byteArrayDataInput.readInt()); - CallbackType type = CallbackType.valueOf(byteArrayDataInput.readUTF()); + public void handle(ByteArrayDataInput in, ServerInfo info) { + SteamwarUser owner = SteamwarUser.get(in.readInt()); + CallbackType type = CallbackType.valueOf(in.readUTF()); if(!inventoryHashMap.containsKey(owner.getId())) { BungeeCore.send(ProxyServer.getInstance().getPlayer(owner.getUuid()), BungeeCore.CHAT_PREFIX + "§cBitte erneut versuchen. Durch ein Softwareupdate konnte die übliche Aktion nicht durchgeführt werden."); if(type == CallbackType.CLICK) { @@ -47,8 +48,8 @@ public class InventoryCallbackHandler implements SpigotHandler { return; } if(type == CallbackType.CLICK) { - int pos = byteArrayDataInput.readInt(); - InvCallback.ClickType clickType = InvCallback.ClickType.valueOf(byteArrayDataInput.readUTF()); + int pos = in.readInt(); + InvCallback.ClickType clickType = InvCallback.ClickType.valueOf(in.readUTF()); inventoryHashMap.get(owner.getId()).handleCallback(clickType, pos); }else if(type == CallbackType.CLOSE) { if(inventoryHashMap.get(owner.getId()).isNext()) { diff --git a/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java b/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java index 46d1a03..14171c5 100644 --- a/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java +++ b/src/de/steamwar/bungeecore/comms/handlers/PrepareSchemHandler.java @@ -27,14 +27,15 @@ import de.steamwar.bungeecore.comms.SpigotHandler; import de.steamwar.bungeecore.sql.SchematicType; import de.steamwar.bungeecore.sql.SteamwarUser; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; public class PrepareSchemHandler implements SpigotHandler { @Override - public void handle(ByteArrayDataInput byteArrayDataInput) { - ProxiedPlayer player = ProxyServer.getInstance().getPlayer(SteamwarUser.get(byteArrayDataInput.readInt()).getUuid()); - int schematicID = byteArrayDataInput.readInt(); - ArenaMode mode = ArenaMode.getBySchemType(SchematicType.fromDB(byteArrayDataInput.readUTF())); + public void handle(ByteArrayDataInput in, ServerInfo info) { + ProxiedPlayer player = ProxyServer.getInstance().getPlayer(SteamwarUser.get(in.readInt()).getUuid()); + int schematicID = in.readInt(); + ArenaMode mode = ArenaMode.getBySchemType(SchematicType.fromDB(in.readUTF())); BauCommand.stopBauserver(player); SubserverSystem.startTestServer(player, mode, mode.getRandomMap(), 0, schematicID); diff --git a/src/de/steamwar/bungeecore/comms/handlers/TablistNameHandler.java b/src/de/steamwar/bungeecore/comms/handlers/TablistNameHandler.java deleted file mode 100644 index 678d930..0000000 --- a/src/de/steamwar/bungeecore/comms/handlers/TablistNameHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.steamwar.bungeecore.comms.handlers; - -import com.google.common.io.ByteArrayDataInput; -import de.steamwar.bungeecore.Subserver; -import de.steamwar.bungeecore.comms.SpigotHandler; -import de.steamwar.bungeecore.sql.SteamwarUser; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -public class TablistNameHandler implements SpigotHandler { - - @Override - public void handle(ByteArrayDataInput byteArrayDataInput) { - ProxiedPlayer player = ProxyServer.getInstance().getPlayer(SteamwarUser.get(byteArrayDataInput.readInt()).getUuid()); - Subserver subserver = Subserver.getSubserver(player.getServer().getInfo()); - if(subserver == null) - return; - subserver.getTablistNames().put(player, byteArrayDataInput.readUTF()); - } -} diff --git a/src/de/steamwar/bungeecore/comms/packets/FightInfoPacket.java b/src/de/steamwar/bungeecore/comms/packets/FightInfoPacket.java new file mode 100644 index 0000000..34f120a --- /dev/null +++ b/src/de/steamwar/bungeecore/comms/packets/FightInfoPacket.java @@ -0,0 +1,183 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore.comms.packets; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; +import de.steamwar.bungeecore.comms.BungeePacket; +import de.steamwar.bungeecore.comms.PacketIdManager; + +import java.util.ArrayList; +import java.util.List; + +public class FightInfoPacket extends BungeePacket { + + private String serverName; // Name of the Server + private final String gameMode; // GameMode aka Schematictype (if known, else "") + private final String arena; // Name of the arena + private final String blueName; // Name of the blue team, expected to begin with "§a" colorcode + private final String redName; // Name of the red team, expected to begin with "§a" colorcode + private final String fightState; // Fight state (technical term) (if known, else "") + private final int countdown; // Countdown state in seconds (if known, else 0) + private final int blueLeader; // SWUserID of the blue team leader (if known, else 0) + private final int redLeader; // SWUserID of the red team leader (if known, else 0) + private final int blueSchem; // Blue SchemID (if known, else 0) + private final int redSchem; // Red SchemID (if known, else 0) + private final List bluePlayers; // List of Blue SWUserIDs + private final List redPlayers; // List of Red SWUserIDs + private final List spectators; // List of Spectator SWUserIDs + + public FightInfoPacket(String serverName, String gameMode, String arena, String blueName, String redName, String fightState, int countdown, int blueLeader, int redLeader, int blueSchem, int redSchem, List bluePlayers, List redPlayers, List spectators) { + this.serverName = serverName; + this.gameMode = gameMode; + this.arena = arena; + this.blueName = blueName; + this.redName = redName; + this.fightState = fightState; + this.countdown = countdown; + this.blueLeader = blueLeader; + this.redLeader = redLeader; + this.blueSchem = blueSchem; + this.redSchem = redSchem; + this.bluePlayers = bluePlayers; + this.redPlayers = redPlayers; + this.spectators = spectators; + } + + public FightInfoPacket(ByteArrayDataInput in) { + this.serverName = in.readUTF(); + this.gameMode = in.readUTF(); + this.arena = in.readUTF(); + this.blueName = in.readUTF(); + this.redName = in.readUTF(); + this.fightState = in.readUTF(); + this.countdown = in.readInt(); + this.blueLeader = in.readInt(); + this.redLeader = in.readInt(); + this.blueSchem = in.readInt(); + this.redSchem = in.readInt(); + this.bluePlayers = readPlayerList(in); + this.redPlayers = readPlayerList(in); + this.spectators = readPlayerList(in); + } + + @Override + public int getId() { + return PacketIdManager.FIGHT_INFO; + } + + @Override + public void writeVars(ByteArrayDataOutput out) { + out.writeUTF(serverName); + out.writeUTF(gameMode); + out.writeUTF(arena); + out.writeUTF(blueName); + out.writeUTF(redName); + out.writeUTF(fightState); + out.writeInt(countdown); + out.writeInt(blueLeader); + out.writeInt(redLeader); + out.writeInt(blueSchem); + out.writeInt(redSchem); + writePlayerList(out, bluePlayers); + writePlayerList(out, redPlayers); + writePlayerList(out, spectators); + } + + public String getServerName() { + return serverName; + } + + public void setServerName(String serverName) { + this.serverName = serverName; + } + + public String getGameMode() { + return gameMode; + } + + public String getArena() { + return arena; + } + + public String getBlueName() { + return blueName; + } + + public String getRedName() { + return redName; + } + + public String getFightState() { + return fightState; + } + + public int getCountdown() { + return countdown; + } + + public int getBlueLeader() { + return blueLeader; + } + + public int getRedLeader() { + return redLeader; + } + + public int getBlueSchem() { + return blueSchem; + } + + public int getRedSchem() { + return redSchem; + } + + public List getBluePlayers() { + return bluePlayers; + } + + public List getRedPlayers() { + return redPlayers; + } + + public List getSpectators() { + return spectators; + } + + public int playerSize(){ + return bluePlayers.size() + redPlayers.size() + spectators.size(); + } + + private static List readPlayerList(ByteArrayDataInput in) { + int length = in.readInt(); + List players = new ArrayList<>(length); + for(int i = 0; i < length; i++) { + players.add(in.readInt()); + } + return players; + } + + private void writePlayerList(ByteArrayDataOutput out, List players) { + out.writeInt(players.size()); + for(Integer player : players) { + out.writeInt(player); + } + } +} diff --git a/src/de/steamwar/bungeecore/listeners/TablistManager.java b/src/de/steamwar/bungeecore/listeners/TablistManager.java index 7c92736..9a0e5e3 100644 --- a/src/de/steamwar/bungeecore/listeners/TablistManager.java +++ b/src/de/steamwar/bungeecore/listeners/TablistManager.java @@ -26,13 +26,12 @@ import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.Servertype; import de.steamwar.bungeecore.Subserver; +import de.steamwar.bungeecore.comms.packets.FightInfoPacket; import de.steamwar.bungeecore.sql.SteamwarUser; -import de.steamwar.bungeecore.sql.Team; import de.steamwar.bungeecore.sql.UserGroup; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.event.EventHandler; @@ -42,21 +41,39 @@ import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; public class TablistManager extends BasicListener { private static final Map tablists = new HashMap<>(); - private int seconds = 0; - private Icon darkGray; - private Icon gray; + @EventHandler + public synchronized void onJoin(PostLoginEvent e){ + tablists.put(e.getPlayer(), new Tablist(e.getPlayer())); + } + @EventHandler + public synchronized void onLeave(PlayerDisconnectEvent e){ + tablists.remove(e.getPlayer()); + } + + + private static final Map fightInfos = new HashMap<>(); + + public static synchronized void newFightInfo(ServerInfo info, FightInfoPacket packet) { + fightInfos.put(info, packet); + fightInfos.keySet().removeIf(serverInfo -> serverInfo.getPlayers().isEmpty()); + } + + + private static Icon darkGray; + private static Icon gray; + + private int seconds = 0; private int size; - private final TreeMap> playerMap = new TreeMap<>(); + private TablistGroup tablist; public TablistManager(){ - ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), this::updateCustomTablist, 1, 1, TimeUnit.SECONDS); + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), this::updateTablist, 1, 1, TimeUnit.SECONDS); try{ BungeeTabListPlusAPI.createIcon(ImageIO.read(new File("/configs/BungeeTabListPlus/heads/colors/dark_gray.png")), (icon) -> darkGray = icon); BungeeTabListPlusAPI.createIcon(ImageIO.read(new File("/configs/BungeeTabListPlus/heads/colors/gray.png")), (icon) -> gray = icon); @@ -69,61 +86,39 @@ public class TablistManager extends BasicListener { } } - private void calculateSize() { - size = -1; - size += playerMap.size() * 2 + ProxyServer.getInstance().getPlayers().size(); - size = (size + 19) / 20; - if(size > 5) size = 5; - } - - private synchronized void updateCustomTablist(){ + private synchronized void updateTablist(){ //Calculate server-player-map - playerMap.clear(); - for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { - Server pserver = player.getServer(); - if (pserver == null) //Happens temporarily + tablist = new TablistGroup(true, ""); + TablistGroup bau = new TablistGroup(false, "Bau"); + tablist.addSubTablist(bau); + for (ServerInfo server : ProxyServer.getInstance().getServers().values()){ + if(server.getPlayers().isEmpty()) continue; - ServerInfo server = pserver.getInfo(); - String serverName = server.getName(); - Subserver subserver = Subserver.getSubserver(server); - if (subserver != null && subserver.getType() == Servertype.BAUSERVER) { - playerMap.computeIfAbsent("Bau", s -> new ArrayList<>()).add(player); - } else { - playerMap.computeIfAbsent(serverName, s -> new ArrayList<>()).add(player); - } + if(subserver != null && subserver.getType() == Servertype.BAUSERVER) + bau.addSubTablist(new TablistServer(server)); + else if(fightInfos.containsKey(server)) + tablist.addSubTablist(new TablistServer(server, fightInfos.get(server))); + else + tablist.addSubTablist(new TablistServer(server)); } - playerMap.forEach((server, players) -> players.sort((proxiedPlayer, t1) -> proxiedPlayer.getName().compareToIgnoreCase(t1.getName()))); - - //Set size - calculateSize(); - - for(Tablist tablist : tablists.values()) - tablist.refresh(); + size = (int) Math.ceil(tablist.size() / 20.0); + tablists.values().forEach(Tablist::refresh); seconds++; } - @EventHandler - public synchronized void onJoin(PostLoginEvent e){ - tablists.put(e.getPlayer(), new Tablist(e.getPlayer())); - } - - @EventHandler - public synchronized void onLeave(PlayerDisconnectEvent e){ - tablists.remove(e.getPlayer()); - } - private class Tablist extends DefaultCustomTablist { private final ProxiedPlayer player; + private int pos = 0; private Tablist(ProxiedPlayer player){ this.player = player; BungeeTabListPlusAPI.setCustomTabList(player, this); } - private String calcHeader(ProxiedPlayer player){ + private String header(){ int phase = (seconds % 16) / 3; switch(phase){ case 0: @@ -136,7 +131,7 @@ public class TablistManager extends BasicListener { } } - private String getPing(){ + private String ping(){ int ping = player.getPing(); if(ping < 50){ return "§a" + ping; @@ -151,126 +146,163 @@ public class TablistManager extends BasicListener { if (player.getServer() == null) { return; } - ServerInfo currentServer = player.getServer().getInfo(); - setHeader(calcHeader(player)); - // TABLIST_FOOTER=§e{0} {1}§8ms §eSpieler§8: §7{2} - setFooter("§e" + currentServer.getName() + " " + getPing() + "§8ms §eSpieler§8: §7" + ProxyServer.getInstance().getPlayers().size()); - setSize(size, 20); + pos = 0; + setHeader(header()); + setFooter("§e" + player.getServer().getInfo().getName() + " " + ping() + "§8ms §eSpieler§8: §7" + ProxyServer.getInstance().getPlayers().size()); + int currentSize = size > 4 ? tablist.slimSize(player) : size; + setSize(currentSize, 20); - if (size >= 5) { - refreshSlim(currentServer); - return; - } + tablist.print(this, size > 4); - try { - int i = 0; - for (String server : playerMap.navigableKeySet()) { - if (i > 0){ - setSlot(i%20, i/20, darkGray, "", 1000); - i++; - } - if(server.equals("Bau")) - server = Message.parse("TABLIST_BAU", player); - setSlot(i%20, i/20, gray, "§7§l" + server, 1000); - i++; - i = update(currentServer, playerMap.get(server), i); - } - - finish(i); - }catch(IndexOutOfBoundsException | NullPointerException e){ - //Ignore IndexOutOfBoundsException - //Ignore NPE, happens sometimes (only 1s long) when somebody is joining, server switching or disconnecting + while (pos < currentSize*20){ + setSlot(darkGray, "", 1000); } } - private void refreshSlim(ServerInfo currentServer) { - try { - int i = 0; - boolean spacer = true; - for (String server : playerMap.navigableKeySet()) { - if (i > 0 && spacer) { - setSlot(i%20, i/20, darkGray, "", 1000); - i++; - } - spacer = true; - Team team = getTeam(player); - List players = playerMap.get(server) - .stream() - .filter(p -> (p.getServer() != null && p.getServer().getInfo() == currentServer) || SteamwarUser.get(p).getUserGroup() != UserGroup.Member || (team != null && team == getTeam(p))) - .collect(Collectors.toList()); + private void setSlot(Icon icon, String name, int ping){ + setSlot(pos % 20, pos / 20, icon, name, ping); + pos++; + } + } - Subserver subserver = Subserver.getSubserver(player); - if (server.equals("Bau")) { - if (subserver != null && subserver.getType() == Servertype.BAUSERVER) { - players = playerMap.get(server); - } - server = Message.parse("TABLIST_BAU", player); - } - if (subserver != null && subserver.getType() == Servertype.ARENA && playerMap.get(server).size() == 1) { - players = playerMap.get(server); - } + private interface TablistPart { + int size(); + int slimSize(ProxiedPlayer viewer); + String name(); + void print(Tablist viewer, boolean slim); + } - int increment = playerMap.get(server).size() - players.size(); - if (players.isEmpty()) { - server += " §7(" + increment + ")"; - spacer = false; - } else if (increment != 0) { - server += " §7(+" + increment + ")"; - } + private static class TablistGroup implements TablistPart { - setSlot(i%20, i/20, gray, "§7§l" + server, 1000); - i++; - i = update(currentServer, players, i); - } + private final boolean withHeaders; + private final String orderName; + private final List subTablists = new ArrayList<>(); - finish(i); - }catch(IndexOutOfBoundsException | NullPointerException e){ - //Ignore IndexOutOfBoundsException - //Ignore NPE, happens sometimes (only 1s long) when somebody is joining, server switching or disconnecting - } + private TablistGroup(boolean withHeaders, String orderName) { + this.withHeaders = withHeaders; + this.orderName = orderName; } - private int update(ServerInfo currentServer, List players, int i) { - for (ProxiedPlayer p : players){ - boolean sameServer = currentServer == p.getServer().getInfo(); - setSlot(i % 20, i / 20, BungeeTabListPlusAPI.getIconFromPlayer(p), getTablistName(p), (sameServer ? 1 : 500)); - i++; - } - return i; + private void addSubTablist(TablistPart tablist){ + subTablists.add(tablist); + subTablists.sort((t1, t2) -> t1.name().compareToIgnoreCase(t2.name())); } - private void finish(int i) { - while (i < size*20){ - setSlot(i%20, i/20, darkGray, "", 1000); - i++; - } + @Override + public int size() { + int size = subTablists.stream().map(TablistPart::size).reduce(Integer::sum).orElse(0); + if(withHeaders) + size += Math.min(subTablists.size() - 1, 0); + return size; } - private Team getTeam(ProxiedPlayer p) { - Team team = Team.get(SteamwarUser.get(p).getTeam()); - return team.getTeamId() <= 0 ? null : team; + @Override + public int slimSize(ProxiedPlayer viewer) { + int size = subTablists.stream().map(tablist -> tablist.slimSize(viewer)).reduce(Integer::sum).orElse(0); + if(withHeaders) + size += Math.min(subTablists.size() - 1, 0); + return size; } - private String getTablistName(ProxiedPlayer p) { - Subserver server = Subserver.getSubserver(p); - if(server != null){ - String tablistName = server.getTablistNames().get(p); - if(tablistName != null) - return tablistName; + @Override + public String name() { + return orderName; + } + + @Override + public void print(Tablist viewer, boolean slim) { + subTablists.forEach(tablist -> { + if(withHeaders) + viewer.setSlot(gray, "§l" + tablist.name(), 1000); + tablist.print(viewer, slim); + if(withHeaders) + viewer.setSlot(darkGray, "", 1000); + }); + } + } + + private static class TablistServer implements TablistPart { + private static class TablistPlayer { + private final ProxiedPlayer player; + private final String defaultName; + + private TablistPlayer(ProxiedPlayer player, String defaultName) { + this.player = player; + this.defaultName = defaultName; } + } + private final List players = new ArrayList<>(); + private final ServerInfo info; + private final Subserver subserver; - StringBuilder st = new StringBuilder(); - UserGroup group = SteamwarUser.get(p).getUserGroup(); + private TablistServer(ServerInfo info, FightInfoPacket packet){ + this.info = info; + subserver = Subserver.getSubserver(info); + Collection onlinePlayers = info.getPlayers(); + addPlayers(packet.getBlueName().substring(0, 2), packet.getBluePlayers(), onlinePlayers); + addPlayers(packet.getRedName().substring(0, 2), packet.getRedPlayers(), onlinePlayers); + addPlayers("§7", packet.getSpectators(), onlinePlayers); + } - if (group == UserGroup.Member) { - Team team = getTeam(player); - if (team != null && team == getTeam(p)) st.append("§f"); - else st.append("§7"); - } else { - st.append(group.getColorCode()); + private void addPlayers(String prefix, List teamPlayers, Collection onlinePlayers){ + teamPlayers.stream().map(SteamwarUser::get).map( + user -> onlinePlayers.stream().filter(player -> player.getUniqueId().equals(user.getUuid())).findAny() + ).filter(Optional::isPresent).map(Optional::get).sorted( + (p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName()) + ).forEachOrdered(player -> players.add(new TablistPlayer(player, prefix + player.getName()))); + } + + private TablistServer(ServerInfo info) { + this.info = info; + subserver = Subserver.getSubserver(info); + for(ProxiedPlayer player : info.getPlayers()){ + players.add(new TablistPlayer(player, SteamwarUser.get(player.getUniqueId()).getUserGroup().getColorCode() + player.getName())); } + players.sort((tp1, tp2) -> tp1.player.getName().compareToIgnoreCase(tp2.player.getName())); + } - return st.append(p.getName()).toString(); + private boolean displaySlim(ProxiedPlayer viewer, ProxiedPlayer player){ + if(subserver != null && subserver.getType() == Servertype.ARENA && info.getPlayers().size() == 1) + return true; + + SteamwarUser user = SteamwarUser.get(player); + if(user.getUserGroup() != UserGroup.Member) + return true; + + return user.getTeam() != 0 && SteamwarUser.get(viewer).getTeam() == user.getTeam(); + } + + @Override + public int size() { + return 1 + players.size(); + } + + @Override + public int slimSize(ProxiedPlayer viewer) { + if(viewer.getServer().getInfo() == info) + return size(); + + int size = 1; + for(TablistPlayer player : players) + size += displaySlim(viewer, player.player) ? 1 : 0; + return size; + } + + @Override + public String name() { + return info.getName(); + } + + @Override + public void print(Tablist viewer, boolean slim) { + boolean sameServer = viewer.player.getServer().getInfo() == info; + + for(TablistPlayer player : players){ + if(sameServer) + viewer.setSlot(BungeeTabListPlusAPI.getIconFromPlayer(player.player), player.defaultName.startsWith("§7") ? "§f" + player.player.getName() : player.defaultName, 1); + else if(!slim || displaySlim(viewer.player, player.player)) + viewer.setSlot(BungeeTabListPlusAPI.getIconFromPlayer(player.player), player.defaultName, 500); + } } } }