From 598dfcd8a0d7949e41f9011b2a085a86fc510555 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 17 Jun 2022 20:43:36 +0200 Subject: [PATCH] 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 48404050..19c3607e 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 69543d0f..bb844b13 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 582fd6f0..6879505f 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 c2e71ab1..22526098 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)