diff --git a/Persistent/build.gradle b/Persistent/build.gradle new file mode 100644 index 0000000..c52ad18 --- /dev/null +++ b/Persistent/build.gradle @@ -0,0 +1,66 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 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 . + */ + +plugins { + id 'java' +} + +group 'de.steamwar' +version '' + +compileJava.options.encoding = 'UTF-8' + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +sourceSets { + main { + java { + srcDirs = ['src/'] + include '**/*.java', '**/*.kt' + } + resources { + srcDirs = ['src/'] + exclude '**/*.java', '**/*.kt' + } + } +} + +repositories { + maven { + url = 'https://repo.fvdh.dev/releases' + content { + includeGroup 'net.frankheijden.serverutils' + } + } +} + +dependencies { + compileOnly 'org.projectlombok:lombok:1.18.22' + testCompileOnly 'org.projectlombok:lombok:1.18.22' + annotationProcessor 'org.projectlombok:lombok:1.18.22' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.22' + + compileOnly 'de.steamwar:velocity:RELEASE' + annotationProcessor 'com.velocitypowered:velocity-api:3.3.0-SNAPSHOT' + + compileOnly 'net.frankheijden.serverutils:ServerUtils:3.5.4' +} diff --git a/Persistent/src/de/steamwar/bungeecore/Arenaserver.java b/Persistent/src/de/steamwar/bungeecore/Arenaserver.java new file mode 100644 index 0000000..37bd9db --- /dev/null +++ b/Persistent/src/de/steamwar/bungeecore/Arenaserver.java @@ -0,0 +1,18 @@ +package de.steamwar.bungeecore; + +import lombok.Getter; + +@Getter +public class Arenaserver extends Subserver { + + private final String mode; + private final String map; + private final boolean allowMerge; + + public Arenaserver(String serverName, String mode, String map, boolean allowMerge, int port, ProcessBuilder processBuilder, Runnable shutdownCallback) { + super(Servertype.ARENA, serverName, port, processBuilder, shutdownCallback, null); + this.mode = mode; + this.map = map; + this.allowMerge = allowMerge; + } +} diff --git a/Persistent/src/de/steamwar/bungeecore/Bauserver.java b/Persistent/src/de/steamwar/bungeecore/Bauserver.java new file mode 100644 index 0000000..c5876f0 --- /dev/null +++ b/Persistent/src/de/steamwar/bungeecore/Bauserver.java @@ -0,0 +1,65 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +@Getter +public class Bauserver extends Subserver { + private static final Map servers = new HashMap<>(); + public static Bauserver get(UUID owner) { + synchronized (servers) { + return servers.get(owner); + } + } + + private final UUID owner; + + public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback){ + this(serverName, owner, port, processBuilder, shutdownCallback, null); + } + + public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer failureCallback){ + super(Servertype.BAUSERVER, serverName, port, processBuilder, shutdownCallback, failureCallback); + this.owner = owner; + + synchronized (servers) { + servers.put(owner, this); + } + } + + @Override + protected void register() { + super.register(); + } + + @Override + protected void unregister() { + synchronized (servers) { + servers.remove(owner); + } + super.unregister(); + } +} diff --git a/Persistent/src/de/steamwar/bungeecore/Builderserver.java b/Persistent/src/de/steamwar/bungeecore/Builderserver.java new file mode 100644 index 0000000..c085bb9 --- /dev/null +++ b/Persistent/src/de/steamwar/bungeecore/Builderserver.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; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +@Getter +public class Builderserver extends Subserver { + + private static final Map servers = new HashMap<>(); + public static Builderserver get(String map) { + synchronized (servers) { + return servers.get(map); + } + } + + private final String map; + public Builderserver(String serverName, String map, int port, ProcessBuilder processBuilder, Runnable shutdownCallback){ + this(serverName, map, port, processBuilder, shutdownCallback, null); + } + + public Builderserver(String serverName, String map, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer failureCallback){ + super(Servertype.BUILDER, serverName, port, processBuilder, shutdownCallback, failureCallback); + this.map = map; + + synchronized (servers) { + servers.put(map, this); + } + } + + @Override + protected void register() { + super.register(); + } + + @Override + protected void unregister() { + synchronized (servers) { + servers.remove(map); + } + super.unregister(); + } +} diff --git a/Persistent/src/de/steamwar/bungeecore/Persistent.java b/Persistent/src/de/steamwar/bungeecore/Persistent.java new file mode 100644 index 0000000..335e972 --- /dev/null +++ b/Persistent/src/de/steamwar/bungeecore/Persistent.java @@ -0,0 +1,81 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore; + +import com.google.inject.Inject; +import com.velocitypowered.api.command.BrigadierCommand; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; +import com.velocitypowered.api.plugin.Dependency; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.ProxyServer; +import lombok.Getter; +import net.frankheijden.serverutils.velocity.ServerUtils; + +import java.nio.file.Path; +import java.util.logging.Logger; + +@Plugin( + id = "persistentvelocitycore", + name = "PersistentVelocityCore", + dependencies = { @Dependency(id = "serverutils") } +) +public class Persistent { + + @Getter + private static Persistent instance; + + @Inject + public Persistent(ProxyServer proxyServer, Logger logger, @DataDirectory Path dataDirectory) { + instance = this; + this.proxyServer = proxyServer; + this.logger = logger; + } + + @Getter + private final ProxyServer proxyServer; + + @Getter + private final Logger logger; + + @Subscribe + public void onEnable(ProxyInitializeEvent event) { + proxyServer.getCommandManager().register( + new BrigadierCommand( + BrigadierCommand.literalArgumentBuilder("softreload") + .requires(commandSource -> commandSource.hasPermission("bungeecore.softreload")) + .executes(commandContext -> softreload()) + .build() + ) + ); + } + + @Subscribe + public void onDisable(ProxyShutdownEvent event) { + Subserver.shutdown(); + } + + public int softreload() { + ServerUtils.getInstance().getPlugin().getPluginManager().reloadPlugin("VelocityCore"); + return 1; + } +} diff --git a/Persistent/src/de/steamwar/bungeecore/Servertype.java b/Persistent/src/de/steamwar/bungeecore/Servertype.java new file mode 100644 index 0000000..06e6663 --- /dev/null +++ b/Persistent/src/de/steamwar/bungeecore/Servertype.java @@ -0,0 +1,26 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore; + +public enum Servertype { + BAUSERVER, + ARENA, + BUILDER +} diff --git a/Persistent/src/de/steamwar/bungeecore/Storage.java b/Persistent/src/de/steamwar/bungeecore/Storage.java new file mode 100644 index 0000000..bd71641 --- /dev/null +++ b/Persistent/src/de/steamwar/bungeecore/Storage.java @@ -0,0 +1,52 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore; + +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; +import lombok.experimental.UtilityClass; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@UtilityClass +public class Storage { + public static final Map> challenges = new HashMap<>(); + + public static final Map lastChats = new HashMap<>(); + + public static final Map> teamInvitations = new HashMap<>(); // UserID -> List + + public static final Map sessions = new HashMap<>(); // Contains session start timestamp + + public static final Map eventServer = new HashMap<>(); // TeamID -> Subserver map + + public static final Map fabricCheckedPlayers = new HashMap<>(); + + public static final Map fabricExpectPluginMessage = new HashMap<>(); + + public static final Map teamServers = new HashMap<>(); // TeamID -> ServerInfo map + + public static final Map> directTabItems = new HashMap<>(); +} diff --git a/Persistent/src/de/steamwar/bungeecore/Subserver.java b/Persistent/src/de/steamwar/bungeecore/Subserver.java new file mode 100644 index 0000000..830608c --- /dev/null +++ b/Persistent/src/de/steamwar/bungeecore/Subserver.java @@ -0,0 +1,268 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.bungeecore; + +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.proxy.server.ServerInfo; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +import java.io.*; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.logging.Logger; + +@SuppressWarnings("unused") +public class Subserver { + + private static final Component PREFIX = Component + .text("Steam").color(NamedTextColor.YELLOW) + .append(Component.text("War» ").color(NamedTextColor.DARK_GRAY)); + + private static final Logger logger = Persistent.getInstance().getLogger(); + + @Getter + private static final List serverList = new LinkedList<>(); + private static final Map infoToServer = new HashMap<>(); + + public static Subserver getSubserver(Player p) { + synchronized (serverList) { + for (int i = serverList.size() - 1; i >= 0; i--) { + if (serverList.get(i).onServer(p)) + return serverList.get(i); + } + } + return null; + } + + public static Subserver getSubserver(ServerInfo server) { + synchronized (serverList) { + return infoToServer.get(server); + } + } + + static void shutdown() { + while (!serverList.isEmpty()) { + Subserver server = serverList.get(0); + server.stop(); + } + } + + private final String serverName; + private final boolean checkpoint; + private final Runnable shutdownCallback; + private final Consumer failureCallback; + private final Process process; + private final PrintWriter writer; + @Getter + private final ServerInfo server; + @Getter + private RegisteredServer registeredServer; + @Getter + private final Servertype type; + private final Thread thread; + @Getter + private boolean started; + + private final List cachedPlayers = new LinkedList<>(); + @Getter + private final Map tablistNames = new HashMap<>(); + + protected Subserver(Servertype type, String serverName, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer failureCallback) { + this.started = false; + this.serverName = serverName; + this.type = type; + this.shutdownCallback = shutdownCallback; + this.failureCallback = failureCallback == null ? this::fatalError : failureCallback; + this.checkpoint = processBuilder.command().contains("criu"); + + try { + this.process = processBuilder.start(); + } catch (IOException e) { + throw new SecurityException("Server could not be started", e); + } + + InetSocketAddress address = new InetSocketAddress("127.0.0.1", port); + this.server = new ServerInfo(serverName, address); + this.writer = new PrintWriter(process.getOutputStream(), true); + + this.thread = new Thread(this::run, "Subserver " + serverName); + this.thread.start(); + } + + @Deprecated(forRemoval = true) + public boolean hasStarted() { + return started; + } + + public void sendPlayer(Player p) { + if (!started) { + p.sendActionBar(generateBar(0)); + cachedPlayers.add(p); + } else { + p.createConnectionRequest(registeredServer).connect(); + } + } + + public void execute(String command) { + writer.println(command); + } + + public void stop() { + try { + long pid = process.pid(); + if (checkpoint) + pid = process.children().findAny().map(ProcessHandle::pid).orElse(pid); + + Runtime.getRuntime().exec(new String[]{"kill", "-SIGUSR1", Long.toString(pid)}); + } catch (IOException e) { + logger.log(Level.SEVERE, "Failed to send SIGUSR1 to subserver.", e); + } + + try { + if (!process.waitFor(1, TimeUnit.MINUTES)) { + logger.log(Level.SEVERE, () -> serverName + " did not stop correctly, forcibly stopping!"); + process.destroyForcibly(); + } + + thread.join(); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Subserver stop interrupted!", e); + Thread.currentThread().interrupt(); + } + } + + private boolean onServer(Player p) { + return cachedPlayers.contains(p) || (registeredServer != null && registeredServer.getPlayersConnected().contains(p)); + } + + private void fatalError(Exception e) { + logger.log(Level.SEVERE, e, () -> serverName + " did not run correctly!"); + + for (Player cached : cachedPlayers) + cached.sendMessage(PREFIX.append(Component.text("Unexpected error during server startup.").color(NamedTextColor.RED))); + if (registeredServer != null) { + for (Player player : registeredServer.getPlayersConnected()) + player.sendMessage(PREFIX.append(Component.text("Lost connection to server.").color(NamedTextColor.RED))); + } + } + + private void start(InputStream stream, Predicate test) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { + String line = ""; + while (!started && (line = reader.readLine()) != null) { + started = test.test(line); + } + + if (line == null) + throw new IOException(serverName + " did not start correctly!"); + } + } + + protected void register() { + if (Persistent.getInstance().getProxyServer().getServer(serverName).isPresent()) { + SecurityException e = new SecurityException("Server already registered: " + serverName); + stop(); + failureCallback.accept(e); + throw e; + } + + synchronized (serverList) { + registeredServer = Persistent.getInstance().getProxyServer().registerServer(server); + serverList.add(this); + infoToServer.put(server, this); + } + } + + protected void unregister() { + synchronized (serverList) { + infoToServer.remove(server); + serverList.remove(this); + Persistent.getInstance().getProxyServer().unregisterServer(server); + registeredServer = null; + } + } + + private void run() { + register(); + + Exception ex = null; + try { + if (checkpoint) { + start(process.getErrorStream(), line -> line.contains("Restore finished successfully.")); + } else { + start(process.getInputStream(), line -> { + if (line.contains("Loading libraries, please wait")) + sendProgress(2); + else if (line.contains("Starting Minecraft server on")) + sendProgress(4); + else if (line.contains("Preparing start region")) + sendProgress(6); + return line.contains("Finished mapping loading"); + }); + } + + if (!started) + return; + + sendProgress(8); + + Thread.sleep(300); + + sendProgress(10); + for (Player cachedPlayer : cachedPlayers) { + sendPlayer(cachedPlayer); + } + cachedPlayers.clear(); + + process.waitFor(); + } catch (IOException e) { + ex = e; + } catch (InterruptedException e) { + ex = e; + Thread.currentThread().interrupt(); + } finally { + unregister(); + shutdownCallback.run(); + if (ex != null) + failureCallback.accept(ex); + } + } + + private Component generateBar(int progress) { + return Component.text("⬛".repeat(Math.max(0, progress))).color(NamedTextColor.GOLD) + .append(Component.text("⬛".repeat(Math.max(0, 10 - progress))).color(NamedTextColor.DARK_GRAY)); + } + + private void sendProgress(int progress) { + Component tc = generateBar(progress); + for (Player cached : cachedPlayers) + cached.sendActionBar(tc); + } +} diff --git a/build.gradle b/build.gradle index 0d0f5dc..b5cd280 100644 --- a/build.gradle +++ b/build.gradle @@ -18,13 +18,6 @@ */ plugins { - // Adding the base plugin fixes the following gradle warnings in IntelliJ: - // - // Warning: root project 'module-work-multi': Unable to resolve all content root directories - // Details: java.lang.IllegalStateException: No value has been specified for this provider. - // - // Warning: root project 'module-work-multi': Unable to resolve additional project configuration. - // Details: java.lang.IllegalStateException: No value has been specified for this provider. id 'base' id 'java' id 'application' @@ -57,24 +50,26 @@ sourceSets { } } -repositories { - mavenCentral() - maven { - url 'https://repo.papermc.io/repository/maven-public/' - content { - includeGroup 'com.velocitypowered' +allprojects { + repositories { + mavenCentral() + maven { + url 'https://repo.papermc.io/repository/maven-public/' + content { + includeGroup 'com.velocitypowered' + } } - } - maven { - url 'https://m2.dv8tion.net/releases' - content { - includeGroup 'net.dv8tion' + maven { + url 'https://m2.dv8tion.net/releases' + content { + includeGroup 'net.dv8tion' + } } - } - maven { - url 'https://repo.lunarclient.dev' - content { - includeGroup 'com.lunarclient' + maven { + url 'https://repo.lunarclient.dev' + content { + includeGroup 'com.lunarclient' + } } } } @@ -96,9 +91,7 @@ dependencies { annotationProcessor 'com.velocitypowered:velocity-api:3.3.0-SNAPSHOT' compileOnly 'de.steamwar:velocity:RELEASE' - //compileOnly 'de.steamwar:persistentbungeecore:RELEASE' - // TODO: Monorepo? - compileOnly files('persistentvelocitycore.jar') + compileOnly project(":Persistent") implementation("net.dv8tion:JDA:4.4.0_352") { exclude module: 'opus-java' diff --git a/settings.gradle b/settings.gradle index aab2ecd..f18b899 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,4 +28,5 @@ pluginManagement { rootProject.name = 'VelocityCore' -include 'CommonCore' \ No newline at end of file +include 'CommonCore' +include 'Persistent' diff --git a/src/de/steamwar/bungeecore/ArenaMode.java b/src/de/steamwar/bungeecore/ArenaMode.java index caba2ed..132e6b5 100644 --- a/src/de/steamwar/bungeecore/ArenaMode.java +++ b/src/de/steamwar/bungeecore/ArenaMode.java @@ -21,16 +21,10 @@ package de.steamwar.bungeecore; import de.steamwar.sql.SchematicType; import lombok.Getter; -import org.yaml.snakeyaml.Yaml; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; import java.util.*; -import static de.steamwar.bungeecore.util.YamlUtils.*; - -public class ArenaMode { +public class ArenaMode extends GameModeConfig { private static final Random random = new Random(); @@ -50,18 +44,25 @@ public class ArenaMode { bySchemType.clear(); allModes.clear(); - File folder = new File(VelocityCore.get().getDataDirectory().getParent().toFile(), "FightSystem"); - for(File configFile : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().toList()) { - Map config; - try { - config = loadYaml(configFile); - } catch (IOException e) { - throw new SecurityException("Could not load SchematicTypes", e); + GameModeConfig.loadAll(ArenaMode.class, (file, mode) -> { + if(mode.getServer() == null) + return; + + mode.config = file.getName(); + mode.internalName = file.getName().replace(".yml", ""); + mode.getMaps().forEach(map -> mode.lowerToRealMapNames.put(map.toLowerCase(), map)); + if(mode.getGameName() == null) + mode.setGameName(mode.internalName); + + allModes.add(mode); + byInternal.put(mode.internalName, mode); + for(String name : mode.getServer().getChatNames()){ + byChat.put(name.toLowerCase(), mode); } - if(config.containsKey("Server")) - new ArenaMode(configFile.getName().replace(".yml", ""), config); - } + if(mode.getSchematic() != null && mode.getSchemType() != null) + bySchemType.put(SchematicType.fromDB(mode.getSchemType()), mode); + }); } public static ArenaMode getByChat(String name){ @@ -75,17 +76,8 @@ public class ArenaMode { public static List getAllChatNames(boolean historic) { List chatNames = new LinkedList<>(); for(ArenaMode mode : byInternal.values()){ - if(historic == mode.historic) - chatNames.addAll(mode.chatNames); - } - return chatNames; - } - - public static List getAllRankedChatNames(){ - List chatNames = new LinkedList<>(); - for(ArenaMode mode : byInternal.values()){ - if(mode.isRanked()) - chatNames.addAll(mode.chatNames); + if(historic == mode.isHistoric()) + chatNames.addAll(mode.getServer().getChatNames()); } return chatNames; } @@ -94,57 +86,16 @@ public class ArenaMode { return bySchemType.get(schemType); } - @Getter - private final String displayName; - @Getter - private final String folder; - private final List chatNames; - @Getter - private final String serverJar; - @Getter - private final String config; - @Getter - private final List maps; + private final Map lowerToRealMapNames = new HashMap<>(); - @Getter - private final boolean historic; - @Getter - private final String internalName; @Getter - private final boolean ranked; + private String internalName; @Getter - private final String schemType; - - private ArenaMode(String internalName, Map config) { - this.internalName = internalName; - this.config = internalName + ".yml"; - - this.displayName = get(config, "GameName", internalName); - Map server = get(config, "Server"); - - this.folder = get(server, "Folder"); - this.serverJar = get(server, "ServerJar"); - this.chatNames = get(server, "ChatNames"); - this.maps = get(server, "Maps"); - this.ranked = get(server, "Ranked", false); - this.historic = get(server, "Historic", false); - maps.forEach(map -> lowerToRealMapNames.put(map.toLowerCase(), map)); - - this.schemType = get(config, "Schematic.Type", "").toLowerCase(); - - allModes.add(this); - byInternal.put(internalName, this); - for(String name : chatNames){ - byChat.put(name.toLowerCase(), this); - } - - if(!this.schemType.isEmpty()) - bySchemType.put(SchematicType.fromDB(this.schemType), this); - } + private String config; public String hasMap(String map){ - for(String m : maps){ + for(String m : getMaps()) { if(m.equalsIgnoreCase(map)) return m; } @@ -152,7 +103,7 @@ public class ArenaMode { } public String getRandomMap(){ - return maps.get(random.nextInt(maps.size())); + return getMaps().get(random.nextInt(getMaps().size())); } public String convertToRealMapName(String map){ @@ -160,11 +111,11 @@ public class ArenaMode { } public String getChatName(){ - return chatNames.get(0); + return getServer().getChatNames().get(0); } public boolean withoutChatName(){ - return chatNames.isEmpty(); + return getServer().getChatNames().isEmpty(); } } diff --git a/src/de/steamwar/bungeecore/GameModeConfig.java b/src/de/steamwar/bungeecore/GameModeConfig.java new file mode 100644 index 0000000..6c0d17a --- /dev/null +++ b/src/de/steamwar/bungeecore/GameModeConfig.java @@ -0,0 +1,101 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 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; + +import lombok.Getter; +import lombok.Setter; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +@Getter +public class GameModeConfig { + + public static void loadAll(Class config, BiConsumer consumer) { + File folder = new File(VelocityCore.get().getDataDirectory().getParent().toFile(), "FightSystem"); + if(!folder.exists()) + return; + + Yaml yaml = new Yaml(new Constructor(config)); + for(File file : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().toList()) { + try { + consumer.accept(file, yaml.load(new FileInputStream(file))); + } catch (IOException e) { + throw new SecurityException("Could not load GameModeConfig", e); + } + } + } + + private Server Server = null; + private List CheckQuestions = Collections.emptyList(); + private String deadline = null; + private Schematic Schematic = null; + @Setter + private String GameName; + + @Getter + public static class Server { + private String Folder; + private String ServerJar; + private List ChatNames = Collections.emptyList(); + private List Maps; + private boolean Ranked = false; + private boolean Historic = false; + } + + @Getter + public static class Schematic { + private String Type; + private String Shortcut; + private String Material; + private boolean ManualCheck = true; + } + + public String getServerJar() { + return getServer().getServerJar(); + } + + public String getFolder() { + return getServer().getFolder(); + } + + public List getMaps() { + return getServer().getMaps(); + } + + public boolean isHistoric() { + return getServer().isHistoric(); + } + + public boolean isRanked() { + return getServer().isRanked(); + } + + public String getSchemType() { + return getSchematic().getType(); + } +} diff --git a/src/de/steamwar/bungeecore/ServerStarter.java b/src/de/steamwar/bungeecore/ServerStarter.java index 53240e0..541bfa1 100644 --- a/src/de/steamwar/bungeecore/ServerStarter.java +++ b/src/de/steamwar/bungeecore/ServerStarter.java @@ -53,7 +53,7 @@ public class ServerStarter { public ServerStarter arena(ArenaMode mode, String map) { portrange = ARENA_PORTS; - serverNameProvider = port -> mode.getDisplayName() + (port - portrange.start); + serverNameProvider = port -> mode.getGameName() + (port - portrange.start); serverJar = mode.getServerJar(); allowMerge = true; fightMap = map; diff --git a/src/de/steamwar/bungeecore/VelocityCore.java b/src/de/steamwar/bungeecore/VelocityCore.java index edef37c..0349dce 100644 --- a/src/de/steamwar/bungeecore/VelocityCore.java +++ b/src/de/steamwar/bungeecore/VelocityCore.java @@ -56,9 +56,7 @@ import java.util.logging.Logger; @Plugin( id = "velocitycore", name = "VelocityCore", - dependencies = { - @Dependency(id = "persistentvelocitycore") - } + dependencies = { @Dependency(id = "persistentvelocitycore") } ) public class VelocityCore { diff --git a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java index 5f1dc45..0ad54b0 100644 --- a/src/de/steamwar/bungeecore/commands/ChallengeCommand.java +++ b/src/de/steamwar/bungeecore/commands/ChallengeCommand.java @@ -51,7 +51,7 @@ public class ChallengeCommand extends SWCommand { challenges.remove(p); new ServerStarter().arena(mode, map).blueLeader(sender.getPlayer()).redLeader(target).callback( - arena -> Chatter.broadcast().system("CHALLENGE_BROADCAST", "CHALLENGE_BROADCAST_HOVER", ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getDisplayName(), p, target) + arena -> Chatter.broadcast().system("CHALLENGE_BROADCAST", "CHALLENGE_BROADCAST_HOVER", ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getGameName(), p, target) ).start(); }else{ if(!challenges.containsKey(p)){ @@ -60,8 +60,8 @@ public class ChallengeCommand extends SWCommand { challenges.get(p).add(target); - sender.system("CHALLENGE_CHALLENGED", target, mode.getDisplayName()); - Chatter.of(target).system("CHALLENGE_CHALLENGED_TARGET", p, mode.getDisplayName(), mode.getMaps().size() != 1 ? new Message("CHALLENGE_CHALLENGED_MAP", m) : ""); + sender.system("CHALLENGE_CHALLENGED", target, mode.getGameName()); + Chatter.of(target).system("CHALLENGE_CHALLENGED_TARGET", p, mode.getGameName(), mode.getMaps().size() != 1 ? new Message("CHALLENGE_CHALLENGED_MAP", m) : ""); Chatter.of(target).system("CHALLENGE_ACCEPT", new Message("CHALLENGE_ACCEPT_HOVER"), ClickEvent.runCommand("/challenge " + p.getUsername() + " " + mode.getChatName() + " " + m)); } diff --git a/src/de/steamwar/bungeecore/commands/FightCommand.java b/src/de/steamwar/bungeecore/commands/FightCommand.java index 10d928a..b131666 100644 --- a/src/de/steamwar/bungeecore/commands/FightCommand.java +++ b/src/de/steamwar/bungeecore/commands/FightCommand.java @@ -109,7 +109,7 @@ public class FightCommand extends SWCommand { declineMerge.run(sender, mode, map); }); Arenaserver finalMergable = mergable; - SWItem item = new SWItem(new Message("FIGHT_MERGE_INFO", mode.getDisplayName(), finalMergable.getMap()), 11); + SWItem item = new SWItem(new Message("FIGHT_MERGE_INFO", mode.getGameName(), finalMergable.getMap()), 11); item.addLore(new Message("FIGHT_MERGE_INFO_LORE_1", finalMergable.getRegisteredServer().getPlayersConnected().toArray(new Player[1])[0].getUsername())); inventory.addItem(4, item, click -> {}); inventory.addItem(8, new SWItem(new Message("FIGHT_MERGE_ACCEPT"), 10), click -> { @@ -127,7 +127,7 @@ public class FightCommand extends SWCommand { public void fight(@Validator("arenaPlayer") PlayerChatter sender, @Mapper("nonHistoricArenaMode") @OptionalValue("") @AllowNull ArenaMode arenaMode, @Mapper("arenaMap") @OptionalValue("") @AllowNull String map) { createArena(sender, "/fight ", true, arenaMode, map, false, (p, mode, m) -> new ServerStarter().arena(mode, m).blueLeader(p.getPlayer()).callback( - arena -> Chatter.broadcast().system("FIGHT_BROADCAST", "FIGHT_BROADCAST_HOVER", ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getDisplayName(), p.getPlayer().getUsername()) + arena -> Chatter.broadcast().system("FIGHT_BROADCAST", "FIGHT_BROADCAST_HOVER", ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getGameName(), p.getPlayer().getUsername()) ).start() ); } diff --git a/src/de/steamwar/bungeecore/commands/HistoricCommand.java b/src/de/steamwar/bungeecore/commands/HistoricCommand.java index 3db72d7..fb170f5 100644 --- a/src/de/steamwar/bungeecore/commands/HistoricCommand.java +++ b/src/de/steamwar/bungeecore/commands/HistoricCommand.java @@ -34,7 +34,7 @@ public class HistoricCommand extends SWCommand { @Register public void historic(@Validator("arenaPlayer") PlayerChatter player, @Mapper("historicArenaMode") @OptionalValue("") @AllowNull ArenaMode arenaMode, @Mapper("arenaMap") @OptionalValue("") @AllowNull String map) { FightCommand.createArena(player, "/historic ", true, arenaMode, map, true, (p, mode, m) -> new ServerStarter().arena(mode, m).blueLeader(p.getPlayer()).callback( - arena -> Chatter.broadcast().system("HISTORIC_BROADCAST", "HISTORIC_BROADCAST_HOVER", ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getDisplayName(), p.getPlayer()) + arena -> Chatter.broadcast().system("HISTORIC_BROADCAST", "HISTORIC_BROADCAST_HOVER", ClickEvent.runCommand("/arena " + arena.getServer().getName()), mode.getGameName(), p.getPlayer()) ).start()); } } diff --git a/src/de/steamwar/bungeecore/discord/util/DiscordRanks.java b/src/de/steamwar/bungeecore/discord/util/DiscordRanks.java index 4fcff4a..037b6d3 100644 --- a/src/de/steamwar/bungeecore/discord/util/DiscordRanks.java +++ b/src/de/steamwar/bungeecore/discord/util/DiscordRanks.java @@ -20,27 +20,30 @@ package de.steamwar.bungeecore.discord.util; import de.steamwar.bungeecore.discord.DiscordBot; -import de.steamwar.bungeecore.discord.DiscordConfig; import de.steamwar.sql.SteamwarUser; +import de.steamwar.sql.UserPerm; import lombok.experimental.UtilityClass; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.exceptions.ErrorResponseException; -import java.util.HashSet; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; + @UtilityClass public class DiscordRanks { + private final Map prefixToPermName = UserPerm.prefixes.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, entry -> entry.getKey().name())); + public void update(SteamwarUser user) { if (user.getDiscordId() == null) return; - Set swRoles = new HashSet<>(DiscordConfig.RANKS.values()); + Set swRoles = new HashSet<>(DiscordBot.getInstance().getConfig().getRanks().values()); Guild guild = DiscordBot.getGuild(); guild.retrieveMemberById(user.getDiscordId()).queue(member -> { - String prefixRole = DiscordConfig.RANKS.get(user.prefix()); + String prefixRole = DiscordBot.getInstance().getConfig().getRanks().get(prefixToPermName.get(user.prefix())); member.getRoles().stream() .filter(role -> swRoles.contains(role.getId())) .filter(role -> !role.getId().equals(prefixRole)) diff --git a/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java b/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java index e687788..796c565 100644 --- a/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java +++ b/src/de/steamwar/bungeecore/network/handlers/FightInfoHandler.java @@ -21,7 +21,6 @@ package de.steamwar.bungeecore.network.handlers; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.proxy.server.ServerInfo; import de.steamwar.bungeecore.network.NetworkSender; import de.steamwar.bungeecore.network.ServerMetaInfo; import de.steamwar.bungeecore.tablist.TablistManager; @@ -45,13 +44,13 @@ public class FightInfoHandler extends PacketHandler { @Handler public void handle(FightInfoPacket packet) { - ServerInfo info = ((ServerMetaInfo) packet.getMetaInfos()).getSender().getServerInfo(); + RegisteredServer server = ((ServerMetaInfo) packet.getMetaInfos()).getSender().getServer(); - FightInfoPacket lobbyPacket = packet.withServerName(info.getName()); + FightInfoPacket lobbyPacket = packet.withServerName(server.getServerInfo().getName()); - TablistManager.newFightInfo(info, packet); + TablistManager.newFightInfo(server, packet); - lobbys.removeIf(server -> server.getPlayersConnected().isEmpty()); + lobbys.removeIf(s -> s.getPlayersConnected().isEmpty()); lobbys.forEach(lobby -> NetworkSender.send(lobby, lobbyPacket)); } } diff --git a/src/de/steamwar/bungeecore/tablist/Tablist.java b/src/de/steamwar/bungeecore/tablist/Tablist.java index fb2e309..fbef8e8 100644 --- a/src/de/steamwar/bungeecore/tablist/Tablist.java +++ b/src/de/steamwar/bungeecore/tablist/Tablist.java @@ -19,46 +19,48 @@ package de.steamwar.bungeecore.tablist; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.util.GameProfile; +import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.network.Connections; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.packet.RemovePlayerInfoPacket; +import com.velocitypowered.proxy.protocol.packet.UpdateTeamsPacket; +import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; +import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import de.steamwar.bungeecore.Storage; +import de.steamwar.bungeecore.VelocityCore; import de.steamwar.messages.Chatter; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.MessageToMessageDecoder; -import net.md_5.bungee.ServerConnection; -import net.md_5.bungee.UserConnection; -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.netty.PipelineUtils; -import net.md_5.bungee.protocol.DefinedPacket; -import net.md_5.bungee.protocol.Either; -import net.md_5.bungee.protocol.PacketWrapper; -import net.md_5.bungee.protocol.ProtocolConstants; -import net.md_5.bungee.protocol.packet.*; +import io.netty.util.ReferenceCountUtil; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @ChannelHandler.Sharable -public class Tablist extends MessageToMessageDecoder { +public class Tablist extends ChannelInboundHandlerAdapter { - 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« " + String.format("%02d", i)).toArray(String[]::new); + private static final UUID[] swUuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); + private static final String[] swNames = IntStream.range(0, 80).mapToObj(i -> " »SW« " + String.format("%02d", i)).toArray(String[]::new); private static final String TAB_TEAM = "zzzzzsw-tab"; - private static final Team teamPacket = new Team(TAB_TEAM, (byte) 0, Either.right(TextComponent.fromLegacy("")), Either.right(TextComponent.fromLegacy("")), Either.right(TextComponent.fromLegacy("")), "never", "always", 21, (byte)0x00, names); + private static final UpdateTeamsPacket createTeamPacket = new UpdateTeamsPacket(TAB_TEAM, UpdateTeamsPacket.Mode.CREATE, Component.empty(), Component.empty(), Component.empty(), UpdateTeamsPacket.NameTagVisibility.NEVER, UpdateTeamsPacket.CollisionRule.ALWAYS, 21, (byte)0x00, Arrays.stream(swNames).toList()); - private final Map directTabItems; + private final Map directTabItems; private final Set npcs = new HashSet<>(); - private final List current = new ArrayList<>(); + private final List current = new ArrayList<>(); - private final ProxiedPlayer player; + private final Player player; private final Chatter viewer; - private ServerConnection connection; + private VelocityServerConnection connection; - public Tablist(ProxiedPlayer player) { + public Tablist(Player player) { this.player = player; this.viewer = Chatter.of(player); this.directTabItems = Storage.directTabItems.computeIfAbsent(player, p -> new HashMap<>()); @@ -69,7 +71,8 @@ public class Tablist extends MessageToMessageDecoder { if (connection == null) return; - player.setTabHeader(header(viewer, seconds), viewer.parse(false, "TABLIST_FOOTER", connection.getInfo().getName(), ping(), ProxyServer.getInstance().getPlayers().size())); + player.sendPlayerListHeaderAndFooter(header(viewer, seconds), viewer.parse(false, "TABLIST_FOOTER", connection.getServerInfo().getName(), ping(), VelocityCore.getProxy().getPlayerCount())); + player.getTabList(); List tablist = new ArrayList<>(); List direct = new ArrayList<>(); @@ -78,28 +81,28 @@ public class Tablist extends MessageToMessageDecoder { // NPC handling List addNpc = new ArrayList<>(); List removeNpc = new ArrayList<>(); - List update = new ArrayList<>(); + List update = new ArrayList<>(); Set nonNPCs = direct.stream().map(TablistPart.Item::getUuid).collect(Collectors.toSet()); synchronized (directTabItems) { for (TablistPart.Item item : direct) { - PlayerListItem.Item tabItem = directTabItems.get(item.getUuid()); + UpsertPlayerInfoPacket.Entry tabItem = directTabItems.get(item.getUuid()); if(npcs.remove(item.getUuid())) - removeNpc.add(tabItem.getUsername()); + removeNpc.add(tabItem.getProfile().getName()); if(tabItem == null) { tablist.add(0, item); - } else if(!tabItem.getDisplayName().equals(item.getDisplayName())) { - tabItem.setDisplayName(item.getDisplayName()); + } else if(!item.getDisplayName().equals(getDisplayName(tabItem))) { + tabItem.setDisplayName(new ComponentHolder(player.getProtocolVersion(), item.getDisplayName())); tabItem.setListed(true); update.add(tabItem); } } - boolean playerNotOnTeamserver = !Storage.teamServers.containsValue(player.getServer().getInfo()); - for(PlayerListItem.Item item : directTabItems.values()) { - if(playerNotOnTeamserver && !nonNPCs.contains(item.getUuid()) && !npcs.contains(item.getUuid()) && !player.getUniqueId().equals(item.getUuid())) { - npcs.add(item.getUuid()); - addNpc.add(item.getUsername()); + boolean playerNotOnTeamserver = !Storage.teamServers.containsValue(player.getCurrentServer().orElseThrow().getServerInfo()); + for(UpsertPlayerInfoPacket.Entry item : directTabItems.values()) { + if(playerNotOnTeamserver && !nonNPCs.contains(item.getProfileId()) && !npcs.contains(item.getProfileId()) && !player.getUniqueId().equals(item.getProfileId())) { + npcs.add(item.getProfileId()); + addNpc.add(item.getProfile().getName()); } } } @@ -108,32 +111,31 @@ public class Tablist extends MessageToMessageDecoder { // Main list handling int i = 0; - List add = new ArrayList<>(); - List remove = new ArrayList<>(); + List add = new ArrayList<>(); + List remove = new ArrayList<>(); for (; i < tablist.size() && i < 80; i++) { - PlayerListItem.Item tabItem; + TablistPart.Item item = tablist.get(i); + UpsertPlayerInfoPacket.Entry 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 = new UpsertPlayerInfoPacket.Entry(swUuids[i]); + tabItem.setProfile(new GameProfile(swUuids[i], swNames[i], Collections.emptyList())); + tabItem.setGameMode(1); tabItem.setListed(true); - tabItem.setPing(1000); + tabItem.setLatency(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()); + if(!tabItem.getProfile().getProperties().equals(item.getProperties())) { + tabItem.setProfile(new GameProfile(tabItem.getProfile().getId(), tabItem.getProfile().getName(), item.getProperties())); + tabItem.setDisplayName(new ComponentHolder(player.getProtocolVersion(), item.getDisplayName())); add.add(tabItem); if(current.size() > i) { remove.add(tabItem); } - } else if(!item.getDisplayName().equals(tabItem.getDisplayName())) { - tabItem.setDisplayName(item.getDisplayName()); + } else if(!item.getDisplayName().equals(getDisplayName(tabItem))) { + tabItem.setDisplayName(new ComponentHolder(player.getProtocolVersion(), item.getDisplayName())); update.add(tabItem); } } @@ -143,208 +145,127 @@ public class Tablist extends MessageToMessageDecoder { remove.add(current.remove(i)); } - sendTabPacket(remove, PlayerListItem.Action.REMOVE_PLAYER); - sendTabPacket(update, PlayerListItem.Action.UPDATE_DISPLAY_NAME); - sendTabPacket(add, PlayerListItem.Action.ADD_PLAYER); + sendTabPacket(remove, null); + sendTabPacket(update, UpsertPlayerInfoPacket.Action.UPDATE_DISPLAY_NAME); + sendTabPacket(add, UpsertPlayerInfoPacket.Action.ADD_PLAYER); } public void onServerSwitch() { injection(); synchronized (directTabItems) { - sendNpcPacket(npcs.stream().map(npc -> directTabItems.get(npc).getUsername()).collect(Collectors.toList()), true); + sendNpcPacket(npcs.stream().map(npc -> directTabItems.get(npc).getProfile().getName()).toList(), true); directTabItems.clear(); npcs.clear(); } } private void injection() { - connection = (ServerConnection) player.getServer(); + connection = (VelocityServerConnection) player.getCurrentServer().orElse(null); + if(connection == null) + return; - if(connection != null) { - ChannelPipeline pipeline = connection.getCh().getHandle().pipeline(); - if(pipeline.get("steamwar-tablist") != null) //Catch unclean exit - pipeline.remove("steamwar-tablist"); + ChannelPipeline pipeline = connection.getConnection().getChannel().pipeline(); + if(pipeline.get("steamwar-tablist") != null) //Catch unclean exit + pipeline.remove("steamwar-tablist"); - pipeline.addBefore(PipelineUtils.BOSS_HANDLER, "steamwar-tablist", this); - sendQueued(player, teamPacket); - } + pipeline.addBefore(Connections.HANDLER, "steamwar-tablist", this); + sendPacket(player, createTeamPacket); } public void disable() { - sendTabPacket(current, PlayerListItem.Action.REMOVE_PLAYER); + sendTabPacket(current, null); current.clear(); synchronized (directTabItems) { - sendNpcPacket(npcs.stream().map(npc -> directTabItems.get(npc).getUsername()).collect(Collectors.toList()), true); + sendNpcPacket(npcs.stream().map(npc -> directTabItems.get(npc).getProfile().getName()).collect(Collectors.toList()), true); npcs.clear(); } if(connection != null) - connection.getCh().getHandle().pipeline().remove(this); + connection.getConnection().getChannel().pipeline().remove(this); } @Override - protected void decode(ChannelHandlerContext ctx, PacketWrapper packetWrapper, List out) { - if(!connection.isObsolete()) { - DefinedPacket packet = packetWrapper.packet; - - if(packet instanceof PlayerListHeaderFooter) { - packetWrapper.trySingleRelease(); + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if(msg instanceof UpsertPlayerInfoPacket packet) { + packet.getActions().remove(UpsertPlayerInfoPacket.Action.INITIALIZE_CHAT); + packet.getActions().remove(UpsertPlayerInfoPacket.Action.UPDATE_LATENCY); + packet.getActions().remove(UpsertPlayerInfoPacket.Action.UPDATE_DISPLAY_NAME); + packet.getActions().remove(UpsertPlayerInfoPacket.Action.UPDATE_LISTED); + if(packet.getActions().isEmpty()) { + ReferenceCountUtil.release(msg); return; } - if(packet instanceof PlayerListItem) { - PlayerListItem list = (PlayerListItem) packet; - PlayerListItem.Action action = list.getAction(); + if(packet.containsAction(UpsertPlayerInfoPacket.Action.ADD_PLAYER)) { + for (UpsertPlayerInfoPacket.Entry entry : packet.getEntries()) { + entry.setLatency(1); + entry.setChatSession(null); - 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: - boolean playerNotOnTeamserver = !Storage.teamServers.containsValue(player.getServer().getInfo()); - for (PlayerListItem.Item item : list.getItems()) { - item.setPing(1); - if (playerNotOnTeamserver) { - item.setDisplayName(new TextComponent()); - item.setListed(false); - } else if (item.getDisplayName() == null) { - item.setDisplayName(TextComponent.fromLegacy("§7" + item.getUsername())); - } - item.setPublicKey(null); - if(!player.getUniqueId().equals(item.getUuid()) && item.getGamemode() == 3) - item.setGamemode(1); + if (!Storage.teamServers.containsValue(connection.getServerInfo())) { + entry.setDisplayName(new ComponentHolder(player.getProtocolVersion(), Component.empty())); + entry.setListed(false); + } else if (entry.getDisplayName() == null) { + entry.setDisplayName(new ComponentHolder(player.getProtocolVersion(), Component.text(entry.getProfile().getName()).color(NamedTextColor.GRAY))); + } - synchronized (directTabItems) { - directTabItems.put(item.getUuid(), item); - } - } - break; - case REMOVE_PLAYER: - List names = new ArrayList<>(); - for(PlayerListItem.Item item : list.getItems()) { - synchronized (directTabItems) { - PlayerListItem.Item directItem = directTabItems.remove(item.getUuid()); - if(npcs.remove(item.getUuid())) - names.add(directItem.getUsername()); - } - } - sendNpcPacket(names, true); - break; - } - } else if(packet instanceof PlayerListItemRemove) { - List names = new ArrayList<>(); - for(UUID uuid : ((PlayerListItemRemove) packet).getUuids()) { synchronized (directTabItems) { - PlayerListItem.Item directItem = directTabItems.remove(uuid); - if(npcs.remove(uuid)) - names.add(directItem.getUsername()); - } - } - sendNpcPacket(names, true); - } else if(packet instanceof PlayerListItemUpdate) { - PlayerListItemUpdate list = (PlayerListItemUpdate) packet; - EnumSet actions = list.getActions(); - actions.remove(PlayerListItemUpdate.Action.INITIALIZE_CHAT); - actions.remove(PlayerListItemUpdate.Action.UPDATE_LATENCY); - actions.remove(PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME); - actions.remove(PlayerListItemUpdate.Action.UPDATE_LISTED); - if(actions.isEmpty()) { - packetWrapper.trySingleRelease(); - return; - } - - boolean playerNotOnTeamserver = !Storage.teamServers.containsValue(player.getServer().getInfo()); - for(PlayerListItemUpdate.Action action : actions) { - switch (action) { - case ADD_PLAYER: - for (PlayerListItem.Item item : list.getItems()) { - item.setPing(1); - if (playerNotOnTeamserver) { - item.setDisplayName(new TextComponent()); - item.setListed(false); - } else if (item.getDisplayName() == null) { - item.setDisplayName(TextComponent.fromLegacy("§7" + item.getUsername())); - } - item.setPublicKey(null); - if(!player.getUniqueId().equals(item.getUuid()) && item.getGamemode() == 3) - item.setGamemode(1); - - synchronized (directTabItems) { - directTabItems.put(item.getUuid(), item); - } - } - break; - 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 UPDATE_LISTED: - case INITIALIZE_CHAT: - case UPDATE_LATENCY: - case UPDATE_DISPLAY_NAME: - break; + directTabItems.put(entry.getProfileId(), entry); } } - if(actions.contains(PlayerListItemUpdate.Action.ADD_PLAYER)) { - actions.add(PlayerListItemUpdate.Action.UPDATE_LISTED); - actions.add(PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME); - actions.add(PlayerListItemUpdate.Action.UPDATE_LATENCY); - actions.add(PlayerListItemUpdate.Action.UPDATE_GAMEMODE); + packet.addAction(UpsertPlayerInfoPacket.Action.UPDATE_LISTED); + packet.addAction(UpsertPlayerInfoPacket.Action.UPDATE_LATENCY); + packet.addAction(UpsertPlayerInfoPacket.Action.UPDATE_DISPLAY_NAME); + packet.addAction(UpsertPlayerInfoPacket.Action.UPDATE_GAME_MODE); + } + + if(packet.containsAction(UpsertPlayerInfoPacket.Action.UPDATE_GAME_MODE)) { + for(UpsertPlayerInfoPacket.Entry entry : packet.getEntries()) { + if(!entry.getProfileId().equals(player.getUniqueId()) && entry.getGameMode() == 3) + entry.setGameMode(1); } } - } - out.add(packetWrapper); - } - - private boolean oldTab() { - return player.getPendingConnection().getVersion() < ProtocolConstants.MINECRAFT_1_19_3; - } - - private void sendTabPacket(List items, PlayerListItem.Action action) { //Breaks in 1.19.3 if action != UPDATE_DISPLAY_NAME, ADD_PLAYER or REMOVE_PLAYER - if(!items.isEmpty()) { - if(oldTab()) { - PlayerListItem packet = new PlayerListItem(); - packet.setAction(action); - packet.setItems(items.toArray(new PlayerListItem.Item[0])); - sendQueued(player, packet); - } else if(action == PlayerListItem.Action.REMOVE_PLAYER) { - PlayerListItemRemove packet = new PlayerListItemRemove(); - packet.setUuids(items.stream().map(PlayerListItem.Item::getUuid).toArray(UUID[]::new)); - sendQueued(player, packet); - } else { - PlayerListItemUpdate packet = new PlayerListItemUpdate(); - packet.setActions(action == PlayerListItem.Action.UPDATE_DISPLAY_NAME ? EnumSet.of(PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME, PlayerListItemUpdate.Action.UPDATE_LISTED) : EnumSet.of(PlayerListItemUpdate.Action.ADD_PLAYER, PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME, PlayerListItemUpdate.Action.UPDATE_LATENCY, PlayerListItemUpdate.Action.UPDATE_LISTED, PlayerListItemUpdate.Action.UPDATE_GAMEMODE)); - packet.setItems(items.toArray(new PlayerListItem.Item[0])); - sendQueued(player, packet); + } else if(msg instanceof RemovePlayerInfoPacket packet) { + List names = new ArrayList<>(); + for(UUID uuid : packet.getProfilesToRemove()) { + synchronized (directTabItems) { + UpsertPlayerInfoPacket.Entry directItem = directTabItems.remove(uuid); + if(npcs.remove(uuid)) + names.add(directItem.getProfile().getName()); + } } + sendNpcPacket(names, true); } + + ctx.fireChannelRead(msg); + } + + private void sendTabPacket(List items, UpsertPlayerInfoPacket.Action action) { //Breaks in 1.19.3 if action != UPDATE_DISPLAY_NAME, ADD_PLAYER or REMOVE_PLAYER + if(items.isEmpty()) + return; + + if(action == null) { //REMOVE + sendPacket(player, new RemovePlayerInfoPacket(items.stream().map(item -> item.getProfile().getId()).toList())); + return; + } + + sendPacket(player, new UpsertPlayerInfoPacket( + action == UpsertPlayerInfoPacket.Action.UPDATE_DISPLAY_NAME ? EnumSet.of(UpsertPlayerInfoPacket.Action.UPDATE_DISPLAY_NAME, UpsertPlayerInfoPacket.Action.UPDATE_LISTED) : EnumSet.of(UpsertPlayerInfoPacket.Action.ADD_PLAYER, UpsertPlayerInfoPacket.Action.UPDATE_DISPLAY_NAME, UpsertPlayerInfoPacket.Action.UPDATE_LATENCY, UpsertPlayerInfoPacket.Action.UPDATE_LISTED, UpsertPlayerInfoPacket.Action.UPDATE_GAME_MODE), + items + )); } private void sendNpcPacket(List names, boolean remove) { if(!names.isEmpty()) { - Team packet = new Team(TAB_TEAM); - packet.setMode((byte) (remove ? 4 : 3)); - packet.setPlayers(names.toArray(new String[0])); - sendQueued(player, packet); + UpdateTeamsPacket packet = new UpdateTeamsPacket(); + packet.setMode(remove ? UpdateTeamsPacket.Mode.REMOVE_PLAYER : UpdateTeamsPacket.Mode.ADD_PLAYER); + packet.setPlayers(names); + sendPacket(player, packet); } } - private BaseComponent header(Chatter p, int seconds) { + private Component header(Chatter p, int seconds) { int phase = (seconds % 10) / 5; if (phase == 0) return p.parse(false, "TABLIST_PHASE_DISCORD"); @@ -353,7 +274,7 @@ public class Tablist extends MessageToMessageDecoder { } private String ping() { - int ping = player.getPing(); + long ping = player.getPing(); if (ping < 50) { return "§a" + ping; } else if (ping < 150) { @@ -363,7 +284,15 @@ public class Tablist extends MessageToMessageDecoder { } } - private static void sendQueued(ProxiedPlayer player, DefinedPacket packet) { - ((UserConnection)player).sendPacketQueued(packet); + private Component getDisplayName(UpsertPlayerInfoPacket.Entry entry) { + ComponentHolder displayName = entry.getDisplayName(); + if(displayName == null) + return null; + + return displayName.getComponent(); + } + + private static void sendPacket(Player player, MinecraftPacket packet) { + ((ConnectedPlayer) player).getConnection().write(packet); } } diff --git a/src/de/steamwar/bungeecore/tablist/TablistBuild.java b/src/de/steamwar/bungeecore/tablist/TablistBuild.java index 15f6058..55aae44 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistBuild.java +++ b/src/de/steamwar/bungeecore/tablist/TablistBuild.java @@ -19,12 +19,12 @@ package de.steamwar.bungeecore.tablist; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; import de.steamwar.bungeecore.Servertype; import de.steamwar.bungeecore.Subserver; +import de.steamwar.bungeecore.VelocityCore; import de.steamwar.messages.Chatter; -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; @@ -34,19 +34,19 @@ import java.util.stream.Collectors; public class TablistBuild implements TablistPart { - private final List servers = new ArrayList<>(); - private final Map> players = new HashMap<>(); + private final List servers = new ArrayList<>(); + private final Map> players = new HashMap<>(); public TablistBuild() { - for (ServerInfo server : new ArrayList<>(ProxyServer.getInstance().getServers().values())){ - Subserver subserver = Subserver.getSubserver(server); - if(server.getPlayers().isEmpty() || subserver == null || subserver.getType() != Servertype.BAUSERVER) + for (RegisteredServer server : VelocityCore.getProxy().getAllServers()){ + Subserver subserver = Subserver.getSubserver(server.getServerInfo()); + if(server.getPlayersConnected().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())); + players.put(server, server.getPlayersConnected().stream().sorted(((p1, p2) -> p1.getUsername().compareToIgnoreCase(p2.getUsername()))).map(Item::new).collect(Collectors.toList())); } - servers.sort((s1, s2) -> s1.getName().compareToIgnoreCase(s2.getName())); + servers.sort((s1, s2) -> s1.getServerInfo().getName().compareToIgnoreCase(s2.getServerInfo().getName())); } @Override @@ -55,14 +55,14 @@ public class TablistBuild implements TablistPart { } @Override - public void print(Chatter viewer, ProxiedPlayer player, List tablist, List direct) { - ServerInfo server = player.getServer().getInfo(); + public void print(Chatter viewer, Player player, List tablist, List direct) { + RegisteredServer server = player.getCurrentServer().orElseThrow().getServer(); 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) { + for (RegisteredServer info : servers) { TablistServer.teamify(players.get(info), player) .forEach(((server == info) ? direct : tablist)::add); } diff --git a/src/de/steamwar/bungeecore/tablist/TablistGroup.java b/src/de/steamwar/bungeecore/tablist/TablistGroup.java index 0ff2976..0331a7b 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistGroup.java +++ b/src/de/steamwar/bungeecore/tablist/TablistGroup.java @@ -19,8 +19,8 @@ package de.steamwar.bungeecore.tablist; +import com.velocitypowered.api.proxy.Player; import de.steamwar.messages.Chatter; -import net.md_5.bungee.api.connection.ProxiedPlayer; import java.util.List; @@ -37,7 +37,7 @@ public class TablistGroup implements TablistPart { } @Override - public void print(Chatter viewer, ProxiedPlayer player, List tablist, List direct) { + public void print(Chatter viewer, Player player, List tablist, List direct) { for (TablistPart sublist : sublists) { sublist.print(viewer, player, tablist, direct); } diff --git a/src/de/steamwar/bungeecore/tablist/TablistManager.java b/src/de/steamwar/bungeecore/tablist/TablistManager.java index 47bbc22..265dc58 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistManager.java +++ b/src/de/steamwar/bungeecore/tablist/TablistManager.java @@ -19,13 +19,23 @@ package de.steamwar.bungeecore.tablist; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.PostLoginEvent; +import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; +import de.steamwar.bungeecore.Servertype; +import de.steamwar.bungeecore.Storage; +import de.steamwar.bungeecore.Subserver; import de.steamwar.bungeecore.VelocityCore; import de.steamwar.bungeecore.listeners.BasicListener; import de.steamwar.network.packets.common.FightInfoPacket; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; public class TablistManager extends BasicListener { @@ -48,22 +58,22 @@ public class TablistManager extends BasicListener { } } - @EventHandler + @Subscribe public void onJoin(PostLoginEvent event) { synchronized (tablists) { tablists.put(event.getPlayer(), new Tablist(event.getPlayer())); } } - @EventHandler - public void onServerConnection(ServerSwitchEvent event) { + @Subscribe + public void onServerConnection(ServerConnectedEvent event) { synchronized (tablists) { tablists.get(event.getPlayer()).onServerSwitch(); } } - @EventHandler - public void onLeave(PlayerDisconnectEvent event) { + @Subscribe + public void onLeave(DisconnectEvent event) { synchronized (tablists) { tablists.remove(event.getPlayer()); Storage.directTabItems.remove(event.getPlayer()); @@ -79,11 +89,11 @@ public class TablistManager extends BasicListener { private void updateTablist() { List subservers = new ArrayList<>(); - for (ServerInfo server : new ArrayList<>(ProxyServer.getInstance().getServers().values())){ - if(server.getPlayers().isEmpty()) + for (RegisteredServer server : new ArrayList<>(VelocityCore.getProxy().getAllServers())){ + if(server.getPlayersConnected().isEmpty()) continue; - Subserver subserver = Subserver.getSubserver(server); + Subserver subserver = Subserver.getSubserver(server.getServerInfo()); if(fightInfos.containsKey(server)) subservers.add(new TablistServer(server, fightInfos.get(server))); else if(subserver == null || subserver.getType() != Servertype.BAUSERVER) diff --git a/src/de/steamwar/bungeecore/tablist/TablistPart.java b/src/de/steamwar/bungeecore/tablist/TablistPart.java index ef9473d..78baeb2 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistPart.java +++ b/src/de/steamwar/bungeecore/tablist/TablistPart.java @@ -20,72 +20,46 @@ package de.steamwar.bungeecore.tablist; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.util.GameProfile; import de.steamwar.messages.Chatter; import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.UserPerm; -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.connection.InitialHandler; -import net.md_5.bungee.connection.LoginResult; -import net.md_5.bungee.protocol.Property; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import java.util.List; import java.util.UUID; -interface TablistPart { +public interface TablistPart { String sortKey(); void print(Chatter viewer, Player player, List tablist, List direct); + @Getter class Item { - - public static Property[] playerProperties(Player player) { - LoginResult loginResult = ((InitialHandler) player.getPendingConnection()).getLoginProfile(); - if(loginResult == null) - return new Property[0]; - - return loginResult.getProperties(); - } - private final UUID uuid; - private final BaseComponent displayName; - private final Property[] properties; + private final Component displayName; + private final List properties; - public Item(UUID uuid, String displayName, Property[] properties) { + public Item(UUID uuid, String displayName, List properties) { this.uuid = uuid; - this.displayName = reformat(displayName); + this.displayName = LegacyComponentSerializer.legacySection().deserialize(displayName); this.properties = properties; } - public Item(ProxiedPlayer player) { + public Item(Player player) { this(player, false); } - public Item(ProxiedPlayer player, boolean sameTeam) { + public Item(Player player, boolean sameTeam) { this.uuid = player.getUniqueId(); UserPerm.Prefix prefix = SteamwarUser.get(player.getUniqueId()).prefix(); if (prefix == UserPerm.emptyPrefix && sameTeam) { - this.displayName = reformat("§f" + player.getName()); + this.displayName = LegacyComponentSerializer.legacySection().deserialize("§f" + player.getUsername()); } else { - this.displayName = reformat(prefix.getColorCode() + player.getName()); + this.displayName = LegacyComponentSerializer.legacySection().deserialize(prefix.getColorCode() + player.getUsername()); } - this.properties = playerProperties(player); - } - - public UUID getUuid() { - return uuid; - } - - public BaseComponent getDisplayName() { - return displayName; - } - - public Property[] getProperties() { - return properties; - } - - private BaseComponent reformat(String string) { - return TextComponent.fromLegacy(string); + this.properties = player.getGameProfileProperties(); } } } diff --git a/src/de/steamwar/bungeecore/tablist/TablistServer.java b/src/de/steamwar/bungeecore/tablist/TablistServer.java index 12992e4..c93924a 100644 --- a/src/de/steamwar/bungeecore/tablist/TablistServer.java +++ b/src/de/steamwar/bungeecore/tablist/TablistServer.java @@ -19,15 +19,16 @@ package de.steamwar.bungeecore.tablist; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ServerConnection; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.util.GameProfile; import de.steamwar.bungeecore.Servertype; import de.steamwar.bungeecore.Subserver; -import de.steamwar.sql.SteamwarUser; +import de.steamwar.bungeecore.VelocityCore; import de.steamwar.messages.Chatter; import de.steamwar.network.packets.common.FightInfoPacket; -import net.md_5.bungee.BungeeCord; -import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.protocol.Property; +import de.steamwar.sql.SteamwarUser; import java.util.ArrayList; import java.util.Collection; @@ -38,63 +39,63 @@ import java.util.stream.Stream; 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=")}; + public static final List GRAY = List.of(new GameProfile.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 List LIGHT_GRAY = List.of(new GameProfile.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 RegisteredServer server; private final List 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(RegisteredServer server) { + this(server, server.getPlayersConnected().stream().sorted((p1, p2) -> p1.getUsername().compareToIgnoreCase(p2.getUsername())).map(TablistPart.Item::new).collect(Collectors.toList())); } - public TablistServer(ServerInfo server, FightInfoPacket info) { + public TablistServer(RegisteredServer server, FightInfoPacket info) { this(server, new ArrayList<>()); - Collection onlinePlayers = server.getPlayers(); + Collection onlinePlayers = server.getPlayersConnected(); 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 players) { + public TablistServer(RegisteredServer server, List players) { this.server = server; this.players = players; } @Override public String sortKey() { - return server.getName(); + return server.getServerInfo().getName(); } @Override - public void print(Chatter viewer, ProxiedPlayer player, List tablist, List direct) { - boolean onServer = player.getServer().getInfo() == server; + public void print(Chatter viewer, Player player, List tablist, List direct) { + boolean onServer = player.getCurrentServer().orElseThrow().getServer() == server; List items = onServer ? direct : tablist; if(!onServer) { items.add(new Item(null, "", GRAY)); - items.add(new Item(null, "§7§l" + server.getName(), LIGHT_GRAY)); + items.add(new Item(null, "§7§l" + server.getServerInfo().getName(), LIGHT_GRAY)); } teamify(players, player).forEach(items::add); } - private void addPlayers(String prefix, List teamPlayers, Collection onlinePlayers){ + private void addPlayers(String prefix, List teamPlayers, Collection onlinePlayers) { teamPlayers.stream().map(SteamwarUser::get).map( user -> onlinePlayers.stream().filter(player -> player.getUniqueId().equals(user.getUUID())).findAny() ).filter(Optional::isPresent).map(Optional::get).sorted( - (p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName()) - ).forEachOrdered(player -> players.add(new Item(player.getUniqueId(), prefix + player.getName(), Item.playerProperties(player)))); + (p1, p2) -> p1.getUsername().compareToIgnoreCase(p2.getUsername()) + ).forEachOrdered(player -> players.add(new Item(player.getUniqueId(), prefix + player.getUsername(), player.getGameProfileProperties()))); } - public static Stream teamify(List players, ProxiedPlayer player) { + public static Stream teamify(List players, Player player) { int team = SteamwarUser.get(player.getUniqueId()).getTeam(); if (team == 0) return players.stream(); return players.stream().map(item -> { - ProxiedPlayer p = BungeeCord.getInstance().getPlayer(item.getUuid()); + Player p = VelocityCore.getProxy().getPlayer(item.getUuid()).orElse(null); if (p == null) return item; - Subserver subserver = Subserver.getSubserver(p.getServer().getInfo()); + Subserver subserver = Subserver.getSubserver(p.getCurrentServer().map(ServerConnection::getServerInfo).orElse(null)); if (subserver != null && subserver.getType() == Servertype.ARENA) return item; if (SteamwarUser.get(p.getUniqueId()).getTeam() != team) return item; return new Item(p, true); diff --git a/src/de/steamwar/bungeecore/util/YamlUtils.java b/src/de/steamwar/bungeecore/util/YamlUtils.java deleted file mode 100644 index ccf169e..0000000 --- a/src/de/steamwar/bungeecore/util/YamlUtils.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2024 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.util; - -import lombok.experimental.UtilityClass; -import org.yaml.snakeyaml.Yaml; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -@UtilityClass -public class YamlUtils { - - public T get(Map config, String key) { - if (key.contains(".")) { - return getRecursive(config, key.split("\\."), null); - } - - return (T) config.get(key); - } - - public T get(Map config, String key, T def) { - if (key.contains(".")) { - return getRecursive(config, key.split("\\."), def); - } - - return (T) config.getOrDefault(key, def); - } - - public T getRecursive(Map config, String[] keys, T def) { - if (keys.length == 1) { - return get(config, keys[0], def); - } else { - Object o = get(config, keys[0], null); - if (o == null) { - if (def == null) { - throw new IllegalArgumentException("Key " + keys[0] + " not found"); - } - return def; - } - - if (o instanceof Map) { - return getRecursive((Map) o, Arrays.copyOfRange(keys, 1, keys.length), def); - } else { - throw new IllegalArgumentException("Key " + keys[0] + " is not a map"); - } - } - } - - public List getList(Map config, String key) { - return (List) config.get(key); - } - - public T loadYaml(File f) throws FileNotFoundException { - return loadYaml(f, new Yaml()); - } - - public T loadYaml(File f, Yaml yaml) throws FileNotFoundException { - return yaml.load(new FileInputStream(f)); - } -} diff --git a/src/de/steamwar/sql/SQLWrapperImpl.java b/src/de/steamwar/sql/SQLWrapperImpl.java index 8e7bf8b..d6daa68 100644 --- a/src/de/steamwar/sql/SQLWrapperImpl.java +++ b/src/de/steamwar/sql/SQLWrapperImpl.java @@ -21,19 +21,15 @@ package de.steamwar.sql; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; +import de.steamwar.bungeecore.GameModeConfig; import de.steamwar.bungeecore.VelocityCore; import de.steamwar.bungeecore.commands.CheckCommand; -import org.yaml.snakeyaml.Yaml; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.*; -import java.util.stream.Collectors; - -import static de.steamwar.bungeecore.util.YamlUtils.*; +import java.util.Date; +import java.util.List; +import java.util.Map; public class SQLWrapperImpl implements SQLWrapper { private static final SimpleDateFormat deadlineFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm"); @@ -50,44 +46,27 @@ public class SQLWrapperImpl implements SQLWrapper { @Override public void loadSchemTypes(List tmpTypes, Map tmpFromDB) { - File folder = new File(VelocityCore.get().getDataDirectory().getParent().toFile(), "FightSystem"); - if(folder.exists()) { - for(File configFile : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().collect(Collectors.toList())) { - Map config; - Yaml yaml = new Yaml(); - try { - config = yaml.load(new FileInputStream(configFile)); - } catch (FileNotFoundException e) { - throw new SecurityException("Could not load config.yml", e); - } + GameModeConfig.loadAll(GameModeConfig.class, (file, config) -> { + if(config.getSchematic() == null || tmpFromDB.containsKey(config.getSchemType().toLowerCase())) + return; - if(!config.containsKey("Schematic")) - continue; + String shortcut = config.getSchematic().getShortcut(); + String material = config.getSchematic().getMaterial(); - String type = get(config, "Schematic.Type"); - assert type != null; - String shortcut = get(config,"Schematic.Shortcut"); - if(tmpFromDB.containsKey(type.toLowerCase())) - continue; - - SchematicType checktype = null; - String material = get(config,"Schematic.Material"); - - if(!getList(config,"CheckQuestions").isEmpty()) { - checktype = new SchematicType("C" + type, "C" + shortcut, SchematicType.Type.CHECK_TYPE, null, material, true); - tmpTypes.add(checktype); - tmpFromDB.put(checktype.toDB(), checktype); - CheckCommand.setCheckQuestions(checktype, get(config, "CheckQuestions")); - } - boolean manualCheck = get(config, "Schematic.ManualCheck", true); - - SchematicType current = new SchematicType(type, shortcut, !config.containsKey("Server") ? SchematicType.Type.FIGHT_TYPE : SchematicType.Type.NORMAL, checktype, material, parseDeadline(get(config,"deadline", null)), manualCheck); - tmpTypes.add(current); - tmpFromDB.put(type.toLowerCase(), current); - if(checktype != null) - CheckCommand.addFightType(checktype, current); + SchematicType checktype = null; + if(!config.getCheckQuestions().isEmpty()) { + checktype = new SchematicType("C" + config.getSchemType(), "C" + shortcut, SchematicType.Type.CHECK_TYPE, null, material, true); + tmpTypes.add(checktype); + tmpFromDB.put(checktype.toDB(), checktype); + CheckCommand.setCheckQuestions(checktype, config.getCheckQuestions()); } - } + + SchematicType current = new SchematicType(config.getSchemType(), shortcut, config.getServer() != null ? SchematicType.Type.FIGHT_TYPE : SchematicType.Type.NORMAL, checktype, material, parseDeadline(config.getDeadline()), config.getSchematic().isManualCheck()); + tmpTypes.add(current); + tmpFromDB.put(config.getSchemType().toLowerCase(), current); + if(checktype != null) + CheckCommand.addFightType(checktype, current); + }); } @Override diff --git a/steamwarci.yml b/steamwarci.yml index 76aa003..109228b 100644 --- a/steamwarci.yml +++ b/steamwarci.yml @@ -3,5 +3,6 @@ build: - "./gradlew --stop" artifacts: - "/binarys/bungeecore.jar": "build/libs/bungeecore.jar" + "/binarys/PersistentVelocityCore.jar": "Persistent/build/libs/Persistent.jar" + "/binarys/VelocityCore.jar": "build/libs/velocitycore.jar" "/binarys/deployarena.py": "deployarena.py"