Archiviert
1
0

Merge pull request 'Tablist' (#411) from tablist into master

Reviewed-on: SteamWar/BungeeCore#411
Reviewed-by: Chaoscaot <chaoscaot444@gmail.com>
Dieser Commit ist enthalten in:
Lixfel 2022-06-18 15:31:48 +02:00
Commit 129e41cb77
14 geänderte Dateien mit 648 neuen und 342 gelöschten Zeilen

Datei anzeigen

@ -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<String, String> 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();
}

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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<ProxiedPlayer, Tablist> 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<ServerInfo, FightInfoPacket> 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<TablistPart> 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<TablistPlayer> 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<ProxiedPlayer> 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<Integer> teamPlayers, Collection<ProxiedPlayer> 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);
}
}
}
}

Datei anzeigen

@ -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;

Datei anzeigen

@ -0,0 +1,252 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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;
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;
@ChannelHandler.Sharable
public class Tablist extends MessageToMessageDecoder<PacketWrapper> {
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("")), "always", "always", 21, (byte)0x00, names);
private final Map<UUID, PlayerListItem.Item> directTabItems;
private final Set<UUID> npcs = new HashSet<>();
private final List<PlayerListItem.Item> 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);
this.directTabItems = Storage.directTabItems.computeIfAbsent(player, p -> new HashMap<>());
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<TablistPart.Item> tablist = new ArrayList<>();
List<TablistPart.Item> direct = new ArrayList<>();
global.print(viewer, player, tablist, direct);
// NPC handling
List<String> addNpc = new ArrayList<>();
List<String> removeNpc = new ArrayList<>();
List<PlayerListItem.Item> update = new ArrayList<>();
for (TablistPart.Item item : direct) {
if(npcs.remove(item.getUuid()))
removeNpc.add(directTabItems.get(item.getUuid()).getUsername());
PlayerListItem.Item tabItem = directTabItems.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<UUID> nonNPCs = direct.stream().map(TablistPart.Item::getUuid).collect(Collectors.toSet());
for(PlayerListItem.Item item : directTabItems.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<PlayerListItem.Item> add = 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<PlayerListItem.Item> 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();
directTabItems.clear();
sendNpcPacket(npcs.stream().map(npc -> directTabItems.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 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
protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List<Object> 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);
directTabItems.put(item.getUuid(), item);
}
break;
case REMOVE_PLAYER:
for(PlayerListItem.Item item : list.getItems()) {
directTabItems.remove(item.getUuid());
npcs.remove(item.getUuid());
}
break;
}
}
}
out.add(packetWrapper);
}
private void sendTabPacket(List<PlayerListItem.Item> 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<String> 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;
}
}
}

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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<ServerInfo> servers = new ArrayList<>();
private final Map<ServerInfo, List<Item>> 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<Item> tablist, List<Item> 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));
}
}
}

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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<TablistPart> sublists;
public TablistGroup(List<TablistPart> sublists) {
this.sublists = sublists;
}
@Override
public String sortKey() {
return "";
}
@Override
public void print(ChatSender viewer, ProxiedPlayer player, List<Item> tablist, List<Item> direct) {
for (TablistPart sublist : sublists) {
sublist.print(viewer, player, tablist, direct);
}
}
}

Datei anzeigen

@ -0,0 +1,107 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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;
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.api.event.ServerSwitchEvent;
import net.md_5.bungee.event.EventHandler;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class TablistManager extends BasicListener {
private static final Map<ServerInfo, FightInfoPacket> fightInfos = new HashMap<>();
public static synchronized void newFightInfo(ServerInfo info, FightInfoPacket packet) {
fightInfos.put(info, packet);
fightInfos.keySet().removeIf(serverInfo -> serverInfo.getPlayers().isEmpty());
}
private final Map<ProxiedPlayer, Tablist> 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)));
}
@EventHandler
public void onJoin(PostLoginEvent event) {
synchronized (tablists) {
tablists.put(event.getPlayer(), new Tablist(event.getPlayer()));
}
}
@EventHandler
public void onServerConnection(ServerSwitchEvent event) {
synchronized (tablists) {
tablists.get(event.getPlayer()).onServerSwitch();
}
}
@EventHandler
public void onLeave(PlayerDisconnectEvent event) {
synchronized (tablists) {
tablists.remove(event.getPlayer());
Storage.directTabItems.remove(event.getPlayer());
}
}
public void disable() {
synchronized (tablists) {
tablists.forEach((player, tablist) -> tablist.disable());
tablists.clear();
}
}
private void updateTablist() {
List<TablistPart> subservers = new ArrayList<>();
for (ServerInfo server : ProxyServer.getInstance().getServersCopy().values()){
if(server.getPlayers().isEmpty())
continue;
Subserver subserver = Subserver.getSubserver(server);
if(fightInfos.containsKey(server))
subservers.add(new TablistServer(server, fightInfos.get(server)));
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()));
TablistPart global = new TablistGroup(subservers);
synchronized (tablists) {
tablists.forEach((player, tablist) -> tablist.update(global, seconds));
}
seconds++;
}
}

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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<Item> tablist, List<Item> 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));
}
}
}

Datei anzeigen

@ -0,0 +1,86 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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.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 List<Item> players;
public TablistServer(ServerInfo server) {
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, new ArrayList<>());
Collection<ProxiedPlayer> 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, List<Item> players) {
this.server = server;
this.players = players;
}
@Override
public String sortKey() {
return server.getName();
}
@Override
public void print(ChatSender viewer, ProxiedPlayer player, List<Item> tablist, List<Item> direct) {
boolean onServer = player.getServer().getInfo() == server;
List<Item> items = onServer ? direct : tablist;
if(!onServer) {
items.add(new Item(null, "", GRAY));
items.add(new Item(null, "§7§l" + server.getName(), LIGHT_GRAY));
}
items.addAll(players);
}
private void addPlayers(String prefix, List<Integer> teamPlayers, Collection<ProxiedPlayer> 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))));
}
}

Datei anzeigen

@ -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

Datei anzeigen

@ -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

Datei anzeigen

@ -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);

Datei anzeigen

@ -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)

Datei anzeigen

@ -2,4 +2,5 @@ name: BungeeCore
main: de.steamwar.bungeecore.BungeeCore
version: 1.0
author: Lixfel
depends: [PersistentBungeeCore, BungeeTabListPlus]
depends:
- PersistentBungeeCore