Archiviert
1
0

Untested full tablist

Signed-off-by: Lixfel <agga-games@gmx.de>
Dieser Commit ist enthalten in:
Lixfel 2022-06-18 13:17:23 +02:00
Ursprung 2e17c380f0
Commit f2ed575f8c
8 geänderte Dateien mit 487 neuen und 567 gelöschten Zeilen

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; 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.NetworkSender;
import de.steamwar.bungeecore.network.ServerMetaInfo; import de.steamwar.bungeecore.network.ServerMetaInfo;
import de.steamwar.bungeecore.tablist.TablistManager;
import de.steamwar.network.packets.PacketHandler; import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.common.FightInfoPacket; import de.steamwar.network.packets.common.FightInfoPacket;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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<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("")), "never", "never", 21, (byte)0x00, names);
private final Map<UUID, PlayerListItem.Item> directlySent = new HashMap<>(); //TODO Persist over softreload
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);
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<>();
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<UUID> 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<PlayerListItem.Item> add = new ArrayList<>();
List<PlayerListItem.Item> 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<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();
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<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);
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<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,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

@ -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 <https://www.gnu.org/licenses/>.
*/
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<PacketWrapper> {
//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<Object> 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<String> 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);
}
}

Datei anzeigen

@ -20,43 +20,29 @@
package de.steamwar.bungeecore.tablist; package de.steamwar.bungeecore.tablist;
import de.steamwar.bungeecore.BungeeCore; 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.listeners.BasicListener;
import de.steamwar.bungeecore.sql.SteamwarUser; import de.steamwar.network.packets.common.FightInfoPacket;
import de.steamwar.messages.ChatSender;
import net.md_5.bungee.ServerConnection;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ServerSwitchEvent; 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.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.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class TablistManager extends BasicListener { public class TablistManager extends BasicListener {
public static final String TAB_TEAM = "»SW-Tab"; private static final Map<ServerInfo, FightInfoPacket> 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)))}; public static synchronized void newFightInfo(ServerInfo info, FightInfoPacket packet) {
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)))}; fightInfos.put(info, packet);
fightInfos.keySet().removeIf(serverInfo -> serverInfo.getPlayers().isEmpty());
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<ProxiedPlayer, Tablist> tablists = new HashMap<>(); private final Map<ProxiedPlayer, Tablist> tablists = new HashMap<>();
@ -76,8 +62,9 @@ public class TablistManager extends BasicListener {
@EventHandler @EventHandler
public void onServerConnection(ServerSwitchEvent event) { public void onServerConnection(ServerSwitchEvent event) {
ServerConnection server = (ServerConnection) event.getPlayer().getServer(); synchronized (tablists) {
server.getCh().getHandle().pipeline().addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", new TablistListener(event.getPlayer(), server)); tablists.get(event.getPlayer()).onServerSwitch();
}
} }
@EventHandler @EventHandler
@ -95,110 +82,30 @@ public class TablistManager extends BasicListener {
} }
private void updateTablist() { private void updateTablist() {
List<TablistPart.Item> buildPlayers = new ArrayList<>();
List<TablistPart> 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) { synchronized (tablists) {
tablists.forEach((player, tablist) -> tablist.update()); tablists.forEach((player, tablist) -> tablist.update(global, seconds));
}
seconds++; 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));
}
}
} }

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,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 <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.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<ChatSender, String> title;
private final List<Item> 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<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, String sortKey, Function<ChatSender, String> title, List<Item> 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<Item> tablist, List<Item> direct) {
boolean onServer = player.getServer().getInfo() == server;
List<Item> 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<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))));
}
}