From c2544e44fc96767b068583a80fcca50d04258b7d Mon Sep 17 00:00:00 2001 From: Lixfel Date: Mon, 13 Jun 2022 21:04:24 +0200 Subject: [PATCH 1/9] WIP new tablist Signed-off-by: Lixfel --- src/de/steamwar/bungeecore/BungeeCore.java | 5 +- .../bungeecore/tablist/TablistListener.java | 64 ++++++++ .../bungeecore/tablist/TablistManager.java | 154 ++++++++++++++++++ src/plugin.yml | 3 +- 4 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 src/de/steamwar/bungeecore/tablist/TablistListener.java create mode 100644 src/de/steamwar/bungeecore/tablist/TablistManager.java diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index e15cfc4..dc64f9b 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -26,6 +26,7 @@ import de.steamwar.bungeecore.comms.SpigotReceiver; import de.steamwar.bungeecore.listeners.*; import de.steamwar.bungeecore.listeners.mods.*; import de.steamwar.bungeecore.sql.*; +import de.steamwar.bungeecore.tablist.TablistManager; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; @@ -63,6 +64,7 @@ public class BungeeCore extends Plugin { public static final Map commands = new HashMap<>(); private ErrorLogger errorLogger; + private TablistManager tablistManager; @Override public void onEnable(){ @@ -157,7 +159,7 @@ public class BungeeCore extends Plugin { new EventStarter(); new SessionManager(); new SpigotReceiver(); - new TablistManager(); + tablistManager = new TablistManager(); new SettingsChangedListener(); getProxy().getScheduler().schedule(this, () -> { @@ -185,6 +187,7 @@ public class BungeeCore extends Plugin { } } + tablistManager.disable(); errorLogger.unregister(); Statement.closeAll(); } diff --git a/src/de/steamwar/bungeecore/tablist/TablistListener.java b/src/de/steamwar/bungeecore/tablist/TablistListener.java new file mode 100644 index 0000000..4840405 --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistListener.java @@ -0,0 +1,64 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.PacketWrapper; +import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; +import net.md_5.bungee.protocol.packet.PlayerListItem; + +import java.util.List; + +public class TablistListener extends MessageToMessageDecoder { + + private final ServerConnection connection; + + public TablistListener(ServerConnection connection) { + this.connection = connection; + } + + @Override + protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List out) { + //Team + if(!connection.isObsolete()) { + DefinedPacket packet = packetWrapper.packet; + + if(packet instanceof PlayerListHeaderFooter) { + packetWrapper.trySingleRelease(); + return; + } else if(packet instanceof PlayerListItem) { + PlayerListItem list = (PlayerListItem) packet; + if(list.getAction() == PlayerListItem.Action.UPDATE_LATENCY || list.getAction() == PlayerListItem.Action.ADD_PLAYER) { + for (PlayerListItem.Item item : list.getItems()) { + item.setPing(1); + //item.setUsername("AAA"); + //item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); + } + } + } + } + out.add(packetWrapper); + + + } +} diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java new file mode 100644 index 0000000..69543d0 --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -0,0 +1,154 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.listeners.BasicListener; +import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.netty.PipelineUtils; +import net.md_5.bungee.protocol.PlayerPublicKey; +import net.md_5.bungee.protocol.Property; +import net.md_5.bungee.protocol.packet.PlayerListItem; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +public class TablistManager extends BasicListener { + + private final Map tablists = new HashMap<>(); + + public TablistManager() { + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), this::updateTablist, 1, 1, TimeUnit.SECONDS); + ProxyServer.getInstance().getPlayers().forEach(player -> tablists.put(player, new Tablist(player))); + } + + @EventHandler + public void onJoin(PostLoginEvent event) { + synchronized (tablists) { + tablists.put(event.getPlayer(), new Tablist(event.getPlayer())); + } + } + + @EventHandler + public void onServerConnection(ServerSwitchEvent event) { + ServerConnection server = (ServerConnection) event.getPlayer().getServer(); + server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(server)); + } + + @EventHandler + public void onLeave(PlayerDisconnectEvent event) { + synchronized (tablists) { + tablists.remove(event.getPlayer()); + } + } + + public void disable() { + synchronized (tablists) { + tablists.forEach((player, tablist) -> tablist.remove()); + tablists.clear(); + } + } + + private void updateTablist() { + synchronized (tablists) { + tablists.forEach((player, tablist) -> tablist.update()); + } + } + + private static class Tablist { + private final ProxiedPlayer player; + private PlayerListItem.Item[] shown = new PlayerListItem.Item[0]; + + private Tablist(ProxiedPlayer player) { + this.player = player; + } + + private void update() { + if(player.getServer() == null) + return; + + //TODO header & footer + PlayerListItem removePacket = removePacket(); + + PlayerListItem addPacket = new PlayerListItem(); + addPacket.setAction(PlayerListItem.Action.ADD_PLAYER); + + shown = ProxyServer.getInstance().getServers().entrySet().stream().filter(entry -> entry.getValue() != player.getServer().getInfo()).sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())).flatMap(entry -> Stream.concat(Stream.of(construct("", "gray"), construct("§7§l" + entry.getKey(), "lightgray")), entry.getValue().getPlayers().stream().map(this::construct))).toArray(PlayerListItem.Item[]::new); + for(int i = 0; i < shown.length; i++) { + shown[i].setUsername("»SW« " + i); + } + addPacket.setItems(shown); + + player.unsafe().sendPacket(removePacket); + player.unsafe().sendPacket(addPacket); + } + + private PlayerListItem.Item construct(String displayName, String color) { + UUID uuid = UUID.randomUUID(); + return construct(uuid, null, new Property[]{createSkin(uuid, "»SW«", color)}, null, 1, 1000, displayName); + } + + private Property createSkin(UUID uuid, String username, String color) { + // \"timestamp\":0,\"profileId\":\"" + uuid.toString().replace("-", "") + "\",\"profileName\":\"" + username + "\", + return new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/" + color + ".png\"}}}").getBytes(StandardCharsets.UTF_8))); + } + + private PlayerListItem.Item construct(ProxiedPlayer player) { + return construct(player.getUniqueId(), null, ((InitialHandler)player.getPendingConnection()).getLoginProfile().getProperties(), ((InitialHandler)player.getPendingConnection()).getLoginRequest().getPublicKey(), 1, 1000, player.getDisplayName()); + } + + private PlayerListItem.Item construct(UUID uuid, String username, Property[] properties, PlayerPublicKey publicKey, int gamemode, int ping, String displayName) { + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid(uuid); + item.setUsername(username); + item.setProperties(properties); + item.setPublicKey(publicKey); + item.setGamemode(gamemode); + item.setPing(ping); + item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(displayName))); + return item; + } + + private PlayerListItem removePacket() { + PlayerListItem packet = new PlayerListItem(); + packet.setAction(PlayerListItem.Action.REMOVE_PLAYER); + packet.setItems(shown); + return packet; + } + + private void remove() { + player.unsafe().sendPacket(removePacket()); + } + } +} diff --git a/src/plugin.yml b/src/plugin.yml index 85fafe1..5bb14e0 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -2,4 +2,5 @@ name: BungeeCore main: de.steamwar.bungeecore.BungeeCore version: 1.0 author: Lixfel -depends: [PersistentBungeeCore, BungeeTabListPlus] \ No newline at end of file +depends: + - PersistentBungeeCore \ No newline at end of file From eed24006fbd0be312ec1892bbe50317468aaffbc Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 17 Jun 2022 20:43:36 +0200 Subject: [PATCH 2/9] Current tablist Signed-off-by: Lixfel --- .../bungeecore/tablist/TablistListener.java | 54 ++++++++-- .../bungeecore/tablist/TablistManager.java | 98 ++++++++++++++----- src/de/steamwar/messages/ChatSender.java | 4 + .../messages/SteamwarResourceBundle.java | 2 +- 4 files changed, 125 insertions(+), 33 deletions(-) diff --git a/src/de/steamwar/bungeecore/tablist/TablistListener.java b/src/de/steamwar/bungeecore/tablist/TablistListener.java index 4840405..19c3607 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistListener.java +++ b/src/de/steamwar/bungeecore/tablist/TablistListener.java @@ -19,27 +19,37 @@ package de.steamwar.bungeecore.tablist; +import de.steamwar.bungeecore.sql.SteamwarUser; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.PacketWrapper; import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; import net.md_5.bungee.protocol.packet.PlayerListItem; +import net.md_5.bungee.protocol.packet.Team; +import java.util.ArrayList; import java.util.List; public class TablistListener extends MessageToMessageDecoder { + //private boolean teamSent = false; + private final ProxiedPlayer player; private final ServerConnection connection; - public TablistListener(ServerConnection connection) { + public TablistListener(ProxiedPlayer player, ServerConnection connection) { + this.player = player; this.connection = connection; + player.unsafe().sendPacket(TablistManager.teamPacket); } @Override protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List out) { - //Team if(!connection.isObsolete()) { DefinedPacket packet = packetWrapper.packet; @@ -48,17 +58,45 @@ public class TablistListener extends MessageToMessageDecoder { return; } else if(packet instanceof PlayerListItem) { PlayerListItem list = (PlayerListItem) packet; - if(list.getAction() == PlayerListItem.Action.UPDATE_LATENCY || list.getAction() == PlayerListItem.Action.ADD_PLAYER) { + PlayerListItem.Action action = list.getAction(); + if(action == PlayerListItem.Action.UPDATE_DISPLAY_NAME || action == PlayerListItem.Action.UPDATE_LATENCY) { + packetWrapper.trySingleRelease(); + return; + } + + if(action == PlayerListItem.Action.UPDATE_GAMEMODE) { for (PlayerListItem.Item item : list.getItems()) { - item.setPing(1); - //item.setUsername("AAA"); - //item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); + ProxiedPlayer p = ProxyServer.getInstance().getPlayer(item.getUuid()); + if(p != null && p != player && item.getGamemode() == 3) { + item.setGamemode(1); + } + } + }else if(action != PlayerListItem.Action.REMOVE_PLAYER) { + List npcs = new ArrayList<>(); + for (PlayerListItem.Item item : list.getItems()) { + ProxiedPlayer p = ProxyServer.getInstance().getPlayer(item.getUuid()); + if(p == null) { + item.setPing(1000); + item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); + npcs.add(item.getUsername()); + } else { + item.setPing(1); + item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(SteamwarUser.get(p.getUniqueId()).getUserGroup().getColorCode() + p.getName()))); + if(p != player && item.getGamemode() == 3) { + item.setGamemode(1); + } + } + } + + if(!npcs.isEmpty()) { + Team teamPacket = new Team(TablistManager.TAB_TEAM); + teamPacket.setMode((byte) 3); + teamPacket.setPlayers(npcs.toArray(new String[0])); + player.unsafe().sendPacket(teamPacket); } } } } out.add(packetWrapper); - - } } diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index 69543d0..bb844b1 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -21,8 +21,11 @@ package de.steamwar.bungeecore.tablist; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.messages.ChatSender; import net.md_5.bungee.ServerConnection; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PlayerDisconnectEvent; @@ -34,20 +37,31 @@ import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.PlayerPublicKey; import net.md_5.bungee.protocol.Property; +import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; import net.md_5.bungee.protocol.packet.PlayerListItem; +import net.md_5.bungee.protocol.packet.Team; import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; import java.util.stream.Stream; public class TablistManager extends BasicListener { + public static final String TAB_TEAM = "»SW-Tab"; + + private static final Property[] GRAY = new Property[]{new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/gray.png\"}}}").getBytes(StandardCharsets.UTF_8)))}; + private static final Property[] LIGHT_GRAY = new Property[]{new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/lightgray.png\"}}}").getBytes(StandardCharsets.UTF_8)))}; + + private static final UUID[] uuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); + private static final String[] names = IntStream.range(0, 80).mapToObj(i -> " »SW« " + i).toArray(String[]::new); + public static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), "never", "never", 21, (byte)0x00, names); // new String[]{} + private final Map tablists = new HashMap<>(); + private int seconds = 0; + public TablistManager() { ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), this::updateTablist, 1, 1, TimeUnit.SECONDS); ProxyServer.getInstance().getPlayers().forEach(player -> tablists.put(player, new Tablist(player))); @@ -63,7 +77,7 @@ public class TablistManager extends BasicListener { @EventHandler public void onServerConnection(ServerSwitchEvent event) { ServerConnection server = (ServerConnection) event.getPlayer().getServer(); - server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(server)); + server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(event.getPlayer(), server)); } @EventHandler @@ -83,12 +97,13 @@ public class TablistManager extends BasicListener { private void updateTablist() { synchronized (tablists) { tablists.forEach((player, tablist) -> tablist.update()); + seconds++; } } - private static class Tablist { + private class Tablist { private final ProxiedPlayer player; - private PlayerListItem.Item[] shown = new PlayerListItem.Item[0]; + private int shown = 0; private Tablist(ProxiedPlayer player) { this.player = player; @@ -98,34 +113,63 @@ public class TablistManager extends BasicListener { if(player.getServer() == null) return; - //TODO header & footer - PlayerListItem removePacket = removePacket(); + player.unsafe().sendPacket(new PlayerListHeaderFooter( + ComponentSerializer.toString(header(ChatSender.of(player))), + ComponentSerializer.toString(ChatSender.of(player).parse(false, "TABLIST_FOOTER", player.getServer().getInfo().getName(), ping(), ProxyServer.getInstance().getPlayers().size())) + )); PlayerListItem addPacket = new PlayerListItem(); addPacket.setAction(PlayerListItem.Action.ADD_PLAYER); - shown = ProxyServer.getInstance().getServers().entrySet().stream().filter(entry -> entry.getValue() != player.getServer().getInfo()).sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())).flatMap(entry -> Stream.concat(Stream.of(construct("", "gray"), construct("§7§l" + entry.getKey(), "lightgray")), entry.getValue().getPlayers().stream().map(this::construct))).toArray(PlayerListItem.Item[]::new); - for(int i = 0; i < shown.length; i++) { - shown[i].setUsername("»SW« " + i); + PlayerListItem.Item[] items = ProxyServer.getInstance().getServers().entrySet().stream().filter(entry -> entry.getValue() != player.getServer().getInfo()).sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())).flatMap(entry -> Stream.concat(Stream.of(construct("", false), construct("§7§l" + entry.getKey(), true)), entry.getValue().getPlayers().stream().map(this::construct))).toArray(PlayerListItem.Item[]::new); + if(items.length > 80) { + items = Arrays.copyOf(items, 80); } - addPacket.setItems(shown); + for(int i = 0; i < items.length; i++) { + items[i].setUuid(uuids[i]); + items[i].setUsername(names[i]); + } + addPacket.setItems(items); - player.unsafe().sendPacket(removePacket); + if(shown > items.length) + player.unsafe().sendPacket(removePacket(items.length)); + + shown = items.length; player.unsafe().sendPacket(addPacket); + //TODO Set direct displaynames for onserver personel (and update correctly) + //TODO Add direct onserver not shown } - private PlayerListItem.Item construct(String displayName, String color) { - UUID uuid = UUID.randomUUID(); - return construct(uuid, null, new Property[]{createSkin(uuid, "»SW«", color)}, null, 1, 1000, displayName); + private BaseComponent[] header(ChatSender p){ + int phase = (seconds % 16) / 3; + switch(phase){ + case 0: + return p.parse(false, "TABLIST_PHASE_1"); + case 1: + return p.parse(false, "TABLIST_PHASE_2"); + default: + return p.parse(false, "TABLIST_PHASE_DEFAULT"); + } } - private Property createSkin(UUID uuid, String username, String color) { - // \"timestamp\":0,\"profileId\":\"" + uuid.toString().replace("-", "") + "\",\"profileName\":\"" + username + "\", - return new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/" + color + ".png\"}}}").getBytes(StandardCharsets.UTF_8))); + private String ping() { + int ping = player.getPing(); + if(ping < 50){ + return "§a" + ping; + }else if(ping < 150){ + return "§e" + ping; + }else{ + return "§c" + ping; + } + } + + private PlayerListItem.Item construct(String displayName, boolean lightgray) { + // TODO lightgray ? LIGHT_GRAY : GRAY + return construct(null, null, new Property[]{}, null, 1, 1000, displayName); } private PlayerListItem.Item construct(ProxiedPlayer player) { - return construct(player.getUniqueId(), null, ((InitialHandler)player.getPendingConnection()).getLoginProfile().getProperties(), ((InitialHandler)player.getPendingConnection()).getLoginRequest().getPublicKey(), 1, 1000, player.getDisplayName()); + return construct(player.getUniqueId(), null, ((InitialHandler)player.getPendingConnection()).getLoginProfile().getProperties(), ((InitialHandler)player.getPendingConnection()).getLoginRequest().getPublicKey(), 1, 1000, SteamwarUser.get(player.getUniqueId()).getUserGroup().getColorCode() + player.getName()); } private PlayerListItem.Item construct(UUID uuid, String username, Property[] properties, PlayerPublicKey publicKey, int gamemode, int ping, String displayName) { @@ -140,15 +184,21 @@ public class TablistManager extends BasicListener { return item; } - private PlayerListItem removePacket() { + private PlayerListItem.Item construct(UUID uuid) { + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid(uuid); + return item; + } + + private PlayerListItem removePacket(int newLength) { PlayerListItem packet = new PlayerListItem(); packet.setAction(PlayerListItem.Action.REMOVE_PLAYER); - packet.setItems(shown); + packet.setItems(IntStream.range(newLength, shown).mapToObj(i -> construct(uuids[i])).toArray(PlayerListItem.Item[]::new)); return packet; } private void remove() { - player.unsafe().sendPacket(removePacket()); + player.unsafe().sendPacket(removePacket(0)); } } } diff --git a/src/de/steamwar/messages/ChatSender.java b/src/de/steamwar/messages/ChatSender.java index 582fd6f..6879505 100644 --- a/src/de/steamwar/messages/ChatSender.java +++ b/src/de/steamwar/messages/ChatSender.java @@ -115,6 +115,10 @@ public interface ChatSender { return parseToComponent(false, message).toLegacyText(); } + default BaseComponent[] parse(boolean prefixed, String format, Object... params) { + return parse(prefixed, new Message(format, params)); + } + default BaseComponent[] parse(boolean prefixed, Message message) { Locale locale = getLocale(); ResourceBundle resourceBundle = SteamwarResourceBundle.getResourceBundle(locale); diff --git a/src/de/steamwar/messages/SteamwarResourceBundle.java b/src/de/steamwar/messages/SteamwarResourceBundle.java index c2e71ab..2252609 100644 --- a/src/de/steamwar/messages/SteamwarResourceBundle.java +++ b/src/de/steamwar/messages/SteamwarResourceBundle.java @@ -35,7 +35,7 @@ public class SteamwarResourceBundle extends PropertyResourceBundle { return getResourceBundle(locale.toString(), getResourceBundle(locale.getLanguage(), getResourceBundle( "", null))); } - private static ResourceBundle getResourceBundle(String locale, ResourceBundle parent) { + private static synchronized ResourceBundle getResourceBundle(String locale, ResourceBundle parent) { return bundles.computeIfAbsent(locale, locale1 -> { InputStream inputStream = Message.class.getResourceAsStream(BASE_PATH + ("".equals(locale) ? "" : "_" + locale) + ".properties"); if(inputStream == null) From 9703e9ea33dfdb21db36806d9081bb8545cb8073 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Mon, 13 Jun 2022 21:04:24 +0200 Subject: [PATCH 3/9] WIP new tablist Signed-off-by: Lixfel --- src/de/steamwar/bungeecore/BungeeCore.java | 5 +- .../bungeecore/tablist/TablistListener.java | 64 ++++++++ .../bungeecore/tablist/TablistManager.java | 154 ++++++++++++++++++ src/plugin.yml | 3 +- 4 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 src/de/steamwar/bungeecore/tablist/TablistListener.java create mode 100644 src/de/steamwar/bungeecore/tablist/TablistManager.java diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index 8336ea7..66385c8 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -27,6 +27,7 @@ import de.steamwar.bungeecore.listeners.mods.*; import de.steamwar.bungeecore.network.BungeeNetworkHandler; import de.steamwar.bungeecore.network.NetworkReceiver; import de.steamwar.bungeecore.sql.*; +import de.steamwar.bungeecore.tablist.TablistManager; import de.steamwar.command.SWCommandUtils; import de.steamwar.command.SWTypeMapperCreator; import de.steamwar.command.TypeMapper; @@ -68,6 +69,7 @@ public class BungeeCore extends Plugin { public static final Map commands = new HashMap<>(); private ErrorLogger errorLogger; + private TablistManager tablistManager; @Override public void onEnable(){ @@ -175,7 +177,7 @@ public class BungeeCore extends Plugin { new SessionManager(); new NetworkReceiver(); BungeeNetworkHandler.register(); - new TablistManager(); + tablistManager = new TablistManager(); new SettingsChangedListener(); getProxy().getScheduler().schedule(this, () -> { @@ -203,6 +205,7 @@ public class BungeeCore extends Plugin { } } + tablistManager.disable(); errorLogger.unregister(); Statement.closeAll(); } diff --git a/src/de/steamwar/bungeecore/tablist/TablistListener.java b/src/de/steamwar/bungeecore/tablist/TablistListener.java new file mode 100644 index 0000000..4840405 --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistListener.java @@ -0,0 +1,64 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.PacketWrapper; +import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; +import net.md_5.bungee.protocol.packet.PlayerListItem; + +import java.util.List; + +public class TablistListener extends MessageToMessageDecoder { + + private final ServerConnection connection; + + public TablistListener(ServerConnection connection) { + this.connection = connection; + } + + @Override + protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List out) { + //Team + if(!connection.isObsolete()) { + DefinedPacket packet = packetWrapper.packet; + + if(packet instanceof PlayerListHeaderFooter) { + packetWrapper.trySingleRelease(); + return; + } else if(packet instanceof PlayerListItem) { + PlayerListItem list = (PlayerListItem) packet; + if(list.getAction() == PlayerListItem.Action.UPDATE_LATENCY || list.getAction() == PlayerListItem.Action.ADD_PLAYER) { + for (PlayerListItem.Item item : list.getItems()) { + item.setPing(1); + //item.setUsername("AAA"); + //item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); + } + } + } + } + out.add(packetWrapper); + + + } +} diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java new file mode 100644 index 0000000..69543d0 --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -0,0 +1,154 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.listeners.BasicListener; +import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.netty.PipelineUtils; +import net.md_5.bungee.protocol.PlayerPublicKey; +import net.md_5.bungee.protocol.Property; +import net.md_5.bungee.protocol.packet.PlayerListItem; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +public class TablistManager extends BasicListener { + + private final Map tablists = new HashMap<>(); + + public TablistManager() { + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), this::updateTablist, 1, 1, TimeUnit.SECONDS); + ProxyServer.getInstance().getPlayers().forEach(player -> tablists.put(player, new Tablist(player))); + } + + @EventHandler + public void onJoin(PostLoginEvent event) { + synchronized (tablists) { + tablists.put(event.getPlayer(), new Tablist(event.getPlayer())); + } + } + + @EventHandler + public void onServerConnection(ServerSwitchEvent event) { + ServerConnection server = (ServerConnection) event.getPlayer().getServer(); + server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(server)); + } + + @EventHandler + public void onLeave(PlayerDisconnectEvent event) { + synchronized (tablists) { + tablists.remove(event.getPlayer()); + } + } + + public void disable() { + synchronized (tablists) { + tablists.forEach((player, tablist) -> tablist.remove()); + tablists.clear(); + } + } + + private void updateTablist() { + synchronized (tablists) { + tablists.forEach((player, tablist) -> tablist.update()); + } + } + + private static class Tablist { + private final ProxiedPlayer player; + private PlayerListItem.Item[] shown = new PlayerListItem.Item[0]; + + private Tablist(ProxiedPlayer player) { + this.player = player; + } + + private void update() { + if(player.getServer() == null) + return; + + //TODO header & footer + PlayerListItem removePacket = removePacket(); + + PlayerListItem addPacket = new PlayerListItem(); + addPacket.setAction(PlayerListItem.Action.ADD_PLAYER); + + shown = ProxyServer.getInstance().getServers().entrySet().stream().filter(entry -> entry.getValue() != player.getServer().getInfo()).sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())).flatMap(entry -> Stream.concat(Stream.of(construct("", "gray"), construct("§7§l" + entry.getKey(), "lightgray")), entry.getValue().getPlayers().stream().map(this::construct))).toArray(PlayerListItem.Item[]::new); + for(int i = 0; i < shown.length; i++) { + shown[i].setUsername("»SW« " + i); + } + addPacket.setItems(shown); + + player.unsafe().sendPacket(removePacket); + player.unsafe().sendPacket(addPacket); + } + + private PlayerListItem.Item construct(String displayName, String color) { + UUID uuid = UUID.randomUUID(); + return construct(uuid, null, new Property[]{createSkin(uuid, "»SW«", color)}, null, 1, 1000, displayName); + } + + private Property createSkin(UUID uuid, String username, String color) { + // \"timestamp\":0,\"profileId\":\"" + uuid.toString().replace("-", "") + "\",\"profileName\":\"" + username + "\", + return new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/" + color + ".png\"}}}").getBytes(StandardCharsets.UTF_8))); + } + + private PlayerListItem.Item construct(ProxiedPlayer player) { + return construct(player.getUniqueId(), null, ((InitialHandler)player.getPendingConnection()).getLoginProfile().getProperties(), ((InitialHandler)player.getPendingConnection()).getLoginRequest().getPublicKey(), 1, 1000, player.getDisplayName()); + } + + private PlayerListItem.Item construct(UUID uuid, String username, Property[] properties, PlayerPublicKey publicKey, int gamemode, int ping, String displayName) { + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid(uuid); + item.setUsername(username); + item.setProperties(properties); + item.setPublicKey(publicKey); + item.setGamemode(gamemode); + item.setPing(ping); + item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(displayName))); + return item; + } + + private PlayerListItem removePacket() { + PlayerListItem packet = new PlayerListItem(); + packet.setAction(PlayerListItem.Action.REMOVE_PLAYER); + packet.setItems(shown); + return packet; + } + + private void remove() { + player.unsafe().sendPacket(removePacket()); + } + } +} diff --git a/src/plugin.yml b/src/plugin.yml index 85fafe1..5bb14e0 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -2,4 +2,5 @@ name: BungeeCore main: de.steamwar.bungeecore.BungeeCore version: 1.0 author: Lixfel -depends: [PersistentBungeeCore, BungeeTabListPlus] \ No newline at end of file +depends: + - PersistentBungeeCore \ No newline at end of file From 598dfcd8a0d7949e41f9011b2a085a86fc510555 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 17 Jun 2022 20:43:36 +0200 Subject: [PATCH 4/9] Current tablist Signed-off-by: Lixfel --- .../bungeecore/tablist/TablistListener.java | 54 ++++++++-- .../bungeecore/tablist/TablistManager.java | 98 ++++++++++++++----- src/de/steamwar/messages/ChatSender.java | 4 + .../messages/SteamwarResourceBundle.java | 2 +- 4 files changed, 125 insertions(+), 33 deletions(-) diff --git a/src/de/steamwar/bungeecore/tablist/TablistListener.java b/src/de/steamwar/bungeecore/tablist/TablistListener.java index 4840405..19c3607 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistListener.java +++ b/src/de/steamwar/bungeecore/tablist/TablistListener.java @@ -19,27 +19,37 @@ package de.steamwar.bungeecore.tablist; +import de.steamwar.bungeecore.sql.SteamwarUser; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.PacketWrapper; import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; import net.md_5.bungee.protocol.packet.PlayerListItem; +import net.md_5.bungee.protocol.packet.Team; +import java.util.ArrayList; import java.util.List; public class TablistListener extends MessageToMessageDecoder { + //private boolean teamSent = false; + private final ProxiedPlayer player; private final ServerConnection connection; - public TablistListener(ServerConnection connection) { + public TablistListener(ProxiedPlayer player, ServerConnection connection) { + this.player = player; this.connection = connection; + player.unsafe().sendPacket(TablistManager.teamPacket); } @Override protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List out) { - //Team if(!connection.isObsolete()) { DefinedPacket packet = packetWrapper.packet; @@ -48,17 +58,45 @@ public class TablistListener extends MessageToMessageDecoder { return; } else if(packet instanceof PlayerListItem) { PlayerListItem list = (PlayerListItem) packet; - if(list.getAction() == PlayerListItem.Action.UPDATE_LATENCY || list.getAction() == PlayerListItem.Action.ADD_PLAYER) { + PlayerListItem.Action action = list.getAction(); + if(action == PlayerListItem.Action.UPDATE_DISPLAY_NAME || action == PlayerListItem.Action.UPDATE_LATENCY) { + packetWrapper.trySingleRelease(); + return; + } + + if(action == PlayerListItem.Action.UPDATE_GAMEMODE) { for (PlayerListItem.Item item : list.getItems()) { - item.setPing(1); - //item.setUsername("AAA"); - //item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); + ProxiedPlayer p = ProxyServer.getInstance().getPlayer(item.getUuid()); + if(p != null && p != player && item.getGamemode() == 3) { + item.setGamemode(1); + } + } + }else if(action != PlayerListItem.Action.REMOVE_PLAYER) { + List npcs = new ArrayList<>(); + for (PlayerListItem.Item item : list.getItems()) { + ProxiedPlayer p = ProxyServer.getInstance().getPlayer(item.getUuid()); + if(p == null) { + item.setPing(1000); + item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); + npcs.add(item.getUsername()); + } else { + item.setPing(1); + item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(SteamwarUser.get(p.getUniqueId()).getUserGroup().getColorCode() + p.getName()))); + if(p != player && item.getGamemode() == 3) { + item.setGamemode(1); + } + } + } + + if(!npcs.isEmpty()) { + Team teamPacket = new Team(TablistManager.TAB_TEAM); + teamPacket.setMode((byte) 3); + teamPacket.setPlayers(npcs.toArray(new String[0])); + player.unsafe().sendPacket(teamPacket); } } } } out.add(packetWrapper); - - } } diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index 69543d0..bb844b1 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -21,8 +21,11 @@ package de.steamwar.bungeecore.tablist; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.messages.ChatSender; import net.md_5.bungee.ServerConnection; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PlayerDisconnectEvent; @@ -34,20 +37,31 @@ import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.PlayerPublicKey; import net.md_5.bungee.protocol.Property; +import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; import net.md_5.bungee.protocol.packet.PlayerListItem; +import net.md_5.bungee.protocol.packet.Team; import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; import java.util.stream.Stream; public class TablistManager extends BasicListener { + public static final String TAB_TEAM = "»SW-Tab"; + + private static final Property[] GRAY = new Property[]{new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/gray.png\"}}}").getBytes(StandardCharsets.UTF_8)))}; + private static final Property[] LIGHT_GRAY = new Property[]{new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/lightgray.png\"}}}").getBytes(StandardCharsets.UTF_8)))}; + + private static final UUID[] uuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); + private static final String[] names = IntStream.range(0, 80).mapToObj(i -> " »SW« " + i).toArray(String[]::new); + public static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), "never", "never", 21, (byte)0x00, names); // new String[]{} + private final Map tablists = new HashMap<>(); + private int seconds = 0; + public TablistManager() { ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), this::updateTablist, 1, 1, TimeUnit.SECONDS); ProxyServer.getInstance().getPlayers().forEach(player -> tablists.put(player, new Tablist(player))); @@ -63,7 +77,7 @@ public class TablistManager extends BasicListener { @EventHandler public void onServerConnection(ServerSwitchEvent event) { ServerConnection server = (ServerConnection) event.getPlayer().getServer(); - server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(server)); + server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(event.getPlayer(), server)); } @EventHandler @@ -83,12 +97,13 @@ public class TablistManager extends BasicListener { private void updateTablist() { synchronized (tablists) { tablists.forEach((player, tablist) -> tablist.update()); + seconds++; } } - private static class Tablist { + private class Tablist { private final ProxiedPlayer player; - private PlayerListItem.Item[] shown = new PlayerListItem.Item[0]; + private int shown = 0; private Tablist(ProxiedPlayer player) { this.player = player; @@ -98,34 +113,63 @@ public class TablistManager extends BasicListener { if(player.getServer() == null) return; - //TODO header & footer - PlayerListItem removePacket = removePacket(); + player.unsafe().sendPacket(new PlayerListHeaderFooter( + ComponentSerializer.toString(header(ChatSender.of(player))), + ComponentSerializer.toString(ChatSender.of(player).parse(false, "TABLIST_FOOTER", player.getServer().getInfo().getName(), ping(), ProxyServer.getInstance().getPlayers().size())) + )); PlayerListItem addPacket = new PlayerListItem(); addPacket.setAction(PlayerListItem.Action.ADD_PLAYER); - shown = ProxyServer.getInstance().getServers().entrySet().stream().filter(entry -> entry.getValue() != player.getServer().getInfo()).sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())).flatMap(entry -> Stream.concat(Stream.of(construct("", "gray"), construct("§7§l" + entry.getKey(), "lightgray")), entry.getValue().getPlayers().stream().map(this::construct))).toArray(PlayerListItem.Item[]::new); - for(int i = 0; i < shown.length; i++) { - shown[i].setUsername("»SW« " + i); + PlayerListItem.Item[] items = ProxyServer.getInstance().getServers().entrySet().stream().filter(entry -> entry.getValue() != player.getServer().getInfo()).sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())).flatMap(entry -> Stream.concat(Stream.of(construct("", false), construct("§7§l" + entry.getKey(), true)), entry.getValue().getPlayers().stream().map(this::construct))).toArray(PlayerListItem.Item[]::new); + if(items.length > 80) { + items = Arrays.copyOf(items, 80); } - addPacket.setItems(shown); + for(int i = 0; i < items.length; i++) { + items[i].setUuid(uuids[i]); + items[i].setUsername(names[i]); + } + addPacket.setItems(items); - player.unsafe().sendPacket(removePacket); + if(shown > items.length) + player.unsafe().sendPacket(removePacket(items.length)); + + shown = items.length; player.unsafe().sendPacket(addPacket); + //TODO Set direct displaynames for onserver personel (and update correctly) + //TODO Add direct onserver not shown } - private PlayerListItem.Item construct(String displayName, String color) { - UUID uuid = UUID.randomUUID(); - return construct(uuid, null, new Property[]{createSkin(uuid, "»SW«", color)}, null, 1, 1000, displayName); + private BaseComponent[] header(ChatSender p){ + int phase = (seconds % 16) / 3; + switch(phase){ + case 0: + return p.parse(false, "TABLIST_PHASE_1"); + case 1: + return p.parse(false, "TABLIST_PHASE_2"); + default: + return p.parse(false, "TABLIST_PHASE_DEFAULT"); + } } - private Property createSkin(UUID uuid, String username, String color) { - // \"timestamp\":0,\"profileId\":\"" + uuid.toString().replace("-", "") + "\",\"profileName\":\"" + username + "\", - return new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/" + color + ".png\"}}}").getBytes(StandardCharsets.UTF_8))); + private String ping() { + int ping = player.getPing(); + if(ping < 50){ + return "§a" + ping; + }else if(ping < 150){ + return "§e" + ping; + }else{ + return "§c" + ping; + } + } + + private PlayerListItem.Item construct(String displayName, boolean lightgray) { + // TODO lightgray ? LIGHT_GRAY : GRAY + return construct(null, null, new Property[]{}, null, 1, 1000, displayName); } private PlayerListItem.Item construct(ProxiedPlayer player) { - return construct(player.getUniqueId(), null, ((InitialHandler)player.getPendingConnection()).getLoginProfile().getProperties(), ((InitialHandler)player.getPendingConnection()).getLoginRequest().getPublicKey(), 1, 1000, player.getDisplayName()); + return construct(player.getUniqueId(), null, ((InitialHandler)player.getPendingConnection()).getLoginProfile().getProperties(), ((InitialHandler)player.getPendingConnection()).getLoginRequest().getPublicKey(), 1, 1000, SteamwarUser.get(player.getUniqueId()).getUserGroup().getColorCode() + player.getName()); } private PlayerListItem.Item construct(UUID uuid, String username, Property[] properties, PlayerPublicKey publicKey, int gamemode, int ping, String displayName) { @@ -140,15 +184,21 @@ public class TablistManager extends BasicListener { return item; } - private PlayerListItem removePacket() { + private PlayerListItem.Item construct(UUID uuid) { + PlayerListItem.Item item = new PlayerListItem.Item(); + item.setUuid(uuid); + return item; + } + + private PlayerListItem removePacket(int newLength) { PlayerListItem packet = new PlayerListItem(); packet.setAction(PlayerListItem.Action.REMOVE_PLAYER); - packet.setItems(shown); + packet.setItems(IntStream.range(newLength, shown).mapToObj(i -> construct(uuids[i])).toArray(PlayerListItem.Item[]::new)); return packet; } private void remove() { - player.unsafe().sendPacket(removePacket()); + player.unsafe().sendPacket(removePacket(0)); } } } diff --git a/src/de/steamwar/messages/ChatSender.java b/src/de/steamwar/messages/ChatSender.java index 582fd6f..6879505 100644 --- a/src/de/steamwar/messages/ChatSender.java +++ b/src/de/steamwar/messages/ChatSender.java @@ -115,6 +115,10 @@ public interface ChatSender { return parseToComponent(false, message).toLegacyText(); } + default BaseComponent[] parse(boolean prefixed, String format, Object... params) { + return parse(prefixed, new Message(format, params)); + } + default BaseComponent[] parse(boolean prefixed, Message message) { Locale locale = getLocale(); ResourceBundle resourceBundle = SteamwarResourceBundle.getResourceBundle(locale); diff --git a/src/de/steamwar/messages/SteamwarResourceBundle.java b/src/de/steamwar/messages/SteamwarResourceBundle.java index c2e71ab..2252609 100644 --- a/src/de/steamwar/messages/SteamwarResourceBundle.java +++ b/src/de/steamwar/messages/SteamwarResourceBundle.java @@ -35,7 +35,7 @@ public class SteamwarResourceBundle extends PropertyResourceBundle { return getResourceBundle(locale.toString(), getResourceBundle(locale.getLanguage(), getResourceBundle( "", null))); } - private static ResourceBundle getResourceBundle(String locale, ResourceBundle parent) { + private static synchronized ResourceBundle getResourceBundle(String locale, ResourceBundle parent) { return bundles.computeIfAbsent(locale, locale1 -> { InputStream inputStream = Message.class.getResourceAsStream(BASE_PATH + ("".equals(locale) ? "" : "_" + locale) + ".properties"); if(inputStream == null) From f2ed575f8ce34a504a4ecf5dc1e17bc421db01ab Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 18 Jun 2022 13:17:23 +0200 Subject: [PATCH 5/9] Untested full tablist Signed-off-by: Lixfel --- .../bungeecore/listeners/TablistManager.java | 335 ------------------ .../network/handlers/FightInfoHandler.java | 3 +- .../steamwar/bungeecore/tablist/Tablist.java | 240 +++++++++++++ .../bungeecore/tablist/TablistGroup.java | 45 +++ .../bungeecore/tablist/TablistListener.java | 102 ------ .../bungeecore/tablist/TablistManager.java | 163 ++------- .../bungeecore/tablist/TablistPart.java | 75 ++++ .../bungeecore/tablist/TablistServer.java | 91 +++++ 8 files changed, 487 insertions(+), 567 deletions(-) delete mode 100644 src/de/steamwar/bungeecore/listeners/TablistManager.java create mode 100644 src/de/steamwar/bungeecore/tablist/Tablist.java create mode 100644 src/de/steamwar/bungeecore/tablist/TablistGroup.java delete mode 100644 src/de/steamwar/bungeecore/tablist/TablistListener.java create mode 100644 src/de/steamwar/bungeecore/tablist/TablistPart.java create mode 100644 src/de/steamwar/bungeecore/tablist/TablistServer.java diff --git a/src/de/steamwar/bungeecore/listeners/TablistManager.java b/src/de/steamwar/bungeecore/listeners/TablistManager.java deleted file mode 100644 index e1c3325..0000000 --- a/src/de/steamwar/bungeecore/listeners/TablistManager.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - 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.listeners; - -import codecrafter47.bungeetablistplus.api.bungee.BungeeTabListPlusAPI; -import codecrafter47.bungeetablistplus.api.bungee.Icon; -import codecrafter47.bungeetablistplus.tablist.DefaultCustomTablist; -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.Message; -import de.steamwar.bungeecore.Servertype; -import de.steamwar.bungeecore.Subserver; -import de.steamwar.bungeecore.sql.SteamwarUser; -import de.steamwar.bungeecore.sql.UserGroup; -import net.md_5.bungee.BungeeCord; -import de.steamwar.network.packets.common.FightInfoPacket; -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.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.event.EventHandler; - -import javax.imageio.ImageIO; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.TimeUnit; - -public class TablistManager extends BasicListener { - - private static final Map tablists = new HashMap<>(); - - @EventHandler - public synchronized void onJoin(PostLoginEvent e){ - BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { - if (e.getPlayer().isConnected()) { - tablists.put(e.getPlayer(), new Tablist(e.getPlayer())); - } - }, 1, TimeUnit.SECONDS); - } - - @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 TablistGroup tablist; - - public TablistManager(){ - 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); - }catch(IOException e){ - throw new SecurityException("Could not load icons", e); - } - - ProxyServer.getInstance().getPlayers().forEach(p -> tablists.put(p, new Tablist(p))); - } - - private synchronized void updateTablist(){ - //Calculate server-player-map - tablist = new TablistGroup(true, ""); - TablistGroup bau = new TablistGroup(false, "Bau"); - for (ServerInfo server : new ArrayList<>(ProxyServer.getInstance().getServers().values())){ - if(server.getPlayers().isEmpty()) - continue; - - Subserver subserver = Subserver.getSubserver(server); - 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)); - } - if(bau.size() > 0) - tablist.addSubTablist(bau); - - size = (int) Math.ceil((tablist.size() - 1) / 20.0); - tablists.values().forEach(Tablist::refresh); - seconds++; - } - - 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 header(){ - int phase = (seconds % 16) / 3; - switch(phase){ - case 0: - return Message.parse("TABLIST_PHASE_1", player); - case 1: - return Message.parse("TABLIST_PHASE_2", player); - case 2: - default: - return Message.parse("TABLIST_PHASE_DEFAULT", player); - } - } - - private String ping(){ - int ping = player.getPing(); - if(ping < 50){ - return "§a" + ping; - }else if(ping < 150){ - return "§e" + ping; - }else{ - return "§c" + ping; - } - } - - private void refresh(){ - if (player.getServer() == null) { - return; - } - pos = 0; - setHeader(header()); - setFooter(Message.parse("TABLIST_FOOTER", player, player.getServer().getInfo().getName(), ping(), ProxyServer.getInstance().getPlayers().size())); - int currentSize = size > 4 ? tablist.slimSize(player) : size; - setSize(currentSize, 20); - - tablist.print(this, size > 4); - - while (pos < currentSize*20){ - setSlot(darkGray, "", 1000); - } - } - - private void setSlot(Icon icon, String name, int ping){ - if(pos / 20 >= getColumns()) - return; - - setSlot(pos % 20, pos / 20, icon, name, ping); - pos++; - } - } - - private interface TablistPart { - int size(); - int slimSize(ProxiedPlayer viewer); - String name(); - void print(Tablist viewer, boolean slim); - } - - private static class TablistGroup implements TablistPart { - - private final boolean withHeaders; - private final String orderName; - private final List subTablists = new ArrayList<>(); - - private TablistGroup(boolean withHeaders, String orderName) { - this.withHeaders = withHeaders; - this.orderName = orderName; - } - - private void addSubTablist(TablistPart tablist){ - subTablists.add(tablist); - subTablists.sort((t1, t2) -> t1.name().compareToIgnoreCase(t2.name())); - } - - @Override - public int size() { - return slimSize(null); - } - - @Override - public int slimSize(ProxiedPlayer viewer) { - return subTablists.stream().mapToInt(tPart -> viewer == null ? tPart.size() : tPart.slimSize(viewer)).map(size -> { - size += withHeaders ? 1 : 0; // Space for header - size += withHeaders && size > 1 ? 1 : 0; // Space for footer - return size; - }).sum(); - } - - @Override - public String name() { - return orderName; - } - - @Override - public void print(Tablist viewer, boolean slim) { - for (int i = 0; i < subTablists.size(); i++) { - TablistPart tPart = subTablists.get(i); - String name = tPart.name(); - if (name.equals("Bau")) { - name = Message.parse("TABLIST_BAU", viewer.player); - } - boolean withoutFooter = i == subTablists.size() - 1; - if (withHeaders) { - if (slim) { - int slimSize = tPart.slimSize(viewer.player); - int size = tPart.size(); - if (size == slimSize) { - viewer.setSlot(gray, "§7§l" + name, 1000); - } else if (slimSize == 0) { - viewer.setSlot(gray, "§7§l" + name + " §8(§7" + size + "§8)", 1000); - withoutFooter = true; - } else { - viewer.setSlot(gray, "§7§l" + name + " §8(§7+" + (size - slimSize) + "§8)", 1000); - } - } else { - viewer.setSlot(gray, "§7§l" + name, 1000); - } - } - tPart.print(viewer, slim); - if (withHeaders && !withoutFooter) { - 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; - - 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); - } - - 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); - info.getPlayers().forEach(player -> players.add(new TablistPlayer(player, SteamwarUser.get(player.getUniqueId()).getUserGroup().getColorCode() + player.getName()))); - players.sort((tp1, tp2) -> tp1.player.getName().compareToIgnoreCase(tp2.player.getName())); - } - - private boolean displaySlim(ProxiedPlayer viewer, ProxiedPlayer player){ - if(viewer.getServer().getInfo() == info) - return true; - - 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 players.size(); - } - - @Override - public int slimSize(ProxiedPlayer viewer) { - if(viewer.getServer().getInfo() == info) - return size(); - - return players.stream().mapToInt(player -> displaySlim(viewer, player.player) ? 1 : 0).sum(); - } - - @Override - public String name() { - return info.getName(); - } - - @Override - public void print(Tablist viewer, boolean slim) { - boolean sameServer = viewer.player.getServer().getInfo() == info; - - SteamwarUser user = SteamwarUser.get(viewer.player.getUniqueId()); - for(TablistPlayer player : players){ - if(slim && !displaySlim(viewer.player, player.player)) - continue; - - int ping = sameServer ? 1 : 1000; - String name = player.defaultName.startsWith("§7") && user.getTeam() != 0 && user.getTeam() == SteamwarUser.get(player.player.getUniqueId()).getTeam() ? "§f" + player.player.getName() : player.defaultName; - viewer.setSlot(BungeeTabListPlusAPI.getIconFromPlayer(player.player), name, ping); - } - } - } -} diff --git a/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java b/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java index 2c98de0..3f4bff2 100644 --- a/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java +++ b/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java @@ -19,10 +19,9 @@ package de.steamwar.bungeecore.network.handlers; -import de.steamwar.bungeecore.listeners.TablistManager; -import de.steamwar.bungeecore.network.NetworkReceiver; import de.steamwar.bungeecore.network.NetworkSender; import de.steamwar.bungeecore.network.ServerMetaInfo; +import de.steamwar.bungeecore.tablist.TablistManager; import de.steamwar.network.packets.PacketHandler; import de.steamwar.network.packets.common.FightInfoPacket; import net.md_5.bungee.api.config.ServerInfo; diff --git a/src/de/steamwar/bungeecore/tablist/Tablist.java b/src/de/steamwar/bungeecore/tablist/Tablist.java new file mode 100644 index 0000000..426cb23 --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/Tablist.java @@ -0,0 +1,240 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import de.steamwar.messages.ChatSender; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.netty.PipelineUtils; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.PacketWrapper; +import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; +import net.md_5.bungee.protocol.packet.PlayerListItem; +import net.md_5.bungee.protocol.packet.Team; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Tablist extends MessageToMessageDecoder { + + private static final UUID[] uuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); + private static final String[] names = IntStream.range(0, 80).mapToObj(i -> " »SW« " + i).toArray(String[]::new); + private static final String TAB_TEAM = "»SW-Tab"; + private static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), "never", "never", 21, (byte)0x00, names); + + private final Map directlySent = new HashMap<>(); //TODO Persist over softreload + private final Set npcs = new HashSet<>(); + private final List current = new ArrayList<>(); + + private final ProxiedPlayer player; + private final ChatSender viewer; + private ServerConnection connection; + + public Tablist(ProxiedPlayer player) { + this.player = player; + this.viewer = ChatSender.of(player); + onServerSwitch(); + } + + public void update(TablistPart global, int seconds) { + if (player.getServer() == null) + return; + + player.unsafe().sendPacket(new PlayerListHeaderFooter( + ComponentSerializer.toString(header(viewer, seconds)), + ComponentSerializer.toString(viewer.parse(false, "TABLIST_FOOTER", connection.getInfo().getName(), ping(), ProxyServer.getInstance().getPlayers().size())) + )); + + List tablist = new ArrayList<>(); + List direct = new ArrayList<>(); + global.print(viewer, player, tablist, direct); + + // NPC handling + List addNpc = new ArrayList<>(); + List removeNpc = new ArrayList<>(); + for (TablistPart.Item item : direct) { + if(npcs.remove(item.getUuid())) + removeNpc.add(directlySent.get(item.getUuid()).getUsername()); + + if(!directlySent.containsKey(item.getUuid())) + tablist.add(0, item); + } + Set nonNPCs = direct.stream().map(TablistPart.Item::getUuid).collect(Collectors.toSet()); + for(PlayerListItem.Item item : directlySent.values()) { + if(!nonNPCs.contains(item.getUuid()) && !npcs.contains(item.getUuid())) + addNpc.add(item.getUsername()); + } + sendNpcPacket(addNpc, false); + sendNpcPacket(removeNpc, true); + + // Main list handling + int i = 0; + List add = new ArrayList<>(); + List update = new ArrayList<>(); + for (; i < tablist.size() && i < 80; i++) { + PlayerListItem.Item tabItem; + if(current.size() > i) { + tabItem = current.get(i); + } else { + tabItem = new PlayerListItem.Item(); + tabItem.setUuid(uuids[i]); + tabItem.setUsername(names[i]); + tabItem.setGamemode(1); + tabItem.setPing(1000); + current.add(tabItem); + } + + TablistPart.Item item = tablist.get(i); + if(!Arrays.equals(tabItem.getProperties(), item.getProperties())) { + tabItem.setProperties(item.getProperties()); + tabItem.setDisplayName(item.getDisplayName()); + add.add(tabItem); + } else if(!item.getDisplayName().equals(tabItem.getDisplayName())) { + tabItem.setDisplayName(item.getDisplayName()); + update.add(tabItem); + } + } + sendTabPacket(add, PlayerListItem.Action.ADD_PLAYER); + sendTabPacket(update, PlayerListItem.Action.UPDATE_DISPLAY_NAME); + + // Excess removal + List remove = new ArrayList<>(); + while(i < current.size()) { + remove.add(current.remove(i)); + } + sendTabPacket(remove, PlayerListItem.Action.REMOVE_PLAYER); + } + + public void onServerSwitch() { + connection = (ServerConnection) player.getServer(); + directlySent.clear(); + sendNpcPacket(npcs.stream().map(npc -> directlySent.get(npc).getUsername()).collect(Collectors.toList()), true); + npcs.clear(); + + if(connection != null) { + connection.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", this); + player.unsafe().sendPacket(teamPacket); + } + } + + public void remove() { + sendTabPacket(current, PlayerListItem.Action.REMOVE_PLAYER); + current.clear(); + sendNpcPacket(npcs.stream().map(npc -> directlySent.get(npc).getUsername()).collect(Collectors.toList()), true); + npcs.clear(); + } + + @Override + protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List out) { + if(!connection.isObsolete()) { + DefinedPacket packet = packetWrapper.packet; + + if(packet instanceof PlayerListHeaderFooter) { + packetWrapper.trySingleRelease(); + return; + } + + if(packet instanceof PlayerListItem) { + PlayerListItem list = (PlayerListItem) packet; + PlayerListItem.Action action = list.getAction(); + + switch(action) { + case UPDATE_LATENCY: + case UPDATE_DISPLAY_NAME: + packetWrapper.trySingleRelease(); + return; + case UPDATE_GAMEMODE: + for (PlayerListItem.Item item : list.getItems()) { + ProxiedPlayer p = ProxyServer.getInstance().getPlayer(item.getUuid()); + if(p != null && p != player && item.getGamemode() == 3) { + item.setGamemode(1); + } + } + break; + case ADD_PLAYER: + for (PlayerListItem.Item item : list.getItems()) { + item.setPing(1); + item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); + if(!player.getUniqueId().equals(item.getUuid()) && item.getGamemode() == 3) + item.setGamemode(1); + + directlySent.put(item.getUuid(), item); + } + break; + case REMOVE_PLAYER: + for(PlayerListItem.Item item : list.getItems()) { + directlySent.remove(item.getUuid()); + npcs.remove(item.getUuid()); + } + break; + } + } + } + out.add(packetWrapper); + } + + private void sendTabPacket(List items, PlayerListItem.Action action) { + if(!items.isEmpty()) { + PlayerListItem packet = new PlayerListItem(); + packet.setAction(action); + packet.setItems(items.toArray(new PlayerListItem.Item[0])); + player.unsafe().sendPacket(packet); + } + } + + private void sendNpcPacket(List names, boolean remove) { + if(!names.isEmpty()) { + Team packet = new Team(TAB_TEAM); + packet.setMode((byte) (remove ? 4 : 3)); + packet.setPlayers(names.toArray(new String[0])); + player.unsafe().sendPacket(packet); + } + } + + private BaseComponent[] header(ChatSender p, int seconds) { + int phase = (seconds % 16) / 3; + switch (phase) { + case 0: + return p.parse(false, "TABLIST_PHASE_1"); + case 1: + return p.parse(false, "TABLIST_PHASE_2"); + default: + return p.parse(false, "TABLIST_PHASE_DEFAULT"); + } + } + + private String ping() { + int ping = player.getPing(); + if (ping < 50) { + return "§a" + ping; + } else if (ping < 150) { + return "§e" + ping; + } else { + return "§c" + ping; + } + } +} diff --git a/src/de/steamwar/bungeecore/tablist/TablistGroup.java b/src/de/steamwar/bungeecore/tablist/TablistGroup.java new file mode 100644 index 0000000..fdb227a --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistGroup.java @@ -0,0 +1,45 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import de.steamwar.messages.ChatSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.List; + +public class TablistGroup implements TablistPart { + private final List sublists; + + public TablistGroup(List sublists) { + this.sublists = sublists; + } + + @Override + public String sortKey() { + return ""; + } + + @Override + public void print(ChatSender viewer, ProxiedPlayer player, List tablist, List direct) { + for (TablistPart sublist : sublists) { + sublist.print(viewer, player, tablist, direct); + } + } +} diff --git a/src/de/steamwar/bungeecore/tablist/TablistListener.java b/src/de/steamwar/bungeecore/tablist/TablistListener.java deleted file mode 100644 index 19c3607..0000000 --- a/src/de/steamwar/bungeecore/tablist/TablistListener.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2022 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.tablist; - -import de.steamwar.bungeecore.sql.SteamwarUser; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import net.md_5.bungee.ServerConnection; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.chat.ComponentSerializer; -import net.md_5.bungee.protocol.DefinedPacket; -import net.md_5.bungee.protocol.PacketWrapper; -import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; -import net.md_5.bungee.protocol.packet.PlayerListItem; -import net.md_5.bungee.protocol.packet.Team; - -import java.util.ArrayList; -import java.util.List; - -public class TablistListener extends MessageToMessageDecoder { - - //private boolean teamSent = false; - private final ProxiedPlayer player; - private final ServerConnection connection; - - public TablistListener(ProxiedPlayer player, ServerConnection connection) { - this.player = player; - this.connection = connection; - player.unsafe().sendPacket(TablistManager.teamPacket); - } - - @Override - protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List out) { - if(!connection.isObsolete()) { - DefinedPacket packet = packetWrapper.packet; - - if(packet instanceof PlayerListHeaderFooter) { - packetWrapper.trySingleRelease(); - return; - } else if(packet instanceof PlayerListItem) { - PlayerListItem list = (PlayerListItem) packet; - PlayerListItem.Action action = list.getAction(); - if(action == PlayerListItem.Action.UPDATE_DISPLAY_NAME || action == PlayerListItem.Action.UPDATE_LATENCY) { - packetWrapper.trySingleRelease(); - return; - } - - if(action == PlayerListItem.Action.UPDATE_GAMEMODE) { - for (PlayerListItem.Item item : list.getItems()) { - ProxiedPlayer p = ProxyServer.getInstance().getPlayer(item.getUuid()); - if(p != null && p != player && item.getGamemode() == 3) { - item.setGamemode(1); - } - } - }else if(action != PlayerListItem.Action.REMOVE_PLAYER) { - List npcs = new ArrayList<>(); - for (PlayerListItem.Item item : list.getItems()) { - ProxiedPlayer p = ProxyServer.getInstance().getPlayer(item.getUuid()); - if(p == null) { - item.setPing(1000); - item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(""))); - npcs.add(item.getUsername()); - } else { - item.setPing(1); - item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(SteamwarUser.get(p.getUniqueId()).getUserGroup().getColorCode() + p.getName()))); - if(p != player && item.getGamemode() == 3) { - item.setGamemode(1); - } - } - } - - if(!npcs.isEmpty()) { - Team teamPacket = new Team(TablistManager.TAB_TEAM); - teamPacket.setMode((byte) 3); - teamPacket.setPlayers(npcs.toArray(new String[0])); - player.unsafe().sendPacket(teamPacket); - } - } - } - } - out.add(packetWrapper); - } -} diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index bb844b1..541e176 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -20,43 +20,29 @@ package de.steamwar.bungeecore.tablist; import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.Servertype; +import de.steamwar.bungeecore.Subserver; import de.steamwar.bungeecore.listeners.BasicListener; -import de.steamwar.bungeecore.sql.SteamwarUser; -import de.steamwar.messages.ChatSender; -import net.md_5.bungee.ServerConnection; +import de.steamwar.network.packets.common.FightInfoPacket; import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.ServerSwitchEvent; -import net.md_5.bungee.chat.ComponentSerializer; -import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.netty.PipelineUtils; -import net.md_5.bungee.protocol.PlayerPublicKey; -import net.md_5.bungee.protocol.Property; -import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; -import net.md_5.bungee.protocol.packet.PlayerListItem; -import net.md_5.bungee.protocol.packet.Team; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; -import java.util.stream.Stream; public class TablistManager extends BasicListener { - public static final String TAB_TEAM = "»SW-Tab"; + private static final Map fightInfos = new HashMap<>(); - private static final Property[] GRAY = new Property[]{new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/gray.png\"}}}").getBytes(StandardCharsets.UTF_8)))}; - private static final Property[] LIGHT_GRAY = new Property[]{new Property("textures", Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"https://steamwar.de/lightgray.png\"}}}").getBytes(StandardCharsets.UTF_8)))}; - - private static final UUID[] uuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); - private static final String[] names = IntStream.range(0, 80).mapToObj(i -> " »SW« " + i).toArray(String[]::new); - public static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), "never", "never", 21, (byte)0x00, names); // new String[]{} + public static synchronized void newFightInfo(ServerInfo info, FightInfoPacket packet) { + fightInfos.put(info, packet); + fightInfos.keySet().removeIf(serverInfo -> serverInfo.getPlayers().isEmpty()); + } private final Map tablists = new HashMap<>(); @@ -76,8 +62,9 @@ public class TablistManager extends BasicListener { @EventHandler public void onServerConnection(ServerSwitchEvent event) { - ServerConnection server = (ServerConnection) event.getPlayer().getServer(); - server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(event.getPlayer(), server)); + synchronized (tablists) { + tablists.get(event.getPlayer()).onServerSwitch(); + } } @EventHandler @@ -95,110 +82,30 @@ public class TablistManager extends BasicListener { } private void updateTablist() { + List buildPlayers = new ArrayList<>(); + List subservers = new ArrayList<>(); + for (ServerInfo server : ProxyServer.getInstance().getServersCopy().values()){ + if(server.getPlayers().isEmpty()) + continue; + + Subserver subserver = Subserver.getSubserver(server); + if(subserver != null && subserver.getType() == Servertype.BAUSERVER) + server.getPlayers().forEach(player -> buildPlayers.add(new TablistPart.Item(player))); + else if(fightInfos.containsKey(server)) + subservers.add(new TablistServer(server, fightInfos.get(server))); + else + subservers.add(new TablistServer(server)); + } + subservers.sort((s1, s2) -> s1.sortKey().compareToIgnoreCase(s2.sortKey())); + if(!buildPlayers.isEmpty()) { + buildPlayers.sort((p1, p2) -> p1.getDisplayName().compareToIgnoreCase(p2.getDisplayName())); + subservers.add(new TablistServer(null, "Build", viewer -> viewer.parseToLegacy("TABLIST_BAU"), buildPlayers)); + } + TablistPart global = new TablistGroup(subservers); + synchronized (tablists) { - tablists.forEach((player, tablist) -> tablist.update()); - seconds++; - } - } - - private class Tablist { - private final ProxiedPlayer player; - private int shown = 0; - - private Tablist(ProxiedPlayer player) { - this.player = player; - } - - private void update() { - if(player.getServer() == null) - return; - - player.unsafe().sendPacket(new PlayerListHeaderFooter( - ComponentSerializer.toString(header(ChatSender.of(player))), - ComponentSerializer.toString(ChatSender.of(player).parse(false, "TABLIST_FOOTER", player.getServer().getInfo().getName(), ping(), ProxyServer.getInstance().getPlayers().size())) - )); - - PlayerListItem addPacket = new PlayerListItem(); - addPacket.setAction(PlayerListItem.Action.ADD_PLAYER); - - PlayerListItem.Item[] items = ProxyServer.getInstance().getServers().entrySet().stream().filter(entry -> entry.getValue() != player.getServer().getInfo()).sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())).flatMap(entry -> Stream.concat(Stream.of(construct("", false), construct("§7§l" + entry.getKey(), true)), entry.getValue().getPlayers().stream().map(this::construct))).toArray(PlayerListItem.Item[]::new); - if(items.length > 80) { - items = Arrays.copyOf(items, 80); - } - for(int i = 0; i < items.length; i++) { - items[i].setUuid(uuids[i]); - items[i].setUsername(names[i]); - } - addPacket.setItems(items); - - if(shown > items.length) - player.unsafe().sendPacket(removePacket(items.length)); - - shown = items.length; - player.unsafe().sendPacket(addPacket); - //TODO Set direct displaynames for onserver personel (and update correctly) - //TODO Add direct onserver not shown - } - - private BaseComponent[] header(ChatSender p){ - int phase = (seconds % 16) / 3; - switch(phase){ - case 0: - return p.parse(false, "TABLIST_PHASE_1"); - case 1: - return p.parse(false, "TABLIST_PHASE_2"); - default: - return p.parse(false, "TABLIST_PHASE_DEFAULT"); - } - } - - private String ping() { - int ping = player.getPing(); - if(ping < 50){ - return "§a" + ping; - }else if(ping < 150){ - return "§e" + ping; - }else{ - return "§c" + ping; - } - } - - private PlayerListItem.Item construct(String displayName, boolean lightgray) { - // TODO lightgray ? LIGHT_GRAY : GRAY - return construct(null, null, new Property[]{}, null, 1, 1000, displayName); - } - - private PlayerListItem.Item construct(ProxiedPlayer player) { - return construct(player.getUniqueId(), null, ((InitialHandler)player.getPendingConnection()).getLoginProfile().getProperties(), ((InitialHandler)player.getPendingConnection()).getLoginRequest().getPublicKey(), 1, 1000, SteamwarUser.get(player.getUniqueId()).getUserGroup().getColorCode() + player.getName()); - } - - private PlayerListItem.Item construct(UUID uuid, String username, Property[] properties, PlayerPublicKey publicKey, int gamemode, int ping, String displayName) { - PlayerListItem.Item item = new PlayerListItem.Item(); - item.setUuid(uuid); - item.setUsername(username); - item.setProperties(properties); - item.setPublicKey(publicKey); - item.setGamemode(gamemode); - item.setPing(ping); - item.setDisplayName(ComponentSerializer.toString(TextComponent.fromLegacyText(displayName))); - return item; - } - - private PlayerListItem.Item construct(UUID uuid) { - PlayerListItem.Item item = new PlayerListItem.Item(); - item.setUuid(uuid); - return item; - } - - private PlayerListItem removePacket(int newLength) { - PlayerListItem packet = new PlayerListItem(); - packet.setAction(PlayerListItem.Action.REMOVE_PLAYER); - packet.setItems(IntStream.range(newLength, shown).mapToObj(i -> construct(uuids[i])).toArray(PlayerListItem.Item[]::new)); - return packet; - } - - private void remove() { - player.unsafe().sendPacket(removePacket(0)); + tablists.forEach((player, tablist) -> tablist.update(global, seconds)); } + seconds++; } } diff --git a/src/de/steamwar/bungeecore/tablist/TablistPart.java b/src/de/steamwar/bungeecore/tablist/TablistPart.java new file mode 100644 index 0000000..2388116 --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistPart.java @@ -0,0 +1,75 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.messages.ChatSender; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.protocol.Property; + +import java.util.List; +import java.util.UUID; + +interface TablistPart { + String sortKey(); + void print(ChatSender viewer, ProxiedPlayer player, List tablist, List direct); + + class Item { + + public static Property[] playerProperties(ProxiedPlayer player) { + return ((InitialHandler) player.getPendingConnection()).getLoginProfile().getProperties(); + } + + private final UUID uuid; + private final String displayName; + private final Property[] properties; + + public Item(UUID uuid, String displayName, Property[] properties) { + this.uuid = uuid; + this.displayName = reformat(displayName); + this.properties = properties; + } + + public Item(ProxiedPlayer player) { + this.uuid = player.getUniqueId(); + this.displayName = reformat(SteamwarUser.get(player.getUniqueId()).getUserGroup().getColorCode() + player.getName()); + this.properties = playerProperties(player); + } + + public UUID getUuid() { + return uuid; + } + + public String getDisplayName() { + return displayName; + } + + public Property[] getProperties() { + return properties; + } + + private String reformat(String string) { + return ComponentSerializer.toString(TextComponent.fromLegacyText(string)); + } + } +} diff --git a/src/de/steamwar/bungeecore/tablist/TablistServer.java b/src/de/steamwar/bungeecore/tablist/TablistServer.java new file mode 100644 index 0000000..d78bced --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistServer.java @@ -0,0 +1,91 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import de.steamwar.bungeecore.sql.SteamwarUser; +import de.steamwar.messages.ChatSender; +import de.steamwar.network.packets.common.FightInfoPacket; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.protocol.Property; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class TablistServer implements TablistPart { + + public static final Property[] GRAY = new Property[]{new Property("textures", "eyJ0aW1lc3RhbXAiOjE0NTU1NzQxMTk0MzMsInByb2ZpbGVJZCI6ImIzYjE4MzQ1MzViZjRiNzU4ZTBjZGJmMGY4MjA2NTZlIiwicHJvZmlsZU5hbWUiOiIxMDExMTEiLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzZlNzJkMzE0NzczMmQ5NzFkZWZhZTIzMWIzOGQ5NDI0MTRiMDU3YTcxNTFjNTNjNWZkNjI5NmEzYjllZGEwYWIifX19", "ro/ZKHt7278yhCr+CFTcPp/q6wAUlef//85k2DzkfRaZqy0CtGgwisDs2U4pVKvQ2pfXvitzWgbJvD0bLeQ12xWi4c1Fc29LCArosVJoFmrJDHz7N2MlstHT+ynQROb9d2aiFA6uOXfLjPKb1noUZ/YQoZjqcPIvD5oFZtD5DHV5O4hYz0IvgHbIjDqjz6ITsTcKiBlbxNg2loTFxSlW1ZfnNCO+kcAmeyB5NFY3j0e+/AqVANiNoiC3OKsECM/yEx/acf+vKWcT8mQn4wRoIGtxfEU7ZjNtgdh73NvXXBygW+K9AiJ242g8Y06Xxuk8kaNEGmT6H/mM7nbwjZmQQXpi/Pao2gYqyeIofeCPfr8RsGXoDX3nXDAw8/LyhTCHgx+sp6IQYSfGcSMJtoNeTJ0liIFxqn1V9/zKmzOZAPzR6qrQPOjoRFljLAlv7rfzotaEqh/1ldd40GdS8tstczn7f29OQerNDaqvbDb00Gy0STdUr1bVyCDptA54XKjT9WFv7QpBikEculxqSppAXPxD2Fb/ZmphbZx8WEGfG6bVFhf6fQdDAUXlcv8BxjElNPwlolF86M2KJd5VquLluhrCjwID7OK/pffNultAVH+Lxw4QOAXmJqjUrA1KHgyG1S0Cwj/f4E2hdxZJBvkfVtq9qPkd9nignhEoTCTOHf0=")}; + public static final Property[] LIGHT_GRAY = new Property[]{new Property("textures", "eyJ0aW1lc3RhbXAiOjE0NTU2MjU1OTM5NjIsInByb2ZpbGVJZCI6ImIzYjE4MzQ1MzViZjRiNzU4ZTBjZGJmMGY4MjA2NTZlIiwicHJvZmlsZU5hbWUiOiIxMDExMTEiLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzc4Y2I3ZmMyMDhiMzM4NTUwNGE4MTQ0MjA0NDI4ZmRjZDYzMjRiZWIzMWNhMmNlODZjYzQyNGI5NjNkODVjIn19fQ==", "R/wZUZRC1dishRdM9a2SSxxW3oYa0XSb/MxHbQpEUA791HxyqjaKLDu0wFX2r2a8ZTeVjzXpNzkg3+PkrA11o8h7lt86MTD1pi/rQqj/WRuoqf2LP+ypbssKV+LU15cYez2cj3QQVcJDXgWEnfSLNuBv6NG8BDUpUAjTWldvu99NCJHUoD0jNMHxY/fu4k5vCgOjaBaKgkjVk2bmUhegusmtMwco+3pYx+y8+gUW8ptx5SnePG+dOwTqLyBFiOt2AQ+gSvbU/jP9aAXgxOwz/b1pMaBWtzVhFU865NHlIdSpIHg/sh3uNah3a7gTgtTvxPQv1OzM/KtqYKiamsrRzAQMzRcs4A7Tp0GakLuxEaz401IwvQ7UGVYLFzGUVLB2MyqtPgifiqQSQxZpiqj9sM5QadhsUw00nfX7mTdW46U0MtNIbby1rLrvgQKoj08zt6LJlhI3yjyawy4iZkgF4oc+PCNwZc93GIbVL9LJaGkXk3RVA+JpGwfMJrGVbL7hl8ibbAcUv7uCEWdkAgZCd6w75jEE4tlhDSPDD4rXbn+FeTZRg2n/PGKtnoTZRzbniiFaNoSAHDZSVRG39xvBDFvtmL3SPaKhzKaifiYrgNn453WtR3kymqdAtPf1GN9d1VltGZ/+vMPwqPJb6thcrlcU64UGHbg1olRkiyZHvY8=")}; + + private final ServerInfo server; + private final String sortKey; + private final Function title; + private final List players; + + public TablistServer(ServerInfo server) { + this(server, server.getName(), viewer -> server.getName(), server.getPlayers().stream().sorted((p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName())).map(TablistPart.Item::new).collect(Collectors.toList())); + } + + public TablistServer(ServerInfo server, FightInfoPacket info) { + this(server, server.getName(), viewer -> server.getName(), new ArrayList<>()); + + Collection onlinePlayers = server.getPlayers(); + addPlayers(info.getBlueName().substring(0, 2), info.getBluePlayers(), onlinePlayers); + addPlayers(info.getRedName().substring(0, 2), info.getRedPlayers(), onlinePlayers); + addPlayers("§7", info.getSpectators(), onlinePlayers); + } + + public TablistServer(ServerInfo server, String sortKey, Function title, List players) { + this.server = server; + this.sortKey = sortKey; + this.title = title; + this.players = players; + } + + @Override + public String sortKey() { + return sortKey; + } + + @Override + public void print(ChatSender viewer, ProxiedPlayer player, List tablist, List direct) { + boolean onServer = player.getServer().getInfo() == server; + List items = onServer ? direct : tablist; + + if(!onServer && title != null) { + items.add(new Item(null, "", GRAY)); + items.add(new Item(null, "§7" + title.apply(viewer), LIGHT_GRAY)); + } + + items.addAll(players); + } + + 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 Item(player.getUniqueId(), prefix + player.getName(), Item.playerProperties(player)))); + } +} From 616062354fa28e4f565218a1809163d50a07e6a3 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 18 Jun 2022 14:07:00 +0200 Subject: [PATCH 6/9] Fixes, working Signed-off-by: Lixfel --- .../steamwar/bungeecore/tablist/Tablist.java | 11 ++- .../bungeecore/tablist/TablistBuild.java | 69 +++++++++++++++++++ .../bungeecore/tablist/TablistManager.java | 12 +--- .../bungeecore/tablist/TablistServer.java | 17 ++--- .../steamwar/messages/BungeeCore.properties | 2 +- .../messages/BungeeCore_de.properties | 2 +- 6 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 src/de/steamwar/bungeecore/tablist/TablistBuild.java diff --git a/src/de/steamwar/bungeecore/tablist/Tablist.java b/src/de/steamwar/bungeecore/tablist/Tablist.java index 426cb23..c8a47e5 100644 --- a/src/de/steamwar/bungeecore/tablist/Tablist.java +++ b/src/de/steamwar/bungeecore/tablist/Tablist.java @@ -20,6 +20,7 @@ package de.steamwar.bungeecore.tablist; import de.steamwar.messages.ChatSender; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import net.md_5.bungee.ServerConnection; @@ -39,6 +40,7 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; +@ChannelHandler.Sharable public class Tablist extends MessageToMessageDecoder { private static final UUID[] uuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); @@ -76,12 +78,18 @@ public class Tablist extends MessageToMessageDecoder { // NPC handling List addNpc = new ArrayList<>(); List removeNpc = new ArrayList<>(); + List update = new ArrayList<>(); for (TablistPart.Item item : direct) { if(npcs.remove(item.getUuid())) removeNpc.add(directlySent.get(item.getUuid()).getUsername()); - if(!directlySent.containsKey(item.getUuid())) + PlayerListItem.Item tabItem = directlySent.get(item.getUuid()); + if(tabItem == null) { tablist.add(0, item); + } else if(!tabItem.getDisplayName().equals(item.getDisplayName())) { + tabItem.setDisplayName(item.getDisplayName()); + update.add(tabItem); + } } Set nonNPCs = direct.stream().map(TablistPart.Item::getUuid).collect(Collectors.toSet()); for(PlayerListItem.Item item : directlySent.values()) { @@ -94,7 +102,6 @@ public class Tablist extends MessageToMessageDecoder { // Main list handling int i = 0; List add = new ArrayList<>(); - List update = new ArrayList<>(); for (; i < tablist.size() && i < 80; i++) { PlayerListItem.Item tabItem; if(current.size() > i) { diff --git a/src/de/steamwar/bungeecore/tablist/TablistBuild.java b/src/de/steamwar/bungeecore/tablist/TablistBuild.java new file mode 100644 index 0000000..dcb8f67 --- /dev/null +++ b/src/de/steamwar/bungeecore/tablist/TablistBuild.java @@ -0,0 +1,69 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 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.tablist; + +import de.steamwar.bungeecore.Servertype; +import de.steamwar.bungeecore.Subserver; +import de.steamwar.messages.ChatSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class TablistBuild implements TablistPart { + + private final List servers = new ArrayList<>(); + private final Map> players = new HashMap<>(); + + public TablistBuild() { + for (ServerInfo server : ProxyServer.getInstance().getServersCopy().values()){ + Subserver subserver = Subserver.getSubserver(server); + if(server.getPlayers().isEmpty() || subserver == null || subserver.getType() != Servertype.BAUSERVER) + continue; + + servers.add(server); + players.put(server, server.getPlayers().stream().sorted(((p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName()))).map(Item::new).collect(Collectors.toList())); + } + servers.sort((s1, s2) -> s1.getName().compareToIgnoreCase(s2.getName())); + } + + @Override + public String sortKey() { + return "Build"; + } + + @Override + public void print(ChatSender viewer, ProxiedPlayer player, List tablist, List direct) { + ServerInfo server = player.getServer().getInfo(); + if(players.keySet().stream().anyMatch(info -> server != info)) { + tablist.add(new Item(null, "", TablistServer.GRAY)); + tablist.add(new Item(null, viewer.parseToLegacy("TABLIST_BAU"), TablistServer.LIGHT_GRAY)); + } + + for(ServerInfo info : servers) { + (server == info ? direct : tablist).addAll(players.get(info)); + } + } +} diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index 541e176..e8a0b08 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -82,25 +82,19 @@ public class TablistManager extends BasicListener { } private void updateTablist() { - List buildPlayers = new ArrayList<>(); List subservers = new ArrayList<>(); for (ServerInfo server : ProxyServer.getInstance().getServersCopy().values()){ if(server.getPlayers().isEmpty()) continue; Subserver subserver = Subserver.getSubserver(server); - if(subserver != null && subserver.getType() == Servertype.BAUSERVER) - server.getPlayers().forEach(player -> buildPlayers.add(new TablistPart.Item(player))); - else if(fightInfos.containsKey(server)) + if(fightInfos.containsKey(server)) subservers.add(new TablistServer(server, fightInfos.get(server))); - else + else if(subserver == null || subserver.getType() != Servertype.BAUSERVER) subservers.add(new TablistServer(server)); } + subservers.add(new TablistBuild()); subservers.sort((s1, s2) -> s1.sortKey().compareToIgnoreCase(s2.sortKey())); - if(!buildPlayers.isEmpty()) { - buildPlayers.sort((p1, p2) -> p1.getDisplayName().compareToIgnoreCase(p2.getDisplayName())); - subservers.add(new TablistServer(null, "Build", viewer -> viewer.parseToLegacy("TABLIST_BAU"), buildPlayers)); - } TablistPart global = new TablistGroup(subservers); synchronized (tablists) { diff --git a/src/de/steamwar/bungeecore/tablist/TablistServer.java b/src/de/steamwar/bungeecore/tablist/TablistServer.java index d78bced..0a95a48 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistServer.java +++ b/src/de/steamwar/bungeecore/tablist/TablistServer.java @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.function.Function; import java.util.stream.Collectors; public class TablistServer implements TablistPart { @@ -39,16 +38,14 @@ public class TablistServer implements TablistPart { public static final Property[] LIGHT_GRAY = new Property[]{new Property("textures", "eyJ0aW1lc3RhbXAiOjE0NTU2MjU1OTM5NjIsInByb2ZpbGVJZCI6ImIzYjE4MzQ1MzViZjRiNzU4ZTBjZGJmMGY4MjA2NTZlIiwicHJvZmlsZU5hbWUiOiIxMDExMTEiLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzc4Y2I3ZmMyMDhiMzM4NTUwNGE4MTQ0MjA0NDI4ZmRjZDYzMjRiZWIzMWNhMmNlODZjYzQyNGI5NjNkODVjIn19fQ==", "R/wZUZRC1dishRdM9a2SSxxW3oYa0XSb/MxHbQpEUA791HxyqjaKLDu0wFX2r2a8ZTeVjzXpNzkg3+PkrA11o8h7lt86MTD1pi/rQqj/WRuoqf2LP+ypbssKV+LU15cYez2cj3QQVcJDXgWEnfSLNuBv6NG8BDUpUAjTWldvu99NCJHUoD0jNMHxY/fu4k5vCgOjaBaKgkjVk2bmUhegusmtMwco+3pYx+y8+gUW8ptx5SnePG+dOwTqLyBFiOt2AQ+gSvbU/jP9aAXgxOwz/b1pMaBWtzVhFU865NHlIdSpIHg/sh3uNah3a7gTgtTvxPQv1OzM/KtqYKiamsrRzAQMzRcs4A7Tp0GakLuxEaz401IwvQ7UGVYLFzGUVLB2MyqtPgifiqQSQxZpiqj9sM5QadhsUw00nfX7mTdW46U0MtNIbby1rLrvgQKoj08zt6LJlhI3yjyawy4iZkgF4oc+PCNwZc93GIbVL9LJaGkXk3RVA+JpGwfMJrGVbL7hl8ibbAcUv7uCEWdkAgZCd6w75jEE4tlhDSPDD4rXbn+FeTZRg2n/PGKtnoTZRzbniiFaNoSAHDZSVRG39xvBDFvtmL3SPaKhzKaifiYrgNn453WtR3kymqdAtPf1GN9d1VltGZ/+vMPwqPJb6thcrlcU64UGHbg1olRkiyZHvY8=")}; private final ServerInfo server; - private final String sortKey; - private final Function title; private final List players; public TablistServer(ServerInfo server) { - this(server, server.getName(), viewer -> server.getName(), server.getPlayers().stream().sorted((p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName())).map(TablistPart.Item::new).collect(Collectors.toList())); + this(server, server.getPlayers().stream().sorted((p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName())).map(TablistPart.Item::new).collect(Collectors.toList())); } public TablistServer(ServerInfo server, FightInfoPacket info) { - this(server, server.getName(), viewer -> server.getName(), new ArrayList<>()); + this(server, new ArrayList<>()); Collection onlinePlayers = server.getPlayers(); addPlayers(info.getBlueName().substring(0, 2), info.getBluePlayers(), onlinePlayers); @@ -56,16 +53,14 @@ public class TablistServer implements TablistPart { addPlayers("§7", info.getSpectators(), onlinePlayers); } - public TablistServer(ServerInfo server, String sortKey, Function title, List players) { + public TablistServer(ServerInfo server, List players) { this.server = server; - this.sortKey = sortKey; - this.title = title; this.players = players; } @Override public String sortKey() { - return sortKey; + return server.getName(); } @Override @@ -73,9 +68,9 @@ public class TablistServer implements TablistPart { boolean onServer = player.getServer().getInfo() == server; List items = onServer ? direct : tablist; - if(!onServer && title != null) { + if(!onServer) { items.add(new Item(null, "", GRAY)); - items.add(new Item(null, "§7" + title.apply(viewer), LIGHT_GRAY)); + items.add(new Item(null, "§7§l" + server.getName(), LIGHT_GRAY)); } items.addAll(players); diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index 45a4f05..e18f608 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -552,7 +552,7 @@ TABLIST_PHASE_1=§8Teamspeak: §eSteam§8War.de TABLIST_PHASE_2=§8Discord: §8https://§eSteam§8War.de/discord TABLIST_PHASE_DEFAULT=§8Website: https://§eSteam§8War.de TABLIST_FOOTER=§e{0} {1}§8ms §ePlayers§8: §7{2} -TABLIST_BAU=Build +TABLIST_BAU=§7§lBuild LIST_COMMAND=§e{0}§8: §7{1} #EventStarter diff --git a/src/de/steamwar/messages/BungeeCore_de.properties b/src/de/steamwar/messages/BungeeCore_de.properties index 21a7a55..d3f7d22 100644 --- a/src/de/steamwar/messages/BungeeCore_de.properties +++ b/src/de/steamwar/messages/BungeeCore_de.properties @@ -531,7 +531,7 @@ TABLIST_PHASE_1=§8Teamspeak: §eSteam§8War.de TABLIST_PHASE_2=§8Discord: §8https://§eSteam§8War.de/discord TABLIST_PHASE_DEFAULT=§8Website: https://§eSteam§8War.de TABLIST_FOOTER=§e{0} {1}§8ms §eSpieler§8: §7{2} -TABLIST_BAU=Bau +TABLIST_BAU=§7§lBau LIST_COMMAND=§e{0}§8: §7{1} #EventStarter From f7fbefec82b1d11902c2a997cfa479099669b128 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 18 Jun 2022 14:15:48 +0200 Subject: [PATCH 7/9] Make Tablist softreloadable Signed-off-by: Lixfel --- .../steamwar/bungeecore/tablist/Tablist.java | 20 ++++++++++--------- .../bungeecore/tablist/TablistManager.java | 3 +++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/de/steamwar/bungeecore/tablist/Tablist.java b/src/de/steamwar/bungeecore/tablist/Tablist.java index c8a47e5..a382c8d 100644 --- a/src/de/steamwar/bungeecore/tablist/Tablist.java +++ b/src/de/steamwar/bungeecore/tablist/Tablist.java @@ -19,6 +19,7 @@ package de.steamwar.bungeecore.tablist; +import de.steamwar.bungeecore.Storage; import de.steamwar.messages.ChatSender; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; @@ -48,7 +49,7 @@ public class Tablist extends MessageToMessageDecoder { private static final String TAB_TEAM = "»SW-Tab"; private static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), "never", "never", 21, (byte)0x00, names); - private final Map directlySent = new HashMap<>(); //TODO Persist over softreload + private final Map directTabItems; private final Set npcs = new HashSet<>(); private final List current = new ArrayList<>(); @@ -59,6 +60,7 @@ public class Tablist extends MessageToMessageDecoder { public Tablist(ProxiedPlayer player) { this.player = player; this.viewer = ChatSender.of(player); + this.directTabItems = Storage.directTabItems.computeIfAbsent(player, p -> new HashMap<>()); onServerSwitch(); } @@ -81,9 +83,9 @@ public class Tablist extends MessageToMessageDecoder { List update = new ArrayList<>(); for (TablistPart.Item item : direct) { if(npcs.remove(item.getUuid())) - removeNpc.add(directlySent.get(item.getUuid()).getUsername()); + removeNpc.add(directTabItems.get(item.getUuid()).getUsername()); - PlayerListItem.Item tabItem = directlySent.get(item.getUuid()); + PlayerListItem.Item tabItem = directTabItems.get(item.getUuid()); if(tabItem == null) { tablist.add(0, item); } else if(!tabItem.getDisplayName().equals(item.getDisplayName())) { @@ -92,7 +94,7 @@ public class Tablist extends MessageToMessageDecoder { } } Set nonNPCs = direct.stream().map(TablistPart.Item::getUuid).collect(Collectors.toSet()); - for(PlayerListItem.Item item : directlySent.values()) { + for(PlayerListItem.Item item : directTabItems.values()) { if(!nonNPCs.contains(item.getUuid()) && !npcs.contains(item.getUuid())) addNpc.add(item.getUsername()); } @@ -138,8 +140,8 @@ public class Tablist extends MessageToMessageDecoder { public void onServerSwitch() { connection = (ServerConnection) player.getServer(); - directlySent.clear(); - sendNpcPacket(npcs.stream().map(npc -> directlySent.get(npc).getUsername()).collect(Collectors.toList()), true); + directTabItems.clear(); + sendNpcPacket(npcs.stream().map(npc -> directTabItems.get(npc).getUsername()).collect(Collectors.toList()), true); npcs.clear(); if(connection != null) { @@ -151,7 +153,7 @@ public class Tablist extends MessageToMessageDecoder { public void remove() { sendTabPacket(current, PlayerListItem.Action.REMOVE_PLAYER); current.clear(); - sendNpcPacket(npcs.stream().map(npc -> directlySent.get(npc).getUsername()).collect(Collectors.toList()), true); + sendNpcPacket(npcs.stream().map(npc -> directTabItems.get(npc).getUsername()).collect(Collectors.toList()), true); npcs.clear(); } @@ -189,12 +191,12 @@ public class Tablist extends MessageToMessageDecoder { if(!player.getUniqueId().equals(item.getUuid()) && item.getGamemode() == 3) item.setGamemode(1); - directlySent.put(item.getUuid(), item); + directTabItems.put(item.getUuid(), item); } break; case REMOVE_PLAYER: for(PlayerListItem.Item item : list.getItems()) { - directlySent.remove(item.getUuid()); + directTabItems.remove(item.getUuid()); npcs.remove(item.getUuid()); } break; diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index e8a0b08..5039ad6 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -21,6 +21,7 @@ package de.steamwar.bungeecore.tablist; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.Servertype; +import de.steamwar.bungeecore.Storage; import de.steamwar.bungeecore.Subserver; import de.steamwar.bungeecore.listeners.BasicListener; import de.steamwar.network.packets.common.FightInfoPacket; @@ -56,6 +57,7 @@ public class TablistManager extends BasicListener { @EventHandler public void onJoin(PostLoginEvent event) { synchronized (tablists) { + Storage.directTabItems.put(event.getPlayer(), new HashMap<>()); tablists.put(event.getPlayer(), new Tablist(event.getPlayer())); } } @@ -71,6 +73,7 @@ public class TablistManager extends BasicListener { public void onLeave(PlayerDisconnectEvent event) { synchronized (tablists) { tablists.remove(event.getPlayer()); + Storage.directTabItems.remove(event.getPlayer()); } } From eb469896f98dac5ebdd2b0d36a9718c497cd0997 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 18 Jun 2022 14:17:38 +0200 Subject: [PATCH 8/9] Remove unnecessary put Signed-off-by: Lixfel --- src/de/steamwar/bungeecore/tablist/TablistManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index 5039ad6..ef1c627 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -57,7 +57,6 @@ public class TablistManager extends BasicListener { @EventHandler public void onJoin(PostLoginEvent event) { synchronized (tablists) { - Storage.directTabItems.put(event.getPlayer(), new HashMap<>()); tablists.put(event.getPlayer(), new Tablist(event.getPlayer())); } } From 64567c155609f07957e5612012bb57084ae2fdc9 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 18 Jun 2022 14:32:25 +0200 Subject: [PATCH 9/9] Fix softreload issues Signed-off-by: Lixfel --- src/de/steamwar/bungeecore/tablist/Tablist.java | 7 +++++-- src/de/steamwar/bungeecore/tablist/TablistManager.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/bungeecore/tablist/Tablist.java b/src/de/steamwar/bungeecore/tablist/Tablist.java index a382c8d..53c2bbe 100644 --- a/src/de/steamwar/bungeecore/tablist/Tablist.java +++ b/src/de/steamwar/bungeecore/tablist/Tablist.java @@ -47,7 +47,7 @@ public class Tablist extends MessageToMessageDecoder { private static final UUID[] uuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); private static final String[] names = IntStream.range(0, 80).mapToObj(i -> " »SW« " + i).toArray(String[]::new); private static final String TAB_TEAM = "»SW-Tab"; - private static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), "never", "never", 21, (byte)0x00, names); + private static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), ComponentSerializer.toString(TextComponent.fromLegacyText("")), "always", "always", 21, (byte)0x00, names); private final Map directTabItems; private final Set npcs = new HashSet<>(); @@ -150,11 +150,14 @@ public class Tablist extends MessageToMessageDecoder { } } - public void remove() { + public void disable() { sendTabPacket(current, PlayerListItem.Action.REMOVE_PLAYER); current.clear(); sendNpcPacket(npcs.stream().map(npc -> directTabItems.get(npc).getUsername()).collect(Collectors.toList()), true); npcs.clear(); + + if(connection != null) + connection.getCh().getHandle().pipeline().remove(this); } @Override diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index ef1c627..0f02f55 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -78,7 +78,7 @@ public class TablistManager extends BasicListener { public void disable() { synchronized (tablists) { - tablists.forEach((player, tablist) -> tablist.remove()); + tablists.forEach((player, tablist) -> tablist.disable()); tablists.clear(); } }