Archiviert
1
0

Object oriented subserver node system

Signed-off-by: Lixfel <agga-games@gmx.de>
Dieser Commit ist enthalten in:
Lixfel 2021-09-16 16:45:47 +02:00
Ursprung 947333b788
Commit 85fe4ba264
7 geänderte Dateien mit 280 neuen und 258 gelöschten Zeilen

Datei anzeigen

@ -89,9 +89,10 @@ public class BungeeCore extends Plugin {
new WorldDownloader();
new BrandListener();
commands.put("/b", null);
commands.put("/gs", null);
commands.put("/bau", null);
new Node.LocalNode();
new Node.RemoteNode("lx", 8);
new Node.RemoteNode("az", 10);
commands.put("/tp", null);
commands.put("/bc", null);
commands.put("/bauchat", null);
@ -158,14 +159,14 @@ public class BungeeCore extends Plugin {
@Override
public void onDisable(){
ErrorLogger.stop();
Statement.close();
try {
SteamwarDiscordBot.instance().getJda().shutdownNow();
SteamwarDiscordBot.instance().getJda().awaitStatus(JDA.Status.SHUTDOWN);
} catch (Exception e) {
// Ignored
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
ErrorLogger.stop();
Statement.close();
}
public static BungeeCore get() {

Datei anzeigen

@ -1,96 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2021 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bungeecore;
import java.io.*;
public class LoadEvaluation {
private LoadEvaluation(){}
private static final File meminfo = new File("/proc/meminfo");
public static double getRamPercentage() {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(meminfo)))) {
String memTotal = bufferedReader.readLine().replaceAll(" +", " ");
bufferedReader.readLine();
String memAvailable = bufferedReader.readLine().replaceAll(" +", " ");
long memTotalLong = getNumber(memTotal);
long memAvailableLong = getNumber(memAvailable);
return (memTotalLong - memAvailableLong) / (double) memTotalLong;
} catch (IOException e) {
return 1D;
}
}
public static double getRemoteRamPercentage(String remote) {
try {
// Attention:
// memInfo.sh needs to contain: cat /proc/meminfo
Process process = new ProcessBuilder("ssh", remote, "\"./memInfo.sh\"").start();
process.waitFor();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String memTotal = bufferedReader.readLine().replaceAll(" +", " ");
bufferedReader.readLine();
String memAvailable = bufferedReader.readLine().replaceAll(" +", " ");
long memTotalLong = getNumber(memTotal);
long memAvailableLong = getNumber(memAvailable);
return (memTotalLong - memAvailableLong) / (double) memTotalLong;
} catch (IOException e) {
return 1D;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return 1D;
}
}
public static double getCPULoad() {
try {
Process process = new ProcessBuilder("bash", "-c", "cat <(grep 'cpu ' /proc/stat) <(sleep 1 && grep 'cpu ' /proc/stat) | awk -v RS=\"\" '{printf \"%.2f\\n\", ($13-$2+$15-$4)*100/($13-$2+$15-$4+$16-$5)}'").start();
process.waitFor();
return Double.parseDouble(new BufferedReader(new InputStreamReader(process.getInputStream())).readLine()) / 100.0;
} catch (IOException e) {
return 1D;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return 1D;
}
}
public static double getRemoteCPULoad(String remote) {
try {
// Attention:
// cpuLoad.sh needs to contain: cat <(grep 'cpu ' /proc/stat) <(sleep 1 && grep 'cpu ' /proc/stat) | awk -v RS="" '{printf "%.2f\n", ($13-$2+$15-$4)*100/($13-$2+$15-$4+$16-$5)}'
Process process = new ProcessBuilder("ssh", remote, "\"./cpuLoad.sh\"").start();
process.waitFor();
return Double.parseDouble(new BufferedReader(new InputStreamReader(process.getInputStream())).readLine()) / 100.0;
} catch (IOException e) {
return 1D;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return 1D;
}
}
private static long getNumber(String s) {
return Long.parseLong(s.split(" ")[1]);
}
}

Datei anzeigen

@ -0,0 +1,235 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bungeecore;
import net.md_5.bungee.api.ProxyServer;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.logging.Level;
public abstract class Node {
private static final List<String> JVM_ARGS = Arrays.asList("-Dlog4j.configurationFile=log4j2.xml", "-server", "-Xms128M", "-XX:+UseCompressedOops", "-XX:+TieredCompilation", "-XX:TargetSurvivorRatio=90", "-XX:SurvivorRatio=8", "-XX:MaxTenuringThreshold=15", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseBiasedLocking", "-XX:UseSSE=3", "-XX:+UseCodeCacheFlushing", "-XX:+UseThreadPriorities", "-XX:+AggressiveOpts", "-XX:+ReduceSignalUsage", "-XX:+UseInterpreter", "-XX:+UseSharedSpaces", "-XX:AllocatePrefetchStyle=1", "-XX:+AlwaysCompileLoopMethods", "-XX:+UseConcMarkSweepGC", "-XX:+RewriteFrequentPairs", "-XX:+OptimizeStringConcat", "-XX:+CMSCleanOnEnter", "-XX:+UseInlineCaches");
private static final List<String> JVM8_ARGS = Arrays.asList("-XX:ThreadPriorityPolicy=42", "-XX:SharedReadOnlySize=30m", "-XX:+UseFastEmptyMethods", "-XX:+UseFastAccessorMethods");
private static final List<Node> nodes = new ArrayList<>();
public static Node local = null;
public static Node getNode() {
Node node = null;
double minLoad = Double.POSITIVE_INFINITY;
synchronized (nodes) {
Iterator<Node> it = nodes.iterator();
while(it.hasNext()) {
Node n = it.next();
double load = n.getLoad();
if (load < minLoad) {
minLoad = load;
node = n;
} else if (load == Double.POSITIVE_INFINITY) {
BungeeCore.get().getLogger().log(Level.SEVERE, "Removing " + n.getName() + " due to infinite load!");
it.remove();
}
}
}
return node;
}
public static void forEach(Consumer<Node> consumer) {
synchronized (nodes) {
nodes.forEach(consumer);
}
}
protected Node() {
synchronized (nodes) {
nodes.add(this);
}
}
public abstract ProcessBuilder startServer(String serverJar, File directory, String worldDir, String levelName, int port, String xmx, String... dParams);
public abstract void execute(String... command);
public abstract String getName();
public abstract double getLoad();
protected void constructServerstart(List<String> cmd, String serverJar, String worldDir, String levelName, int port, String xmx, String... dParams) {
boolean jdk11 = serverJar.contains("1.15.2");
if(jdk11)
cmd.add("/usr/lib/jvm/java-11-openjdk-amd64/bin/java");
else
cmd.add("java");
for(String param : dParams){
cmd.add("-D" + param);
}
cmd.add("-Xmx" + xmx);
cmd.addAll(JVM_ARGS);
if(!jdk11)
cmd.addAll(JVM8_ARGS);
cmd.add("-jar");
cmd.add("/binarys/" + serverJar);
cmd.add("--log-strip-color");
cmd.add("--world-dir");
cmd.add(worldDir);
cmd.add("--level-name");
cmd.add(levelName);
cmd.add("--port");
cmd.add(String.valueOf(port));
cmd.add("nogui");
}
protected void execute(ProcessBuilder builder) {
try {
builder.start().waitFor();
} catch (IOException e) {
throw new SecurityException("Could not execute command", e);
} catch (InterruptedException e) {
ProxyServer.getInstance().getLogger().log(Level.SEVERE, "Interrupted during execution", e);
Thread.currentThread().interrupt();
}
}
public static class LocalNode extends Node {
private static final File meminfo = new File("/proc/meminfo");
private static final File loadavg = new File("/proc/loadavg");
private final int cores;
public LocalNode() {
super();
this.cores = Runtime.getRuntime().availableProcessors();
local = this;
}
@Override
public ProcessBuilder startServer(String serverJar, File directory, String worldDir, String levelName, int port, String xmx, String... dParams) {
List<String> cmd = new ArrayList<>();
constructServerstart(cmd, serverJar, worldDir, levelName, port, xmx, dParams);
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.directory(directory);
return builder;
}
@Override
public void execute(String... command) {
execute(new ProcessBuilder(command));
}
@Override
public String getName() {
return "local";
}
@Override
public double getLoad() {
double totalMem;
double freeMem;
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(meminfo)))) {
totalMem = Double.parseDouble(bufferedReader.readLine().replaceAll(" +", " ").split(" ")[1]);
bufferedReader.readLine();
freeMem = Double.parseDouble(bufferedReader.readLine().replaceAll(" +", " ").split(" ")[1]);
} catch (IOException e) {
throw new SecurityException("Could not read local memory", e);
}
double cpuLoad;
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(loadavg)))) {
cpuLoad = Double.parseDouble(bufferedReader.readLine().split(" ")[0]);
} catch (IOException e) {
throw new SecurityException("Could not read local cpu", e);
}
return cpuLoad / cores + (totalMem - freeMem) / totalMem;
}
}
public static class RemoteNode extends Node {
private final int cores;
private final String remote;
public RemoteNode(String remote, int cores) {
super();
this.cores = cores;
this.remote = remote;
}
@Override
public ProcessBuilder startServer(String serverJar, File directory, String worldDir, String levelName, int port, String xmx, String... dParams) {
List<String> cmd = new ArrayList<>();
cmd.add("ssh");
cmd.add("-L");
cmd.add(port + ":localhost:" + port);
cmd.add(remote);
cmd.add("cd");
cmd.add(directory.getPath());
cmd.add(";");
constructServerstart(cmd, serverJar, worldDir, levelName, port, xmx, dParams);
return new ProcessBuilder(cmd);
}
@Override
public void execute(String... command) {
List<String> cmd = new ArrayList<>();
cmd.add("ssh");
cmd.add(remote);
cmd.addAll(Arrays.asList(command));
execute(new ProcessBuilder(cmd));
}
@Override
public String getName() {
return remote;
}
@Override
public double getLoad() {
Process process;
try {
process = new ProcessBuilder("ssh", remote, "cat /proc/loadavg;cat /proc/meminfo").start();
if(!process.waitFor(1, TimeUnit.SECONDS))
return Double.POSITIVE_INFINITY;
} catch (IOException e) {
BungeeCore.get().getLogger().log(Level.SEVERE, "Could starting process to read load", e);
return Double.POSITIVE_INFINITY;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Double.POSITIVE_INFINITY;
}
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
double cpuLoad = Double.parseDouble(bufferedReader.readLine().split(" ")[0]);
double totalMem = Double.parseDouble(bufferedReader.readLine().replaceAll(" +", " ").split(" ")[1]);
bufferedReader.readLine();
double freeMem = Double.parseDouble(bufferedReader.readLine().replaceAll(" +", " ").split(" ")[1]);
return cpuLoad / cores + (totalMem - freeMem) / totalMem;
} catch (IOException e) {
BungeeCore.get().getLogger().log(Level.SEVERE, "Could read load", e);
return Double.POSITIVE_INFINITY;
}
}
}
}

Datei anzeigen

@ -27,18 +27,14 @@ import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.logging.Level;
import java.util.UUID;
public class SubserverSystem {
private SubserverSystem(){}
private static final String BACKBONE = "/home/minecraft/backbone/";
private static final List<String> JVM_ARGS = Arrays.asList("-Dlog4j.configurationFile=log4j2.xml", "-server", "-Xms128M", "-XX:+UseCompressedOops", "-XX:+TieredCompilation", "-XX:TargetSurvivorRatio=90", "-XX:SurvivorRatio=8", "-XX:MaxTenuringThreshold=15", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseBiasedLocking", "-XX:UseSSE=3", "-XX:+UseCodeCacheFlushing", "-XX:+UseThreadPriorities", "-XX:+AggressiveOpts", "-XX:+ReduceSignalUsage", "-XX:+UseInterpreter", "-XX:+UseSharedSpaces", "-XX:AllocatePrefetchStyle=1", "-XX:+AlwaysCompileLoopMethods", "-XX:+UseConcMarkSweepGC", "-XX:+RewriteFrequentPairs", "-XX:+OptimizeStringConcat", "-XX:+CMSCleanOnEnter", "-XX:+UseInlineCaches");
private static final List<String> JVM8_ARGS = Arrays.asList("-XX:ThreadPriorityPolicy=42", "-XX:SharedReadOnlySize=30m", "-XX:+UseFastEmptyMethods", "-XX:+UseFastAccessorMethods");
private static final String BACKBONE = "/home/minecraft/";
private static final String ARENA_PATH = BACKBONE + "arenaserver/";
private static final String SERVER_PATH = BACKBONE + "server/";
private static final String EVENT_PATH = BACKBONE + "event/";
@ -91,6 +87,7 @@ public class SubserverSystem {
*/
public static synchronized Subserver startArena(ArenaMode modus, String map, int eventFightID, int checkSchemID, int prepareSchemID, String serverName, String mapName, UUID player1, UUID player2, boolean ranked){
//Generate missing parameters
Node node = eventFightID > 0 ? Node.local : Node.getNode();
int port = freePort(FIRST_ARENA_PORT);
if(serverName == null){
@ -109,56 +106,31 @@ public class SubserverSystem {
worldDir = ARENA_PATH;
//Copy world
try {
new ProcessBuilder("cp", "-r", SERVER_PATH + modus.getFolder() + "/arenas/" + map, worldDir + mapName).start().waitFor();
} catch (IOException e) {
throw new SecurityException("Could not copy folder", e);
} catch (InterruptedException e) {
ProxyServer.getInstance().getLogger().log(Level.SEVERE, "Interrupted while copying folder", e);
Thread.currentThread().interrupt();
}
node.execute("cp", "-r", SERVER_PATH + modus.getFolder() + "/arenas/" + map, worldDir + mapName);
File directory = new File(SERVER_PATH, modus.getFolder());
List<String> cmd = serverStartCommand(
modus.serverJar(),
directory,
worldDir,
mapName,
port,
"2G",
"logPath=" + mapName,
"config="+modus.getConfig(),
"fightID=" + eventFightID,
"ranked=" + ranked,
"checkSchemID=" + checkSchemID,
"prepareSchemID=" + prepareSchemID,
ProcessBuilder builder = node.startServer(
modus.serverJar(), directory, worldDir, mapName, port, "2G",
"logPath=" + mapName, "config=" + modus.getConfig(),
"fightID=" + eventFightID, "ranked=" + ranked,
"checkSchemID=" + checkSchemID, "prepareSchemID=" + prepareSchemID,
player1 != null && eventFightID != -1 ? "blueLeader=" + player1 : null,
player2 != null ? "redLeader=" + player2 : null);
//Start server
ProcessBuilder process = new ProcessBuilder(cmd);
process.directory(directory);
player2 != null ? "redLeader=" + player2 : null
);
String finalMapName = mapName;
if(eventFightID == -1)
return new Bauserver(serverName, player1, port, process, () -> deleteFolder(ARENA_PATH + finalMapName));
return new Bauserver(serverName, player1, port, builder, () -> deleteFolder(node, ARENA_PATH + finalMapName));
else
return new Subserver(Servertype.ARENA, serverName, port, process, () -> {
return new Subserver(Servertype.ARENA, serverName, port, builder, () -> {
if(eventFightID > 0)
return;
deleteFolder(ARENA_PATH + finalMapName);
deleteFolder(node, ARENA_PATH + finalMapName);
});
}
public static void deleteFolder(String worldName){
try {
new ProcessBuilder("rm", "-r", worldName).start().waitFor();
} catch (IOException e) {
throw new SecurityException("Could not clean up folder", e);
} catch (InterruptedException e) {
ProxyServer.getInstance().getLogger().log(Level.SEVERE, "Interrupted while deleting folder", e);
Thread.currentThread().interrupt();
}
public static void deleteFolder(Node node, String worldName){
node.execute("rm", "-r", worldName);
}
public static Subserver startEventArena(EventFight eventFight, String serverName){
@ -183,25 +155,15 @@ public class SubserverSystem {
if(bauRunning(p, owner))
return;
copyBauweltIfRequired(prototype, worldFolder + worldName);
SteamwarUser user = SteamwarUser.get(owner);
copyBauweltIfRequired(p, prototype, worldFolder + worldName);
File directory = new File(SERVER_PATH, serverName);
Node node = Node.getNode();
int port = freePort(4000);
File directory = new File(SERVER_PATH, serverName);
List<String> cmd = serverStartCommand(
serverJar,
directory,
worldDir,
worldName,
port,
xmx,
"logPath=" + worldName);
//Start server
ProcessBuilder process = new ProcessBuilder(cmd);
process.directory(directory);
new Bauserver(user.getUserName() + "s Bau", owner, port, process, () -> {}).sendPlayer(p);
new Bauserver(user.getUserName() + "s Bau", owner, port, node.startServer(
serverJar, directory, worldDir, worldName, port, xmx, "logPath=" + worldName
), () -> {}).sendPlayer(p);
}
public static void sendToBauServer(ProxiedPlayer p, UUID owner){
@ -235,63 +197,6 @@ public class SubserverSystem {
new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/bau addmember " + p.getName()));
}
private static List<String> serverStartCommand(String serverJar, File directory, String worldDir, String levelName, int port, String xmx, String... dParams){
List<String> cmd = new ArrayList<>();
boolean jdk11 = serverJar.contains("1.15.2");
boolean fallback = false;
if (!steamwarStartAvailable()) {
cmd.add("ssh");
cmd.add("-L");
cmd.add(port + ":localhost:" + port);
if (remoteStartAvailable("lx")) {
cmd.add("lx");
} else if (remoteStartAvailable("az")) {
cmd.add("az");
} else {
fallback = true;
}
cmd.add("cd");
cmd.add(directory.getPath());
cmd.add(";");
}
if (fallback) {
cmd.clear();
}
if(jdk11)
cmd.add("/usr/lib/jvm/java-11-openjdk-amd64/bin/java");
else
cmd.add("java");
for(String param : dParams){
cmd.add("-D" + param);
}
cmd.add("-Xmx" + xmx);
cmd.addAll(JVM_ARGS);
if(!jdk11)
cmd.addAll(JVM8_ARGS);
cmd.add("-jar");
cmd.add("/binarys/" + serverJar);
cmd.add("--log-strip-color");
cmd.add("--world-dir");
cmd.add(worldDir);
cmd.add("--level-name");
cmd.add(levelName);
cmd.add("--port");
cmd.add(String.valueOf(port));
return cmd;
}
private static boolean steamwarStartAvailable(){
return LoadEvaluation.getCPULoad() < 0.7 && LoadEvaluation.getRamPercentage() < 0.8;
}
private static boolean remoteStartAvailable(String remote) {
return LoadEvaluation.getRemoteCPULoad(remote) < 0.7 && LoadEvaluation.getRemoteRamPercentage(remote) < 0.8;
}
private static boolean bauRunning(ProxiedPlayer p, UUID owner){
for(Subserver subserver : Subserver.getServerList()){
if(subserver.getType() == Servertype.BAUSERVER && ((Bauserver)subserver).getOwner().equals(owner)){
@ -302,20 +207,10 @@ public class SubserverSystem {
return false;
}
private static void copyBauweltIfRequired(ProxiedPlayer p, String sourcePath, String targetPath){
private static void copyBauweltIfRequired(String sourcePath, String targetPath){
File w = new File(targetPath);
if (!w.exists() || !w.isDirectory()){
try {
new ProcessBuilder("cp", "-r", sourcePath, targetPath).start().waitFor();
} catch (IOException e) {
Message.send("SERVER_WORLD_ERROR", p);
throw new SecurityException("Could not create Bauwelt", e);
} catch (InterruptedException e){
BungeeCore.log("Could not create Bauwelt", e);
Message.send("SERVER_WORLD_ERROR", p);
Thread.currentThread().interrupt();
}
}
if (!w.exists() || !w.isDirectory())
Node.local.execute("cp", "-r", sourcePath, targetPath);
}
private static int freePort(int start){

Datei anzeigen

@ -257,7 +257,7 @@ public class BauCommand extends BasicCommand {
break;
}
}
SubserverSystem.deleteFolder(world);
SubserverSystem.deleteFolder(Node.local, world);
});
}

Datei anzeigen

@ -19,8 +19,8 @@
package de.steamwar.bungeecore.commands;
import de.steamwar.bungeecore.LoadEvaluation;
import de.steamwar.bungeecore.Message;
import de.steamwar.bungeecore.Node;
import net.md_5.bungee.api.CommandSender;
import java.io.BufferedReader;
@ -36,34 +36,22 @@ public class StatCommand extends BasicCommand {
@Override
public void execute(CommandSender sender, String[] args) {
Map<String, Integer> serverCount = new HashMap<>();
try {
Process process = new ProcessBuilder("ps", "x").start();
process.waitFor();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
Map<String, Integer> serverCounts = new HashMap<>();
bufferedReader.lines().forEach(s -> {
if (!s.contains("--port")) {
new BufferedReader(new InputStreamReader(process.getInputStream())).lines().forEach(s -> {
if (s.contains("--port"))
return;
}
String server = "SW";
if (s.contains("ssh -L")) {
server = s.substring(s.indexOf("ssh -L") + 6).split(" ")[2];
}
serverCounts.put(server, serverCounts.computeIfAbsent(server, s1 -> 0) + 1);
serverCount.compute(
s.contains("ssh -L") ? s.substring(s.indexOf("ssh -L") + 6).split(" ")[2] : "local",
(server, count) -> (count != null ? count : 0) + 1
);
});
serverCounts.forEach((s, integer) -> {
if (s.equals("SW")) {
Message.send("STAT_SERVER", sender, s, LoadEvaluation.getRamPercentage(), LoadEvaluation.getCPULoad(), integer);
} else {
Message.send("STAT_SERVER", sender, s.toUpperCase(), LoadEvaluation.getRemoteRamPercentage(s), LoadEvaluation.getRemoteCPULoad(s), integer);
}
});
if (serverCounts.isEmpty()) {
Message.send("NO_STATS", sender);
}
} catch (Exception e) {
throw new SecurityException(e.getMessage(), e);
}
Node.forEach(node -> Message.send("STAT_SERVER", sender, node.getName(), node.getLoad(), serverCount.getOrDefault(node.getName(), 0)));
}
}

Datei anzeigen

@ -107,8 +107,7 @@ MOD_YELLOW_PLUR=§7Deaktiviere die Mods\n§e{0}\n§7um weiter auf §eSteam§8War
#Various commands
ALERT=§f{0}
STAT_SERVER=§7Server §f{0} - §7Ram §f{1} §7CPU §f{2} §7Server Count §f{3}
NO_STATS=§7Kein Bau oder Fight Server gestartet
STAT_SERVER=§7Server §e{0}§8: §7Load §e{1} §7Serveranzahl §e{2}
#Ban&Mute-Command
BAN_TEAM_BANNED={0} §c{1} wurde von {2} {3} gebannt. §f§lGrund: §f{4}