From c2544e44fc96767b068583a80fcca50d04258b7d Mon Sep 17 00:00:00 2001 From: Lixfel Date: Mon, 13 Jun 2022 21:04:24 +0200 Subject: [PATCH 1/2] 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/2] 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)