Long DiscordId #242
@ -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");
|
||||
new Node.RemoteNode("az");
|
||||
|
||||
commands.put("/tp", null);
|
||||
commands.put("/bc", null);
|
||||
commands.put("/bauchat", null);
|
||||
@ -158,14 +159,13 @@ 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();
|
||||
}
|
||||
Statement.close();
|
||||
}
|
||||
|
||||
public static BungeeCore get() {
|
||||
|
@ -20,83 +20,51 @@
|
||||
package de.steamwar.bungeecore;
|
||||
|
||||
import de.steamwar.bungeecore.sql.SWException;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import de.steamwar.bungeecore.sql.Statement;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.logging.Filter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ErrorLogger extends Handler implements Filter {
|
||||
private static ErrorLogger instance;
|
||||
public class ErrorLogger extends Handler {
|
||||
private int ddosRate = 0;
|
||||
|
||||
ErrorLogger(){
|
||||
ProxyServer.getInstance().getLogger().addHandler(this);
|
||||
ProxyServer.getInstance().getLogger().setFilter(this);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
static void stop(){
|
||||
ProxyServer.getInstance().getLogger().removeHandler(instance);
|
||||
Logger.getLogger("").addHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord record) {
|
||||
if(record.getLevel() != Level.SEVERE)
|
||||
return true;
|
||||
public void publish(LogRecord logRecord) {
|
||||
if(logRecord.getLevel().intValue() < Level.WARNING.intValue())
|
||||
return;
|
||||
|
||||
StringBuilder stacktrace = new StringBuilder(record.getSourceClassName() + "\n" + record.getSourceMethodName());
|
||||
Throwable thrown = record.getThrown();
|
||||
while(thrown != null){
|
||||
stacktrace.append("\nCaused by ").append(thrown.getMessage());
|
||||
String message = MessageFormat.format(logRecord.getMessage(), logRecord.getParameters());
|
||||
for(String reason : ignoreContains)
|
||||
if(message.contains(reason))
|
||||
return;
|
||||
|
||||
for(StackTraceElement ste : thrown.getStackTrace())
|
||||
stacktrace.append("\n").append(ste.toString());
|
||||
|
||||
thrown = thrown.getCause();
|
||||
}
|
||||
|
||||
String stacktraceString = stacktrace.toString();
|
||||
if(stacktraceString.contains("Cannot request protocol")){
|
||||
ddosRate++;
|
||||
if(ddosRate % 1000 == 0){
|
||||
ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream();
|
||||
if(logRecord.getThrown() != null)
|
||||
logRecord.getThrown().printStackTrace(new PrintStream(stacktraceOutput));
|
||||
String stacktrace = stacktraceOutput.toString();
|
||||
if(stacktrace.contains("Cannot request protocol")) {
|
||||
if(++ddosRate % 1000 == 0) {
|
||||
SWException.log("Bungee", "DDOS", ddosRate + "");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
if(record.getLevel() != Level.SEVERE && record.getLevel() != Level.WARNING)
|
||||
return;
|
||||
|
||||
StringBuilder stacktrace = new StringBuilder(record.getSourceClassName() + "\n" + record.getSourceMethodName());
|
||||
Throwable thrown = record.getThrown();
|
||||
while(thrown != null){
|
||||
stacktrace.append("\nCaused by ").append(thrown.getMessage());
|
||||
|
||||
for(StackTraceElement ste : thrown.getStackTrace())
|
||||
stacktrace.append("\n").append(ste.toString());
|
||||
|
||||
thrown = thrown.getCause();
|
||||
} else if (!Statement.connectionStable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String stacktraceString = stacktrace.toString();
|
||||
String message = MessageFormat.format(record.getMessage(), record.getParameters());
|
||||
|
||||
if(message.contains("ServerConnector")
|
||||
|| message.contains("InitialHandler")
|
||||
|| message.contains("UpstreamBridge")
|
||||
|| message.contains("DownstreamBridge")
|
||||
|| message.contains(" took ")
|
||||
|| message.contains("No client connected for pending server!"))
|
||||
return;
|
||||
|
||||
SWException.log("Bungee", message, stacktraceString);
|
||||
SWException.log("Bungee", message, stacktrace);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -108,4 +76,17 @@ public class ErrorLogger extends Handler implements Filter {
|
||||
public void close() {
|
||||
//ignored
|
||||
}
|
||||
|
||||
private static final List<String> ignoreContains;
|
||||
|
||||
static {
|
||||
List<String> contains = new ArrayList<>();
|
||||
contains.add("ServerConnector");
|
||||
contains.add("InitialHandler");
|
||||
contains.add("UpstreamBridge");
|
||||
contains.add("DownstreamBridge");
|
||||
contains.add(" took ");
|
||||
contains.add("No client connected for pending server!");
|
||||
ignoreContains = Collections.unmodifiableList(contains);
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
261
src/de/steamwar/bungeecore/Node.java
Normale Datei
261
src/de/steamwar/bungeecore/Node.java
Normale Datei
@ -0,0 +1,261 @@
|
||||
/*
|
||||
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 double MIN_FREE_MEM = 4.0 * 1024 * 1024; // 4 GiB
|
||||
|
||||
private static final List<Node> nodes = new ArrayList<>();
|
||||
public static Node local = null;
|
||||
|
||||
public static Node getNode() {
|
||||
Node node = local;
|
||||
double minLoad = local.getLoad();
|
||||
if(minLoad < 0.5)
|
||||
return local;
|
||||
|
||||
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) {
|
||||
consumer.accept(local);
|
||||
synchronized (nodes) {
|
||||
nodes.forEach(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
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 + (freeMem > MIN_FREE_MEM ? 0 : ((totalMem - freeMem) / totalMem));
|
||||
}
|
||||
}
|
||||
|
||||
public static class RemoteNode extends Node {
|
||||
private final int cores;
|
||||
private final String remote;
|
||||
|
||||
public RemoteNode(String remote) {
|
||||
this.remote = remote;
|
||||
|
||||
//Determin core count
|
||||
Process process;
|
||||
try {
|
||||
process = new ProcessBuilder("ssh", remote, "nproc").start();
|
||||
if(!process.waitFor(5, TimeUnit.SECONDS))
|
||||
throw new IOException("Timeout of " + remote + " on init");
|
||||
} catch (IOException e) {
|
||||
BungeeCore.get().getLogger().log(Level.SEVERE, "Could not initialize " + remote);
|
||||
cores = 1;
|
||||
return;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
cores = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
int c;
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
c = Integer.parseInt(bufferedReader.readLine());
|
||||
} catch (IOException e) {
|
||||
BungeeCore.get().getLogger().log(Level.SEVERE, "Could not read cores of" + remote, e);
|
||||
c = 1;
|
||||
}
|
||||
cores = c;
|
||||
BungeeCore.get().getLogger().log(Level.INFO, "Adding node " + remote + " with " + cores + " cores.");
|
||||
|
||||
synchronized (nodes) {
|
||||
nodes.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
@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 + (freeMem > MIN_FREE_MEM ? 0 : ((totalMem - freeMem) / totalMem));
|
||||
} catch (IOException e) {
|
||||
BungeeCore.get().getLogger().log(Level.SEVERE, "Could read load", e);
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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){
|
||||
|
@ -257,7 +257,7 @@ public class BauCommand extends BasicCommand {
|
||||
break;
|
||||
}
|
||||
}
|
||||
SubserverSystem.deleteFolder(world);
|
||||
SubserverSystem.deleteFolder(Node.local, world);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,39 +40,48 @@ public class Statement {
|
||||
Statement.user = user;
|
||||
Statement.password = password;
|
||||
try {
|
||||
con = DriverManager.getConnection(url + "?autoreconnect=true", user, password);
|
||||
con = DriverManager.getConnection(url + "?autoreconnect=true&useServerPrepStmts=true", user, password);
|
||||
} catch (SQLException e) {
|
||||
ProxyServer.getInstance().stop();
|
||||
throw new SecurityException("Could not start SQL-Connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void reset(SQLException e) {
|
||||
BungeeCore.get().getLogger().log(Level.WARNING, "SQL Exception thrown", e);
|
||||
private static void reset() {
|
||||
close();
|
||||
connect(url, user, password);
|
||||
try {
|
||||
for (Statement statement : statements) {
|
||||
statement.init();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new SecurityException("Could not reprepare SQL Statements", ex);
|
||||
} catch (SQLException e) {
|
||||
throw new SecurityException("Could not reprepare SQL statements", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void close() {
|
||||
for (Statement statement : statements) {
|
||||
synchronized (statements) {
|
||||
for (Statement statement : statements) {
|
||||
try {
|
||||
statement.st.close();
|
||||
} catch (SQLException e) {
|
||||
BungeeCore.get().getLogger().log(Level.INFO, "Could not close statement", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
statement.st.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
BungeeCore.get().getLogger().log(Level.INFO, "Could not close statement", e);
|
||||
BungeeCore.get().getLogger().log(Level.INFO, "Could not close SQL connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean connectionStable() {
|
||||
try {
|
||||
con.close();
|
||||
return !con.isClosed();
|
||||
} catch (SQLException e) {
|
||||
BungeeCore.log("Could not close SQL-Connection", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,39 +94,38 @@ public class Statement {
|
||||
try {
|
||||
init();
|
||||
} catch (SQLException e) {
|
||||
reset(e);
|
||||
throw new SecurityException("Could not init SQL statement", e);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void init() throws SQLException {
|
||||
private void init() throws SQLException {
|
||||
st = con.prepareStatement(sql);
|
||||
}
|
||||
|
||||
<T> T select(ResultSetUser<T> user, Object... objects) {
|
||||
return prepare(() -> {
|
||||
ResultSet rs = st.executeQuery();
|
||||
T result = user.use(rs);
|
||||
rs.close();
|
||||
return result;
|
||||
}, objects);
|
||||
synchronized (statements) {
|
||||
return prepare(() -> {
|
||||
ResultSet rs = st.executeQuery();
|
||||
T result = user.use(rs);
|
||||
rs.close();
|
||||
return result;
|
||||
}, objects);
|
||||
}
|
||||
}
|
||||
|
||||
void update(Object... objects) {
|
||||
prepare(st::executeUpdate, objects);
|
||||
synchronized (statements) {
|
||||
prepare(st::executeUpdate, objects);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized <T> T prepare(SQLRunnable<T> runnable, Object... objects) {
|
||||
private <T> T prepare(SQLRunnable<T> runnable, Object... objects) {
|
||||
try {
|
||||
setObjects(objects);
|
||||
return runnable.run();
|
||||
} catch (SQLException e) {
|
||||
reset(e);
|
||||
try {
|
||||
setObjects(objects);
|
||||
return runnable.run();
|
||||
} catch (SQLException ex) {
|
||||
throw new SecurityException("Could not execute SQL statement", ex);
|
||||
}
|
||||
reset();
|
||||
throw new SecurityException("Could not execute SQL statement", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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}
|
||||
|
6
steamwarci.yml
Normale Datei
6
steamwarci.yml
Normale Datei
@ -0,0 +1,6 @@
|
||||
build:
|
||||
- "ln -s /home/gitea/lib"
|
||||
- "mvn package -B"
|
||||
|
||||
artifacts:
|
||||
"/binarys/bungeecore.jar": "target/bungeecore.jar"
|
In neuem Issue referenzieren
Einen Benutzer sperren