diff --git a/Persistent/build.gradle b/Persistent/build.gradle
new file mode 100644
index 00000000..c52ad18e
--- /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 00000000..37bd9db9
--- /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 00000000..c5876f08
--- /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 00000000..c085bb98
--- /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 00000000..335e972a
--- /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 00000000..06e66633
--- /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 00000000..bd71641c
--- /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 00000000..830608cc
--- /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 0d0f5dc5..b5cd2805 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 aab2ecde..f18b8991 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 caba2ed5..132e6b5c 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 00000000..6c0d17a7
--- /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 53240e03..541bfa1e 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 edef37cc..0349dce1 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 5f1dc454..0ad54b0e 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 10d928ad..b1316663 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 3db72d77..fb170f57 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 4fcff4aa..037b6d3d 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 e6877881..796c5658 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 fb2e3097..fbef8e85 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