mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-11 02:48:22 +01:00
Multi proxy support (#2968)
- Change proxy server info storage to allow multiple proxies to exist in the database - Allow naming Proxies with Server.Name config setting - Server.IP is no longer required to be set during installation - Change logic reliant on single proxy server to consider multiple proxies - If multiple proxies have webserver or export enabled, the address given by commands can be any of them. - Network players online graph now stacks if redisbungee is not used. Individual players online graphs for different proxies can be viewed from Performance tab. - Last Peak and All Time Peak are not given for multi-proxy networks without redisbungee since it would be expensive to calculate. - Improved network performance tab considerably - /plan info displays the Server UUID of the current server - /plan server link goes to /server/UUID instead of /server/Name - Fix join address graphs not loading if strict GROUP BY is enabled in MySQL Affects issues: - Close #1454
This commit is contained in:
parent
f43d8f89fb
commit
f7cec19372
@ -58,4 +58,9 @@ public class BungeeSensor implements ServerSensor<Object> {
|
||||
public List<String> getOnlinePlayerNames() {
|
||||
return getPlayers.get().stream().map(ProxiedPlayer::getName).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usingRedisBungee() {
|
||||
return RedisCheck.isClassAvailable();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import com.djrapitops.plan.processing.Processing;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
import com.djrapitops.plan.settings.locale.lang.PluginLang;
|
||||
import net.playeranalytics.plugin.server.PluginLogger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@ -73,17 +74,16 @@ public class BungeeServerInfo extends ServerInfo {
|
||||
@Override
|
||||
public void loadServerInfo() {
|
||||
logger.info(locale.getString(PluginLang.LOADING_SERVER_INFO));
|
||||
checkIfDefaultIP();
|
||||
|
||||
this.server = fromFile.load(null).orElseGet(() -> fromDatabase.load(null)
|
||||
.orElseGet(this::registerServer));
|
||||
this.server = fromFile.load(null)
|
||||
.orElseGet(this::registerServer);
|
||||
this.server.setProxy(true); // Ensure isProxy if loaded from file
|
||||
|
||||
processing.submitNonCritical(this::updateStorage);
|
||||
}
|
||||
|
||||
private void updateStorage() {
|
||||
String address = addresses.getAccessAddress().orElseGet(addresses::getFallbackLocalhostAddress);
|
||||
String address = getAddress();
|
||||
|
||||
server.setWebAddress(address);
|
||||
|
||||
@ -92,18 +92,6 @@ public class BungeeServerInfo extends ServerInfo {
|
||||
fromFile.save(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EnableException If IP setting is unset
|
||||
*/
|
||||
private void checkIfDefaultIP() {
|
||||
String ip = serverProperties.getIp();
|
||||
if ("0.0.0.0".equals(ip)) {
|
||||
logger.error("IP setting still 0.0.0.0 - Configure Alternative_IP/IP that connects to the Proxy server.");
|
||||
logger.info("Player Analytics partially enabled (Use /planproxy reload to reload config)");
|
||||
throw new EnableException("IP setting still 0.0.0.0 - Configure Alternative_IP/IP that connects to the Proxy server.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EnableException If IP setting is unset
|
||||
*/
|
||||
@ -111,19 +99,22 @@ public class BungeeServerInfo extends ServerInfo {
|
||||
Server proxy = createServerObject();
|
||||
|
||||
fromDatabase.save(proxy);
|
||||
Server stored = fromDatabase.load(null)
|
||||
.orElseThrow(() -> new EnableException("BungeeCord registration failed (DB)"));
|
||||
Server stored = fromDatabase.load(proxy.getUuid())
|
||||
.orElseThrow(() -> new EnableException("Server registration to database failed"));
|
||||
|
||||
fromFile.save(stored);
|
||||
return stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EnableException If IP setting is unset
|
||||
*/
|
||||
private Server createServerObject() {
|
||||
ServerUUID serverUUID = generateNewUUID();
|
||||
String accessAddress = addresses.getAccessAddress().orElseThrow(() -> new EnableException("Velocity can not have '0.0.0.0' or '' as an address. Set up 'Server.IP' setting."));
|
||||
String accessAddress = getAddress();
|
||||
return new Server(-1, serverUUID, "BungeeCord", accessAddress, true, currentVersion);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getAddress() {
|
||||
return addresses.getAccessAddress()
|
||||
.orElse(addresses.isWebserverEnabled() ? addresses.getFallbackLocalhostAddress() : null);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ import utilities.mocks.BungeeMockComponent;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
/**
|
||||
@ -74,7 +75,7 @@ class BungeeSystemTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void bungeeDoesNotEnableWithDefaultIP() {
|
||||
void bungeeEnablesWithDefaultIP() {
|
||||
PlanSystem bungeeSystem = component.getPlanSystem();
|
||||
try {
|
||||
PlanConfig config = bungeeSystem.getConfigSystem().getConfig();
|
||||
@ -86,8 +87,8 @@ class BungeeSystemTest {
|
||||
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
dbSystem.setActiveDatabase(db);
|
||||
|
||||
EnableException thrown = assertThrows(EnableException.class, bungeeSystem::enable);
|
||||
assertEquals("IP setting still 0.0.0.0 - Configure Alternative_IP/IP that connects to the Proxy server.", thrown.getMessage());
|
||||
bungeeSystem.enable();
|
||||
assertTrue(bungeeSystem.isEnabled());
|
||||
} finally {
|
||||
bungeeSystem.disable();
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ public class LinkCommands {
|
||||
.orElseThrow(() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_SERVER_NOT_FOUND, identifier)));
|
||||
}
|
||||
|
||||
String address = getAddress(sender) + "/server/" + Html.encodeToURL(server.getName());
|
||||
String address = getAddress(sender) + "/server/" + Html.encodeToURL(server.getUuid().toString());
|
||||
sender.buildMessage()
|
||||
.addPart(colors.getMainColor() + locale.getString(CommandLang.LINK_SERVER))
|
||||
.apply(builder -> linkTo(builder, sender, address))
|
||||
@ -126,7 +126,7 @@ public class LinkCommands {
|
||||
String serversListed = dbSystem.getDatabase()
|
||||
.query(ServerQueries.fetchPlanServerInformationCollection())
|
||||
.stream().sorted()
|
||||
.map(server -> m + server.getId().orElse(0) + "::" + t + server.getName() + "::" + s + server.getUuid() + "::" + s + server.getPlanVersion() + "\n")
|
||||
.map(server -> m + server.getId().orElse(0) + "::" + t + server.getIdentifiableName() + "::" + s + server.getUuid() + "::" + s + server.getPlanVersion() + "\n")
|
||||
.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
|
||||
.toString();
|
||||
sender.buildMessage()
|
||||
@ -195,7 +195,7 @@ public class LinkCommands {
|
||||
.addPart(colors.getMainColor() + locale.getString(CommandLang.LINK_NETWORK))
|
||||
.apply(builder -> linkTo(builder, sender, address))
|
||||
.send();
|
||||
if (dbSystem.getDatabase().query(ServerQueries.fetchProxyServerInformation()).isEmpty()) {
|
||||
if (dbSystem.getDatabase().query(ServerQueries.fetchProxyServers()).isEmpty()) {
|
||||
throw new IllegalArgumentException(locale.getString(CommandLang.NOTIFY_NO_NETWORK));
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ public class PluginStatusCommands {
|
||||
Database database = dbSystem.getDatabase();
|
||||
|
||||
String updateAvailable = versionChecker.isNewVersionAvailable() ? yes : no;
|
||||
String proxyAvailable = database.query(ServerQueries.fetchProxyServerInformation()).isPresent() ? yes : no;
|
||||
String proxyAvailable = database.query(ServerQueries.fetchProxyServers()).isEmpty() ? no : yes;
|
||||
|
||||
|
||||
String[] messages = {
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.domain.datatransfer.graphs;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents multiple graphs of same type.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class GraphCollection<T> {
|
||||
|
||||
private final List<T> graphs;
|
||||
private final String color;
|
||||
|
||||
public GraphCollection(List<T> graphs, String color) {
|
||||
this.graphs = graphs;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public List<T> getGraphs() {
|
||||
return graphs;
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
GraphCollection<?> that = (GraphCollection<?>) o;
|
||||
return Objects.equals(getGraphs(), that.getGraphs()) && Objects.equals(getColor(), that.getColor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getGraphs(), getColor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GraphCollection{" +
|
||||
"graphs=" + graphs +
|
||||
", color='" + color + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.domain.datatransfer.graphs;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.datatransfer.ServerDto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a line graph of some server so that they can be stacked.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class ServerSpecificLineGraph {
|
||||
|
||||
private final List<Double[]> points;
|
||||
private final ServerDto server;
|
||||
|
||||
public ServerSpecificLineGraph(List<Double[]> points, ServerDto server) {
|
||||
this.points = points;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public List<Double[]> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
public ServerDto getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ServerSpecificLineGraph that = (ServerSpecificLineGraph) o;
|
||||
return Objects.equals(getPoints(), that.getPoints()) && Objects.equals(getServer(), that.getServer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getPoints(), getServer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerSpecificLineGraph{" +
|
||||
"points=" + points +
|
||||
", server=" + server +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -75,7 +75,7 @@ public class ExportScheduler extends PluginRunnable {
|
||||
|
||||
private void scheduleExport() {
|
||||
Database database = dbSystem.getDatabase();
|
||||
boolean hasProxy = database.query(ServerQueries.fetchProxyServerInformation()).isPresent();
|
||||
boolean hasProxy = !database.query(ServerQueries.fetchProxyServers()).isEmpty();
|
||||
if (serverInfo.getServer().isNotProxy() && hasProxy) {
|
||||
return;
|
||||
}
|
||||
|
@ -157,6 +157,7 @@ public class NetworkPageExporter extends FileExporter {
|
||||
"network/sessionsOverview",
|
||||
"network/playerbaseOverview",
|
||||
"graph?type=playersOnline&server=" + serverUUID,
|
||||
"graph?type=playersOnlineProxies",
|
||||
"graph?type=uniqueAndNew",
|
||||
"graph?type=hourlyUniqueAndNew",
|
||||
"graph?type=serverPie",
|
||||
|
@ -118,12 +118,32 @@ public class JSONFactory {
|
||||
|
||||
Database database = dbSystem.getDatabase();
|
||||
|
||||
ServerUUID mainServerUUID = database.query(ServerQueries.fetchProxyServerInformation()).map(Server::getUuid).orElse(serverInfo.getServerUUID());
|
||||
Map<UUID, ExtensionTabData> pluginData = database.query(new ExtensionServerTableDataQuery(mainServerUUID, xMostRecentPlayers));
|
||||
List<ServerUUID> mainServerUUIDs = database.query(ServerQueries.fetchProxyServers())
|
||||
.stream()
|
||||
.map(Server::getUuid)
|
||||
.collect(Collectors.toList());
|
||||
if (mainServerUUIDs.isEmpty()) mainServerUUIDs.add(serverInfo.getServerUUID());
|
||||
|
||||
Map<UUID, ExtensionTabData> allPluginData = new HashMap<>();
|
||||
|
||||
for (ServerUUID serverUUID : mainServerUUIDs) {
|
||||
Map<UUID, ExtensionTabData> pluginData = database.query(new ExtensionServerTableDataQuery(serverUUID, xMostRecentPlayers));
|
||||
for (Map.Entry<UUID, ExtensionTabData> entry : pluginData.entrySet()) {
|
||||
UUID playerUUID = entry.getKey();
|
||||
ExtensionTabData dataFromServer = entry.getValue();
|
||||
ExtensionTabData alreadyIncludedData = allPluginData.get(playerUUID);
|
||||
if (alreadyIncludedData == null) {
|
||||
allPluginData.put(playerUUID, dataFromServer);
|
||||
} else {
|
||||
alreadyIncludedData.combine(dataFromServer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new PlayersTableJSONCreator(
|
||||
database.query(new NetworkTablePlayersQuery(System.currentTimeMillis(), playtimeThreshold, xMostRecentPlayers)),
|
||||
pluginData,
|
||||
allPluginData,
|
||||
openPlayerLinksInNewTab,
|
||||
formatters, locale,
|
||||
true // players page
|
||||
|
@ -20,6 +20,9 @@ import com.djrapitops.plan.delivery.domain.DateMap;
|
||||
import com.djrapitops.plan.delivery.domain.DateObj;
|
||||
import com.djrapitops.plan.delivery.domain.JoinAddressCount;
|
||||
import com.djrapitops.plan.delivery.domain.JoinAddressCounts;
|
||||
import com.djrapitops.plan.delivery.domain.datatransfer.ServerDto;
|
||||
import com.djrapitops.plan.delivery.domain.datatransfer.graphs.GraphCollection;
|
||||
import com.djrapitops.plan.delivery.domain.datatransfer.graphs.ServerSpecificLineGraph;
|
||||
import com.djrapitops.plan.delivery.domain.mutators.MutatorFunctions;
|
||||
import com.djrapitops.plan.delivery.domain.mutators.PingMutator;
|
||||
import com.djrapitops.plan.delivery.domain.mutators.TPSMutator;
|
||||
@ -59,10 +62,7 @@ import net.playeranalytics.plugin.scheduling.TimeAmount;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -495,4 +495,22 @@ public class GraphJSONCreator {
|
||||
.put("join_addresses_by_date", joinAddressCounts)
|
||||
.build();
|
||||
}
|
||||
|
||||
public GraphCollection<ServerSpecificLineGraph> proxyPlayersOnlineGraphs() {
|
||||
Database db = dbSystem.getDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
|
||||
|
||||
List<ServerSpecificLineGraph> proxyGraphs = new ArrayList<>();
|
||||
for (Server proxy : db.query(ServerQueries.fetchProxyServers())) {
|
||||
ServerUUID proxyUUID = proxy.getUuid();
|
||||
List<Double[]> points = Lists.map(
|
||||
db.query(TPSQueries.fetchPlayersOnlineOfServer(halfYearAgo, now, proxyUUID)),
|
||||
point -> Point.fromDateObj(point).toArray()
|
||||
);
|
||||
proxyGraphs.add(new ServerSpecificLineGraph(points, ServerDto.fromServer(proxy)));
|
||||
}
|
||||
|
||||
return new GraphCollection<>(proxyGraphs, theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE));
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import com.djrapitops.plan.delivery.webserver.http.WebServer;
|
||||
import com.djrapitops.plan.identification.Server;
|
||||
import com.djrapitops.plan.identification.properties.ServerProperties;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.config.paths.ExportSettings;
|
||||
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
|
||||
@ -78,7 +79,11 @@ public class Addresses {
|
||||
public Optional<String> getAccessAddress() {
|
||||
WebServer webServer = this.webserver.get();
|
||||
if (!webServer.isEnabled()) {
|
||||
return Optional.of(getFallbackExternalAddress());
|
||||
if (config.isTrue(ExportSettings.SERVER_PAGE)) {
|
||||
return Optional.of(getFallbackExternalAddress());
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
return getIP().map(ip -> webServer.getProtocol() + "://" + ip);
|
||||
}
|
||||
@ -100,12 +105,15 @@ public class Addresses {
|
||||
}
|
||||
|
||||
public Optional<String> getProxyServerAddress() {
|
||||
return dbSystem.getDatabase().query(ServerQueries.fetchProxyServerInformation()).map(Server::getWebAddress)
|
||||
.filter(this::isValidAddress);
|
||||
return dbSystem.getDatabase().query(ServerQueries.fetchProxyServers())
|
||||
.stream()
|
||||
.map(Server::getWebAddress)
|
||||
.filter(this::isValidAddress)
|
||||
.findAny();
|
||||
}
|
||||
|
||||
private boolean isValidAddress(String address) {
|
||||
return !address.isEmpty() && !"0.0.0.0".equals(address);
|
||||
return address != null && !address.isEmpty() && !"0.0.0.0".equals(address);
|
||||
}
|
||||
|
||||
public Optional<String> getServerPropertyIP() {
|
||||
@ -113,6 +121,10 @@ public class Addresses {
|
||||
return isValidAddress(ip) ? Optional.of(ip) : Optional.empty();
|
||||
}
|
||||
|
||||
public boolean isWebserverEnabled() {
|
||||
return webserver.get().isEnabled();
|
||||
}
|
||||
|
||||
public String getBasePath(String address) {
|
||||
String basePath = address
|
||||
.replace("http://", "")
|
||||
|
@ -32,6 +32,7 @@ public enum DataID {
|
||||
GRAPH_PERFORMANCE,
|
||||
GRAPH_OPTIMIZED_PERFORMANCE,
|
||||
GRAPH_ONLINE,
|
||||
GRAPH_ONLINE_PROXIES,
|
||||
GRAPH_UNIQUE_NEW,
|
||||
GRAPH_HOURLY_UNIQUE_NEW,
|
||||
GRAPH_CALENDAR,
|
||||
|
@ -91,6 +91,7 @@ public class GraphsJSONResolver extends JSONResolver {
|
||||
@ExampleObject(value = "performance", description = "Deprecated, use optimizedPerformance"),
|
||||
@ExampleObject("optimizedPerformance"),
|
||||
@ExampleObject("playersOnline"),
|
||||
@ExampleObject("playersOnlineProxies"),
|
||||
@ExampleObject("uniqueAndNew"),
|
||||
@ExampleObject("hourlyUniqueAndNew"),
|
||||
@ExampleObject("serverCalendar"),
|
||||
@ -160,6 +161,8 @@ public class GraphsJSONResolver extends JSONResolver {
|
||||
return DataID.GRAPH_OPTIMIZED_PERFORMANCE;
|
||||
case "playersOnline":
|
||||
return DataID.GRAPH_ONLINE;
|
||||
case "playersOnlineProxies":
|
||||
return DataID.GRAPH_ONLINE_PROXIES;
|
||||
case "uniqueAndNew":
|
||||
return DataID.GRAPH_UNIQUE_NEW;
|
||||
case "hourlyUniqueAndNew":
|
||||
@ -241,6 +244,8 @@ public class GraphsJSONResolver extends JSONResolver {
|
||||
return graphJSON.playerHostnamePieJSONAsMap();
|
||||
case GRAPH_WORLD_MAP:
|
||||
return graphJSON.geolocationGraphsJSONAsMap();
|
||||
case GRAPH_ONLINE_PROXIES:
|
||||
return graphJSON.proxyPlayersOnlineGraphs();
|
||||
case JOIN_ADDRESSES_BY_DAY:
|
||||
try {
|
||||
return graphJSON.joinAddressesByDay(
|
||||
|
@ -20,6 +20,7 @@ import com.djrapitops.plan.delivery.domain.datatransfer.ServerDto;
|
||||
import com.djrapitops.plan.delivery.web.resolver.NoAuthResolver;
|
||||
import com.djrapitops.plan.delivery.web.resolver.Response;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.Request;
|
||||
import com.djrapitops.plan.gathering.ServerSensor;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
|
||||
@ -44,11 +45,13 @@ import java.util.stream.Collectors;
|
||||
public class NetworkMetadataJSONResolver implements NoAuthResolver {
|
||||
|
||||
private final ServerInfo serverInfo;
|
||||
private final ServerSensor<?> serverSensor;
|
||||
private final DBSystem dbSystem;
|
||||
|
||||
@Inject
|
||||
public NetworkMetadataJSONResolver(ServerInfo serverInfo, DBSystem dbSystem) {
|
||||
public NetworkMetadataJSONResolver(ServerInfo serverInfo, ServerSensor<?> serverSensor, DBSystem dbSystem) {
|
||||
this.serverInfo = serverInfo;
|
||||
this.serverSensor = serverSensor;
|
||||
this.dbSystem = dbSystem;
|
||||
}
|
||||
|
||||
@ -70,6 +73,7 @@ public class NetworkMetadataJSONResolver implements NoAuthResolver {
|
||||
.sorted()
|
||||
.collect(Collectors.toList()))
|
||||
.put("currentServer", ServerDto.fromServer(serverInfo.getServer()))
|
||||
.put("usingRedisBungee", serverSensor.usingRedisBungee())
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
@ -60,4 +60,8 @@ public interface ServerSensor<W> {
|
||||
default List<String> getOnlinePlayerNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
default boolean usingRedisBungee() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -62,12 +62,15 @@ public class Server implements Comparable<Server> {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static String getIdentifiableName(String name, int id) {
|
||||
return !"Plan".equalsIgnoreCase(name) ? name : "Server " + id;
|
||||
public static String getIdentifiableName(String name, int id, boolean proxy) {
|
||||
String serverPrefix = proxy ? "Proxy " : "Server ";
|
||||
return "Plan".equalsIgnoreCase(name) || "Proxy".equalsIgnoreCase(name)
|
||||
? serverPrefix + id
|
||||
: name;
|
||||
}
|
||||
|
||||
public String getIdentifiableName() {
|
||||
return getIdentifiableName(name, id);
|
||||
return getIdentifiableName(name, id, proxy);
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
|
@ -45,8 +45,7 @@ public class ServerDBLoader implements ServerLoader {
|
||||
public Optional<Server> load(ServerUUID serverUUID) {
|
||||
try {
|
||||
if (serverUUID == null) {
|
||||
// Assumes that we want the server that has single entry in the database, the proxy
|
||||
return dbSystem.getDatabase().query(ServerQueries.fetchProxyServerInformation());
|
||||
throw new EnableException("Attempted to load a server with null UUID (Old behavior that is no longer supported)");
|
||||
}
|
||||
|
||||
return dbSystem.getDatabase().query(
|
||||
|
@ -84,7 +84,7 @@ public class ServerFileLoader implements ServerLoader {
|
||||
ServerUUID serverUUID = ServerUUID.fromString(serverUUIDString);
|
||||
String name = config.getNode(PluginSettings.SERVER_NAME.getPath())
|
||||
.map(ConfigNode::getString)
|
||||
.orElse("Proxy");
|
||||
.orElse("Plan");
|
||||
String address = serverInfoConfig.get().getString("Server.Web_address");
|
||||
|
||||
return Optional.of(new Server(id, serverUUID, name, address, false, currentVersion));
|
||||
|
@ -373,10 +373,12 @@ public enum HtmlLang implements Lang {
|
||||
PLAYER_COUNT("html.label.unit.playerCount", "Player Count"),
|
||||
|
||||
WARNING_NO_GAME_SERVERS("html.description.noGameServers", "Some data requires Plan to be installed on game servers."),
|
||||
WARNING_PERFORMANCE_NO_GAME_SERVERS("html.description.performanceNoGameServers", "TPS, Entity or Chunk data is not gathered from proxy servers since they don't have game tick loop."),
|
||||
WARNING_NO_GEOLOCATIONS("html.description.noGeolocations", "Geolocation gathering needs to be enabled in the config (Accept GeoLite2 EULA)."),
|
||||
WARNING_NO_SPONGE_CHUNKS("html.description.noSpongeChunks", "Chunks unavailable on Sponge"),
|
||||
WARNING_NO_DATA_24H("html.description.noData24h", "Server has not sent data for over 24 hours"),
|
||||
WARNING_NO_DATA_30D("html.description.noData30d", "Server has not sent data for over 30 days"),
|
||||
WARNING_NO_DATA_24H("html.description.noData24h", "Server has not sent data for over 24 hours."),
|
||||
WARNING_NO_DATA_7D("html.description.noData7d", "Server has not sent data for over 7 days."),
|
||||
WARNING_NO_DATA_30D("html.description.noData30d", "Server has not sent data for over 30 days."),
|
||||
EXPORTED_TITLE("html.label.exported", "Data export time");
|
||||
|
||||
private final String key;
|
||||
|
@ -28,6 +28,7 @@ import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.*;
|
||||
import com.djrapitops.plan.utilities.dev.Untrusted;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -50,17 +51,19 @@ public class PluginBooleanGroupFilter extends MultiOptionFilter {
|
||||
}
|
||||
|
||||
private static Query<List<PluginBooleanOption>> pluginBooleanOptionsQuery() {
|
||||
@Language("SQL")
|
||||
String selectOptions = SELECT + DISTINCT +
|
||||
"server." + ServerTable.ID + " as server_id," +
|
||||
"server." + ServerTable.NAME + " as server_name," +
|
||||
"server." + ServerTable.PROXY + " as is_proxy," +
|
||||
"plugin." + ExtensionPluginTable.PLUGIN_NAME + " as plugin_name," +
|
||||
"provider." + ExtensionProviderTable.TEXT + " as provider_text" +
|
||||
FROM + ServerTable.TABLE_NAME + " server" +
|
||||
INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " plugin on plugin." + ExtensionPluginTable.SERVER_UUID + "=server." + ServerTable.SERVER_UUID +
|
||||
INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " provider on provider." + ExtensionProviderTable.PLUGIN_ID + "=plugin." + ExtensionPluginTable.ID +
|
||||
INNER_JOIN + ExtensionPlayerValueTable.TABLE_NAME + " value on value." + ExtensionPlayerValueTable.PROVIDER_ID + "=provider." + ExtensionProviderTable.ID +
|
||||
WHERE + "value." + ExtensionPlayerValueTable.BOOLEAN_VALUE + " IS NOT NULL" +
|
||||
ORDER_BY + "server_name ASC, plugin_name ASC, provider_text ASC";
|
||||
INNER_JOIN + ExtensionPlayerValueTable.TABLE_NAME + " v on v." + ExtensionPlayerValueTable.PROVIDER_ID + "=provider." + ExtensionProviderTable.ID +
|
||||
WHERE + "v." + ExtensionPlayerValueTable.BOOLEAN_VALUE + " IS NOT NULL" +
|
||||
ORDER_BY + "server_name, plugin_name, provider_text";
|
||||
return new QueryAllStatement<>(selectOptions) {
|
||||
@Override
|
||||
public List<PluginBooleanOption> processResults(ResultSet set) throws SQLException {
|
||||
@ -68,10 +71,11 @@ public class PluginBooleanGroupFilter extends MultiOptionFilter {
|
||||
while (set.next()) {
|
||||
int serverId = set.getInt("server_id");
|
||||
String serverName = set.getString("server_name");
|
||||
boolean proxy = set.getBoolean("is_proxy");
|
||||
String pluginName = set.getString("plugin_name");
|
||||
String providerText = set.getString("provider_text");
|
||||
options.add(new PluginBooleanOption(
|
||||
Server.getIdentifiableName(serverName, serverId),
|
||||
Server.getIdentifiableName(serverName, serverId, proxy),
|
||||
pluginName,
|
||||
providerText
|
||||
));
|
||||
|
@ -85,6 +85,7 @@ public class PluginGroupsFilter extends MultiOptionFilter {
|
||||
"pl." + ExtensionPluginTable.PLUGIN_NAME + " as plugin_name," +
|
||||
"s." + ServerTable.NAME + " as server_name," +
|
||||
"s." + ServerTable.ID + " as server_id," +
|
||||
"s." + ServerTable.PROXY + " as is_proxy," +
|
||||
"pl." + ExtensionPluginTable.SERVER_UUID + " as server_uuid," +
|
||||
"pr." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," +
|
||||
"gr." + ExtensionGroupsTable.GROUP_NAME + " as group_name" +
|
||||
@ -106,7 +107,8 @@ public class PluginGroupsFilter extends MultiOptionFilter {
|
||||
ProviderIdentifier identifier = new ProviderIdentifier(serverUUID, plugin, provider);
|
||||
identifier.setServerName(Server.getIdentifiableName(
|
||||
set.getString("server_name"),
|
||||
set.getInt("server_id")
|
||||
set.getInt("server_id"),
|
||||
set.getBoolean("is_proxy")
|
||||
));
|
||||
|
||||
String group = set.getString("group_name");
|
||||
|
@ -176,7 +176,7 @@ public class JoinAddressQueries {
|
||||
WHERE + SessionsTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID +
|
||||
AND + SessionsTable.SESSION_START + ">?" +
|
||||
AND + SessionsTable.SESSION_START + "<=?" +
|
||||
GROUP_BY + "date,j." + JoinAddressTable.ID;
|
||||
GROUP_BY + "date,j." + JoinAddressTable.JOIN_ADDRESS;
|
||||
|
||||
return db.query(new QueryStatement<>(selectAddresses, 1000) {
|
||||
@Override
|
||||
@ -219,7 +219,7 @@ public class JoinAddressQueries {
|
||||
LEFT_JOIN + JoinAddressTable.TABLE_NAME + " j on s." + SessionsTable.JOIN_ADDRESS_ID + "=j." + JoinAddressTable.ID +
|
||||
WHERE + SessionsTable.SESSION_START + ">?" +
|
||||
AND + SessionsTable.SESSION_START + "<=?" +
|
||||
GROUP_BY + "date,j." + JoinAddressTable.ID;
|
||||
GROUP_BY + "date,j." + JoinAddressTable.JOIN_ADDRESS;
|
||||
|
||||
return db.query(new QueryStatement<>(selectAddresses, 1000) {
|
||||
@Override
|
||||
|
@ -170,7 +170,7 @@ public class KillQueries {
|
||||
new PlayerKill.Killer(killer, killerName),
|
||||
new PlayerKill.Victim(victim, victimName, set.getLong("victim_" + UsersTable.REGISTERED)),
|
||||
new ServerIdentifier(ServerUUID.fromString(set.getString(KillsTable.SERVER_UUID)),
|
||||
Server.getIdentifiableName(set.getString("server_name"), set.getInt("server_id"))
|
||||
Server.getIdentifiableName(set.getString("server_name"), set.getInt("server_id"), false)
|
||||
), weapon, date
|
||||
));
|
||||
}
|
||||
|
@ -146,32 +146,29 @@ public class ServerQueries {
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Optional<Server>> fetchProxyServerInformation() {
|
||||
public static Query<List<Server>> fetchProxyServers() {
|
||||
String sql = SELECT + '*' + FROM + ServerTable.TABLE_NAME +
|
||||
WHERE + ServerTable.INSTALLED + "=?" +
|
||||
AND + ServerTable.PROXY + "=?" +
|
||||
LIMIT + '1';
|
||||
return new QueryStatement<>(sql) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setBoolean(1, true);
|
||||
statement.setBoolean(2, true);
|
||||
}
|
||||
AND + ServerTable.PROXY + "=?";
|
||||
return db -> db.queryList(sql, set ->
|
||||
new Server(
|
||||
set.getInt(ServerTable.ID),
|
||||
ServerUUID.fromString(set.getString(ServerTable.SERVER_UUID)),
|
||||
set.getString(ServerTable.NAME),
|
||||
set.getString(ServerTable.WEB_ADDRESS),
|
||||
set.getBoolean(ServerTable.PROXY),
|
||||
set.getString(ServerTable.PLAN_VERSION)
|
||||
), true, true
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Server> processResults(ResultSet set) throws SQLException {
|
||||
if (set.next()) {
|
||||
return Optional.of(new Server(
|
||||
set.getInt(ServerTable.ID),
|
||||
ServerUUID.fromString(set.getString(ServerTable.SERVER_UUID)),
|
||||
set.getString(ServerTable.NAME),
|
||||
set.getString(ServerTable.WEB_ADDRESS),
|
||||
set.getBoolean(ServerTable.PROXY),
|
||||
set.getString(ServerTable.PLAN_VERSION)));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
};
|
||||
public static Query<List<ServerUUID>> fetchProxyServerUUIDs() {
|
||||
String sql = SELECT + ServerTable.SERVER_UUID + FROM + ServerTable.TABLE_NAME +
|
||||
WHERE + ServerTable.INSTALLED + "=?" +
|
||||
AND + ServerTable.PROXY + "=?";
|
||||
return db -> db.queryList(sql, set -> ServerUUID.fromString(set.getString(ServerTable.SERVER_UUID)),
|
||||
true, true
|
||||
);
|
||||
}
|
||||
|
||||
public static Query<List<String>> fetchGameServerNames() {
|
||||
@ -185,7 +182,7 @@ public class ServerQueries {
|
||||
public List<String> processResults(ResultSet set) throws SQLException {
|
||||
List<String> names = new ArrayList<>();
|
||||
while (set.next()) {
|
||||
names.add(Server.getIdentifiableName(set.getString(ServerTable.NAME), set.getInt(ServerTable.ID)));
|
||||
names.add(Server.getIdentifiableName(set.getString(ServerTable.NAME), set.getInt(ServerTable.ID), false));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
@ -194,7 +191,7 @@ public class ServerQueries {
|
||||
|
||||
public static Query<Map<ServerUUID, String>> fetchServerNames() {
|
||||
String sql = Select.from(ServerTable.TABLE_NAME,
|
||||
ServerTable.ID, ServerTable.SERVER_UUID, ServerTable.NAME)
|
||||
ServerTable.ID, ServerTable.SERVER_UUID, ServerTable.NAME, ServerTable.PROXY)
|
||||
.toString();
|
||||
|
||||
return new QueryAllStatement<>(sql) {
|
||||
@ -203,7 +200,9 @@ public class ServerQueries {
|
||||
Map<ServerUUID, String> names = new HashMap<>();
|
||||
while (set.next()) {
|
||||
ServerUUID serverUUID = ServerUUID.fromString(set.getString(ServerTable.SERVER_UUID));
|
||||
names.put(serverUUID, Server.getIdentifiableName(set.getString(ServerTable.NAME), set.getInt(ServerTable.ID)));
|
||||
names.put(serverUUID, Server.getIdentifiableName(set.getString(ServerTable.NAME),
|
||||
set.getInt(ServerTable.ID),
|
||||
set.getBoolean(ServerTable.PROXY)));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
@ -186,7 +186,8 @@ public class SessionQueries {
|
||||
ServerName serverName = new ServerName(
|
||||
Server.getIdentifiableName(
|
||||
set.getString("server_name"),
|
||||
set.getInt("server_id")
|
||||
set.getInt("server_id"),
|
||||
false
|
||||
));
|
||||
extraData.put(ServerName.class, serverName);
|
||||
|
||||
@ -777,7 +778,8 @@ public class SessionQueries {
|
||||
while (set.next()) {
|
||||
String name = Server.getIdentifiableName(
|
||||
set.getString(ServerTable.NAME),
|
||||
set.getInt(ServerTable.ID)
|
||||
set.getInt(ServerTable.ID),
|
||||
false
|
||||
);
|
||||
playtimePerServer.put(name, set.getLong("playtime"));
|
||||
}
|
||||
|
@ -58,9 +58,10 @@ public class ExtensionDisableOnGameServerTask extends TaskSystem.Task {
|
||||
|
||||
private void checkAndDisableProxyExtensions(String pluginName) {
|
||||
Database db = dbSystem.getDatabase();
|
||||
db.query(ServerQueries.fetchProxyServerInformation())
|
||||
db.query(ServerQueries.fetchProxyServers())
|
||||
.stream()
|
||||
.map(Server::getUuid)
|
||||
.ifPresent(proxyUUID -> checkAndDisableProxyExtension(proxyUUID, pluginName));
|
||||
.forEach(proxyUUID -> checkAndDisableProxyExtension(proxyUUID, pluginName));
|
||||
}
|
||||
|
||||
private void checkAndDisableProxyExtension(ServerUUID proxyUUID, String pluginName) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
# https://github.com/plan-player-analytics/Plan/wiki/Bukkit-Configuration
|
||||
# -----------------------------------------------------
|
||||
Server:
|
||||
ServerName: Plan
|
||||
IP: 0.0.0.0
|
||||
Network:
|
||||
Name: Plan
|
||||
|
@ -104,7 +104,7 @@ class ExportRegressionTest {
|
||||
String address = "http://" + webserver.getHost() + ":" + webserver.getMappedPort(8080)
|
||||
+ (endpoint.startsWith("/") ? endpoint : '/' + endpoint);
|
||||
List<LogEntry> logs = getLogsAfterRequestToAddress(driver, address);
|
||||
assertNoLogs(logs);
|
||||
assertNoLogs(logs, endpoint);
|
||||
})
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ public class ExportTestUtilities {
|
||||
/* Static utility method class */
|
||||
}
|
||||
|
||||
public static void assertNoLogs(List<LogEntry> logs) {
|
||||
public static void assertNoLogs(List<LogEntry> logs, String endpoint) {
|
||||
List<String> loggedLines = logs.stream()
|
||||
.map(log -> "\n" + log.getLevel().getName() + " " + log.getMessage())
|
||||
.filter(log -> !StringUtils.containsAny(log,
|
||||
"fonts.gstatic.com", "fonts.googleapis.com", "cdn.jsdelivr.net"
|
||||
)).toList();
|
||||
assertTrue(loggedLines.isEmpty(), () -> "Browser console included " + loggedLines.size() + " logs: " + loggedLines);
|
||||
assertTrue(loggedLines.isEmpty(), () -> "Loading " + endpoint + ", Browser console included " + loggedLines.size() + " logs: " + loggedLines);
|
||||
}
|
||||
|
||||
public static void assertNoLogsExceptFaviconError(List<LogEntry> logs) {
|
||||
|
@ -28,6 +28,7 @@ import utilities.OptionalAssert;
|
||||
import utilities.TestConstants;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -53,8 +54,8 @@ public interface ServerQueriesTest extends DatabaseTestPreparer {
|
||||
|
||||
@Test
|
||||
default void bungeeInformationIsStored() {
|
||||
Optional<Server> bungeeInfo = db().query(ServerQueries.fetchProxyServerInformation());
|
||||
assertFalse(bungeeInfo.isPresent());
|
||||
List<Server> proxies = db().query(ServerQueries.fetchProxyServers());
|
||||
assertTrue(proxies.isEmpty());
|
||||
|
||||
ServerUUID bungeeUUID = ServerUUID.randomUUID();
|
||||
Server bungeeCord = new Server(bungeeUUID, "BungeeCord", "Random:1234", TestConstants.VERSION);
|
||||
@ -65,9 +66,9 @@ public interface ServerQueriesTest extends DatabaseTestPreparer {
|
||||
|
||||
bungeeCord.setId(2);
|
||||
|
||||
bungeeInfo = db().query(ServerQueries.fetchProxyServerInformation());
|
||||
assertTrue(bungeeInfo.isPresent());
|
||||
assertEquals(bungeeCord, bungeeInfo.get());
|
||||
proxies = db().query(ServerQueries.fetchProxyServers());
|
||||
assertFalse(proxies.isEmpty());
|
||||
assertEquals(List.of(bungeeCord), proxies);
|
||||
|
||||
Optional<Server> found = db().query(ServerQueries.fetchServerMatchingIdentifier(bungeeUUID));
|
||||
OptionalAssert.equals(bungeeCord.getWebAddress(), found.map(Server::getWebAddress));
|
||||
|
@ -71,7 +71,7 @@ public class TestSettings {
|
||||
|
||||
public static Collection<Setting> getProxySettings() throws IllegalAccessException {
|
||||
List<Setting> settings = new ArrayList<>();
|
||||
for (Class settingKeyClass : new Class[]{
|
||||
for (Class<?> settingKeyClass : new Class[]{
|
||||
DatabaseSettings.class,
|
||||
DisplaySettings.class,
|
||||
ExportSettings.class,
|
||||
@ -84,7 +84,6 @@ public class TestSettings {
|
||||
settings.addAll(FieldFetcher.getPublicStaticFields(settingKeyClass, Setting.class));
|
||||
}
|
||||
// Server settings contained in the key classes, remove
|
||||
settings.remove(PluginSettings.SERVER_NAME);
|
||||
settings.remove(PluginSettings.PROXY_COPY_CONFIG);
|
||||
settings.remove(DatabaseSettings.TYPE);
|
||||
settings.remove(DisplaySettings.WORLD_ALIASES);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useEffect, useMemo, useState} from 'react';
|
||||
import CardTabs from "../../CardTabs";
|
||||
import {
|
||||
faDragon,
|
||||
@ -39,41 +39,7 @@ const PerformanceGraphsCard = ({data}) => {
|
||||
const {t} = useTranslation();
|
||||
const {networkMetadata} = useMetadata();
|
||||
|
||||
if (!data || !Object.values(data).length) return <CardLoader/>
|
||||
|
||||
const zones = {
|
||||
tps: [{
|
||||
value: data.zones.tpsThresholdMed,
|
||||
color: data.colors.low
|
||||
}, {
|
||||
value: data.zones.tpsThresholdHigh,
|
||||
color: data.colors.med
|
||||
}, {
|
||||
value: 30,
|
||||
color: data.colors.high
|
||||
}],
|
||||
disk: [{
|
||||
value: data.zones.diskThresholdMed,
|
||||
color: data.colors.low
|
||||
}, {
|
||||
value: data.zones.diskThresholdHigh,
|
||||
color: data.colors.med
|
||||
}, {
|
||||
value: Number.MAX_VALUE,
|
||||
color: data.colors.high
|
||||
}]
|
||||
};
|
||||
const serverData = [];
|
||||
for (let i = 0; i < data.servers.length; i++) {
|
||||
const server = data.servers[i];
|
||||
const values = data.values[i];
|
||||
serverData.push({
|
||||
serverName: server.serverName,
|
||||
values
|
||||
});
|
||||
}
|
||||
|
||||
const series = {
|
||||
const [performanceSeries, setPerformanceSeries] = useState({
|
||||
players: [],
|
||||
tps: [],
|
||||
cpu: [],
|
||||
@ -81,75 +47,148 @@ const PerformanceGraphsCard = ({data}) => {
|
||||
entities: [],
|
||||
chunks: [],
|
||||
disk: []
|
||||
}
|
||||
});
|
||||
|
||||
const spline = 'spline';
|
||||
useEffect(() => {
|
||||
if (!data?.zones) return;
|
||||
|
||||
for (const server of serverData) {
|
||||
series.players.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.playersOnline, color: data.colors.playersOnline, yAxis: 0
|
||||
});
|
||||
series.tps.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.twoDecimals,
|
||||
data: server.values.tps, color: data.colors.high, zones: zones.tps, yAxis: 0
|
||||
});
|
||||
series.cpu.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.twoDecimals,
|
||||
data: server.values.cpu, color: data.colors.cpu, yAxis: 0
|
||||
});
|
||||
series.ram.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.ram, color: data.colors.ram, yAxis: 0
|
||||
});
|
||||
series.entities.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.entities, color: data.colors.entities, yAxis: 0
|
||||
});
|
||||
series.chunks.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.chunks, color: data.colors.chunks, yAxis: 0
|
||||
});
|
||||
series.disk.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.disk, color: data.colors.high, zones: zones.disk, yAxis: 0
|
||||
});
|
||||
}
|
||||
const zones = {
|
||||
tps: [{
|
||||
value: data.zones.tpsThresholdMed,
|
||||
color: data.colors.low
|
||||
}, {
|
||||
value: data.zones.tpsThresholdHigh,
|
||||
color: data.colors.med
|
||||
}, {
|
||||
value: 30,
|
||||
color: data.colors.high
|
||||
}],
|
||||
disk: [{
|
||||
value: data.zones.diskThresholdMed,
|
||||
color: data.colors.low
|
||||
}, {
|
||||
value: data.zones.diskThresholdHigh,
|
||||
color: data.colors.med
|
||||
}, {
|
||||
value: Number.MAX_VALUE,
|
||||
color: data.colors.high
|
||||
}]
|
||||
};
|
||||
|
||||
const serverData = [];
|
||||
for (let i = 0; i < data.servers.length; i++) {
|
||||
const server = data.servers[i];
|
||||
const values = data.values[i];
|
||||
serverData.push({
|
||||
serverName: server.serverName,
|
||||
values
|
||||
});
|
||||
}
|
||||
|
||||
const series = {
|
||||
players: [],
|
||||
tps: [],
|
||||
cpu: [],
|
||||
ram: [],
|
||||
entities: [],
|
||||
chunks: [],
|
||||
disk: []
|
||||
}
|
||||
|
||||
const spline = 'spline';
|
||||
|
||||
const changeColor = (colorHex, index) => {
|
||||
// TODO Convert color somehow using index
|
||||
return colorHex;
|
||||
}
|
||||
|
||||
const minuteResolution = point => {
|
||||
// Ensure that the points can be stacked by moving data to minute level
|
||||
point[0] -= (point[0] % 60000);
|
||||
return point;
|
||||
}
|
||||
|
||||
serverData.forEach((server, i) => {
|
||||
const playersOnlineColor = changeColor(data.colors.playersOnline, i);
|
||||
const tpsColor = changeColor(data.colors.high, i);
|
||||
const tpsZone = [...zones.tps]
|
||||
tpsZone.forEach(zone => zone.color = changeColor(zone.color, i));
|
||||
const cpuColor = changeColor(data.colors.cpu, i);
|
||||
const ramColors = changeColor(data.colors.ram, i);
|
||||
const entitiesColor = changeColor(data.colors.entities, i);
|
||||
const chunksColor = changeColor(data.colors.chunks, i);
|
||||
const diskColor = changeColor(data.colors.high, i);
|
||||
const diskZones = [...zones.disk];
|
||||
diskZones.forEach(zone => zone.color = changeColor(zone.color, i));
|
||||
|
||||
series.players.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.playersOnline.map(minuteResolution), color: playersOnlineColor, yAxis: 0
|
||||
});
|
||||
series.tps.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.twoDecimals,
|
||||
data: server.values.tps.map(minuteResolution), color: tpsColor, zones: tpsZone, yAxis: 0
|
||||
});
|
||||
series.cpu.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.twoDecimals,
|
||||
data: server.values.cpu.map(minuteResolution), color: cpuColor, yAxis: 0
|
||||
});
|
||||
series.ram.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.ram.map(minuteResolution), color: ramColors, yAxis: 0
|
||||
});
|
||||
series.entities.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.entities.map(minuteResolution), color: entitiesColor, yAxis: 0
|
||||
});
|
||||
series.chunks.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.chunks.map(minuteResolution), color: chunksColor, yAxis: 0
|
||||
});
|
||||
series.disk.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.disk.map(minuteResolution), color: diskColor, zones: diskZones, yAxis: 0
|
||||
});
|
||||
});
|
||||
setPerformanceSeries(series);
|
||||
}, [data, setPerformanceSeries])
|
||||
|
||||
const tabs = useMemo(() => [
|
||||
{
|
||||
name: t('html.label.playersOnline'), icon: faUser, color: 'light-blue', href: 'players-online',
|
||||
element: <Tab data={performanceSeries.players} yAxis={yAxisConfigurations.PLAYERS_ONLINE}/>
|
||||
}, {
|
||||
name: t('html.label.tps'), icon: faTachometerAlt, color: 'red', href: 'tps',
|
||||
element: <Tab data={performanceSeries.tps} yAxis={yAxisConfigurations.TPS}/>
|
||||
}, {
|
||||
name: t('html.label.cpu'), icon: faTachometerAlt, color: 'amber', href: 'cpu',
|
||||
element: <Tab data={performanceSeries.cpu} yAxis={yAxisConfigurations.CPU}/>
|
||||
}, {
|
||||
name: t('html.label.ram'), icon: faMicrochip, color: 'light-green', href: 'ram',
|
||||
element: <Tab data={performanceSeries.ram} yAxis={yAxisConfigurations.RAM_OR_DISK}/>
|
||||
}, {
|
||||
name: t('html.label.entities'), icon: faDragon, color: 'purple', href: 'entities',
|
||||
element: <Tab data={performanceSeries.entities} yAxis={yAxisConfigurations.ENTITIES}/>
|
||||
}, {
|
||||
name: t('html.label.loadedChunks'), icon: faMap, color: 'blue-grey', href: 'chunks',
|
||||
element: <Tab data={performanceSeries.chunks} yAxis={yAxisConfigurations.CHUNKS}/>
|
||||
}, {
|
||||
name: t('html.label.diskSpace'), icon: faHdd, color: 'green', href: 'disk',
|
||||
element: <Tab data={performanceSeries.disk} yAxis={yAxisConfigurations.RAM_OR_DISK}/>
|
||||
}, {
|
||||
name: t('html.label.ping'), icon: faSignal, color: 'amber', href: 'ping',
|
||||
element: networkMetadata ? <PingTab identifier={networkMetadata.currentServer.serverUUID}/> :
|
||||
<ChartLoader/>
|
||||
},
|
||||
], [performanceSeries, networkMetadata, t]);
|
||||
|
||||
if (!data || !Object.values(data).length) return <CardLoader/>
|
||||
if (data.errors.length) {
|
||||
return <ErrorViewCard error={data.errors[0]}/>
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTabs tabs={[
|
||||
{
|
||||
name: t('html.label.playersOnline'), icon: faUser, color: 'light-blue', href: 'players-online',
|
||||
element: <Tab data={series.players} yAxis={yAxisConfigurations.PLAYERS_ONLINE}/>
|
||||
}, {
|
||||
name: t('html.label.tps'), icon: faTachometerAlt, color: 'red', href: 'tps',
|
||||
element: <Tab data={series.tps} yAxis={yAxisConfigurations.TPS}/>
|
||||
}, {
|
||||
name: t('html.label.cpu'), icon: faTachometerAlt, color: 'amber', href: 'cpu',
|
||||
element: <Tab data={series.cpu} yAxis={yAxisConfigurations.CPU}/>
|
||||
}, {
|
||||
name: t('html.label.ram'), icon: faMicrochip, color: 'light-green', href: 'ram',
|
||||
element: <Tab data={series.ram} yAxis={yAxisConfigurations.RAM_OR_DISK}/>
|
||||
}, {
|
||||
name: t('html.label.entities'), icon: faDragon, color: 'purple', href: 'entities',
|
||||
element: <Tab data={series.entities} yAxis={yAxisConfigurations.ENTITIES}/>
|
||||
}, {
|
||||
name: t('html.label.loadedChunks'), icon: faMap, color: 'blue-grey', href: 'chunks',
|
||||
element: <Tab data={series.chunks} yAxis={yAxisConfigurations.CHUNKS}/>
|
||||
}, {
|
||||
name: t('html.label.diskSpace'), icon: faHdd, color: 'green', href: 'disk',
|
||||
element: <Tab data={series.disk} yAxis={yAxisConfigurations.RAM_OR_DISK}/>
|
||||
}, {
|
||||
name: t('html.label.ping'), icon: faSignal, color: 'amber', href: 'ping',
|
||||
element: networkMetadata ? <PingTab identifier={networkMetadata.currentServer.serverUUID}/> :
|
||||
<ChartLoader/>
|
||||
},
|
||||
]}/>
|
||||
<CardTabs tabs={tabs}/>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
@ -10,15 +10,34 @@ import {ChartLoader} from "../../../navigation/Loader";
|
||||
import TimeByTimeGraph from "../../../graphs/TimeByTimeGraph";
|
||||
import PlayersOnlineGraph from "../../../graphs/PlayersOnlineGraph";
|
||||
import {useMetadata} from "../../../../hooks/metadataHook";
|
||||
import StackedPlayersOnlineGraph from "../../../graphs/StackedPlayersOnlineGraph";
|
||||
|
||||
const PlayersOnlineTab = () => {
|
||||
const {serverUUID} = useMetadata();
|
||||
const SingleProxyPlayersOnlineGraph = ({serverUUID}) => {
|
||||
const {data, loadingError} = useDataRequest(fetchPlayersOnlineGraph, [serverUUID]);
|
||||
|
||||
if (loadingError) return <ErrorViewBody error={loadingError}/>
|
||||
if (!serverUUID || !data) return <ChartLoader/>;
|
||||
|
||||
return <PlayersOnlineGraph data={data}/>
|
||||
}
|
||||
|
||||
const MultiProxyPlayersOnlineGraph = () => {
|
||||
const {data, loadingError} = useDataRequest(fetchPlayersOnlineGraph, []);
|
||||
|
||||
if (loadingError) return <ErrorViewBody error={loadingError}/>
|
||||
if (!data) return <ChartLoader/>;
|
||||
|
||||
return <PlayersOnlineGraph data={data}/>
|
||||
return <StackedPlayersOnlineGraph data={data}/>
|
||||
}
|
||||
|
||||
const PlayersOnlineTab = () => {
|
||||
const {serverUUID, networkMetadata} = useMetadata();
|
||||
|
||||
if (networkMetadata.usingRedisBungee || networkMetadata.servers.filter(server => server.proxy).length === 1) {
|
||||
return <SingleProxyPlayersOnlineGraph serverUUID={serverUUID}/>
|
||||
} else {
|
||||
return <MultiProxyPlayersOnlineGraph/>
|
||||
}
|
||||
}
|
||||
|
||||
const DayByDayTab = () => {
|
||||
|
@ -3,12 +3,24 @@ import PerformanceAsNumbersTable from "../../../table/PerformanceAsNumbersTable"
|
||||
import CardHeader from "../../CardHeader";
|
||||
import {faBookOpen} from "@fortawesome/free-solid-svg-icons";
|
||||
import {Card} from "react-bootstrap";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
const PerformanceAsNumbersCard = ({data, servers}) => {
|
||||
const {t} = useTranslation();
|
||||
const noData24h = data && "Unavailable" === data.cpu_24h;
|
||||
const noData7d = data && "Unavailable" === data.cpu_7d;
|
||||
const noData30d = data && "Unavailable" === data.cpu_30d;
|
||||
|
||||
const noDataAlert = noData30d ? <p className={"alert alert-warning mb-0"}>{t('html.description.noData30d')}</p>
|
||||
: (noData7d ? <p className={"alert alert-warning mb-0"}>{t('html.description.noData7d')}</p>
|
||||
: (noData24h ? <p className={"alert alert-warning mb-0"}>{t('html.description.noData24h')}</p>
|
||||
: ''));
|
||||
|
||||
const PerformanceAsNumbersCard = ({data}) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader icon={faBookOpen} color="blue-grey" label={'html.label.performanceAsNumbers'}/>
|
||||
<PerformanceAsNumbersTable data={data}/>
|
||||
{noDataAlert}
|
||||
<PerformanceAsNumbersTable data={data} servers={servers}/>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
@ -15,17 +15,21 @@ import {faCalendarCheck, faClock} from "@fortawesome/free-regular-svg-icons";
|
||||
import React from "react";
|
||||
import {CardLoader} from "../../../navigation/Loader";
|
||||
import ExtendableCardBody from "../../../layout/extension/ExtendableCardBody";
|
||||
import {useMetadata} from "../../../../hooks/metadataHook";
|
||||
|
||||
const ServerAsNumbersCard = ({data}) => {
|
||||
const {t} = useTranslation();
|
||||
const {networkMetadata} = useMetadata();
|
||||
|
||||
if (!data) return <CardLoader/>;
|
||||
if (!data || !networkMetadata) return <CardLoader/>;
|
||||
|
||||
const isGameServer = data.player_kills !== undefined;
|
||||
const showPeaks = isGameServer || networkMetadata.usingRedisBungee || networkMetadata.servers.filter(server => server.proxy).length === 1;
|
||||
return (
|
||||
<Card>
|
||||
<Card.Header>
|
||||
<h6 className="col-black">
|
||||
<Fa icon={faBookOpen}/> {data.player_kills !== undefined ? t('html.label.serverAsNumberse') : t('html.label.networkAsNumbers')}
|
||||
<Fa icon={faBookOpen}/> {isGameServer ? t('html.label.serverAsNumberse') : t('html.label.networkAsNumbers')}
|
||||
</h6>
|
||||
</Card.Header>
|
||||
<ExtendableCardBody
|
||||
@ -43,13 +47,15 @@ const ServerAsNumbersCard = ({data}) => {
|
||||
<Datapoint name={t('html.label.playersOnline')}
|
||||
color={'blue'} icon={faUser}
|
||||
value={data.online_players} bold/>
|
||||
<hr/>
|
||||
<Datapoint name={t('html.label.lastPeak') + ' (' + data.last_peak_date + ')'}
|
||||
color={'blue'} icon={faChartLine}
|
||||
value={data.last_peak_players} valueLabel={t('html.unit.players')} bold/>
|
||||
<Datapoint name={t('html.label.bestPeak') + ' (' + data.best_peak_date + ')'}
|
||||
color={'light-green'} icon={faChartLine}
|
||||
value={data.best_peak_players} valueLabel={t('html.unit.players')} bold/>
|
||||
{showPeaks && <>
|
||||
<hr/>
|
||||
<Datapoint name={t('html.label.lastPeak') + ' (' + data.last_peak_date + ')'}
|
||||
color={'blue'} icon={faChartLine}
|
||||
value={data.last_peak_players} valueLabel={t('html.unit.players')} bold/>
|
||||
<Datapoint name={t('html.label.bestPeak') + ' (' + data.best_peak_date + ')'}
|
||||
color={'light-green'} icon={faChartLine}
|
||||
value={data.best_peak_players} valueLabel={t('html.unit.players')} bold/>
|
||||
</>}
|
||||
<hr/>
|
||||
<Datapoint name={t('html.label.totalPlaytime')}
|
||||
color={'green'} icon={faClock}
|
||||
|
@ -0,0 +1,73 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {linegraphButtons, tooltip} from "../../util/graphs";
|
||||
import LineGraph from './LineGraph'
|
||||
import {ChartLoader} from "../navigation/Loader";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
|
||||
const StackedPlayersOnlineGraph = ({data}) => {
|
||||
const {t} = useTranslation();
|
||||
const {nightModeEnabled} = useTheme();
|
||||
const [graphOptions, setGraphOptions] = useState({title: {text: ''},});
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
const playersOnlineSeries = data.graphs.map(graph => {
|
||||
return {
|
||||
name: t('html.label.playersOnline') + ' (' + graph.server.serverName + ')',
|
||||
type: 'areaspline',
|
||||
tooltip: tooltip.zeroDecimals,
|
||||
data: graph.points.map(point => {
|
||||
// Ensure that the points can be stacked by moving data to minute level
|
||||
point[0] -= (point[0] % 60000);
|
||||
return point;
|
||||
}),
|
||||
color: data.color,
|
||||
yAxis: 0
|
||||
}
|
||||
});
|
||||
setGraphOptions({
|
||||
title: {text: ''},
|
||||
rangeSelector: {
|
||||
selected: 2,
|
||||
buttons: linegraphButtons
|
||||
},
|
||||
chart: {
|
||||
zooming: {
|
||||
type: 'xy'
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
areaspline: {
|
||||
fillOpacity: nightModeEnabled ? 0.2 : 0.4,
|
||||
stacking: 'normal'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
enabled: true,
|
||||
},
|
||||
xAxis: {
|
||||
zoomEnabled: true,
|
||||
title: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
zoomEnabled: true,
|
||||
title: {text: t('html.label.players')},
|
||||
softMax: 2,
|
||||
min: 0
|
||||
},
|
||||
series: playersOnlineSeries
|
||||
})
|
||||
}, [data, nightModeEnabled, t])
|
||||
|
||||
if (!data) return <ChartLoader/>;
|
||||
|
||||
return (
|
||||
<LineGraph id="stacked-players-online-graph"
|
||||
options={graphOptions}/>
|
||||
)
|
||||
}
|
||||
|
||||
export default StackedPlayersOnlineGraph
|
@ -12,14 +12,20 @@ import {
|
||||
import React from "react";
|
||||
import {TableRow} from "./TableRow";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faEye} from "@fortawesome/free-regular-svg-icons";
|
||||
import {faEye, faQuestionCircle} from "@fortawesome/free-regular-svg-icons";
|
||||
import AsNumbersTable from "./AsNumbersTable";
|
||||
import {ChartLoader} from "../navigation/Loader";
|
||||
|
||||
const PerformanceAsNumbersTable = ({data}) => {
|
||||
const PerformanceAsNumbersTable = ({data, servers}) => {
|
||||
const {t} = useTranslation();
|
||||
if (!data) return <ChartLoader/>;
|
||||
|
||||
const dataIncludesGameServers = servers && Boolean(servers.filter(server => !server.proxy).length);
|
||||
const noTPSOnProxies = !servers || dataIncludesGameServers
|
||||
? ''
|
||||
: <Fa icon={faQuestionCircle}
|
||||
title={t('html.description.performanceNoGameServers')}/>;
|
||||
|
||||
return (
|
||||
<AsNumbersTable
|
||||
headers={[t('html.label.last30days'), t('html.label.last7days'), t('html.label.last24hours')]}
|
||||
@ -52,9 +58,9 @@ const PerformanceAsNumbersTable = ({data}) => {
|
||||
]}/>
|
||||
<TableRow icon={faTachometerAlt} color="orange" text={t('html.label.averageTps')}
|
||||
values={[
|
||||
data.tps_30d,
|
||||
data.tps_7d,
|
||||
data.tps_24h
|
||||
<>{data.tps_30d} {noTPSOnProxies}</>,
|
||||
<>{data.tps_7d} {noTPSOnProxies}</>,
|
||||
<>{data.tps_24h} {noTPSOnProxies}</>
|
||||
]}/>
|
||||
<TableRow icon={faTachometerAlt} color="amber" text={t('html.label.averageCpuUsage')}
|
||||
values={[
|
||||
@ -70,17 +76,17 @@ const PerformanceAsNumbersTable = ({data}) => {
|
||||
]}/>
|
||||
<TableRow icon={faDragon} color="purple" text={t('html.label.averageEntities')}
|
||||
values={[
|
||||
data.entities_30d,
|
||||
data.entities_7d,
|
||||
data.entities_24h
|
||||
<>{data.entities_30d} {noTPSOnProxies}</>,
|
||||
<>{data.entities_7d} {noTPSOnProxies}</>,
|
||||
<>{data.entities_24h} {noTPSOnProxies}</>
|
||||
]}/>
|
||||
<TableRow icon={faMap} color="blue-grey"
|
||||
text={<>{t('html.label.averageChunks')}{' '}{data.chunks_30d === 'Unavailable' ?
|
||||
<Fa icon={faEye} title={t('html.description.noSpongeChunks')}/> : ''}</>}
|
||||
values={[
|
||||
data.chunks_30d,
|
||||
data.chunks_7d,
|
||||
data.chunks_24h
|
||||
<>{data.chunks_30d} {noTPSOnProxies}</>,
|
||||
<>{data.chunks_7d} {noTPSOnProxies}</>,
|
||||
<>{data.chunks_24h} {noTPSOnProxies}</>
|
||||
]}/>
|
||||
<TableRow icon={faHdd} color="green"
|
||||
text={t('html.label.maxFreeDisk')}
|
||||
|
@ -114,8 +114,8 @@ const fetchPlayersOnlineGraphServer = async (timestamp, identifier) => {
|
||||
}
|
||||
|
||||
const fetchPlayersOnlineGraphNetwork = async (timestamp) => {
|
||||
let url = `/v1/graph?type=playersOnline`;
|
||||
if (staticSite) url = `/data/graph-playersOnline.json`;
|
||||
let url = `/v1/graph?type=playersOnlineProxies`;
|
||||
if (staticSite) url = `/data/graph-playersOnlineProxies.json`;
|
||||
return doGetRequest(url, timestamp);
|
||||
}
|
||||
|
||||
|
@ -123,9 +123,31 @@ export const colorClassToBgClass = colorClass => {
|
||||
return "bg-" + colorClassToColorName(colorClass);
|
||||
}
|
||||
|
||||
export const hsxStringToArray = (hsvString) => {
|
||||
const color = hsvString.substring(4, hsvString.length - 1);
|
||||
const split = color.split(',');
|
||||
const h = Number(split[0]);
|
||||
const s = Number(split[1].substring(0, split[1].length - 1));
|
||||
const x = Number(split[2].substring(0, split[2].length - 1));
|
||||
return [h, s, x];
|
||||
}
|
||||
|
||||
export const hslToHsv = ([h, s, l]) => {
|
||||
const hsv1 = s * (l < 50 ? l : 100 - l) / 100;
|
||||
const hsvS = hsv1 === 0 ? 0 : 2 * hsv1 / (l + hsv1) * 100;
|
||||
const hsvV = l + hsv1;
|
||||
return [h, hsvS, hsvV];
|
||||
}
|
||||
|
||||
export const hsvToRgb = ([h, s, v]) => {
|
||||
let r, g, b;
|
||||
|
||||
if (s > 1) {
|
||||
h = h / 360;
|
||||
s = s / 100;
|
||||
v = v / 100;
|
||||
}
|
||||
|
||||
const i = Math.floor(h * 6);
|
||||
const f = h * 6 - i;
|
||||
const p = v * (1 - s);
|
||||
@ -186,15 +208,15 @@ const rgbToHex = (component) => {
|
||||
return Math.floor(component).toString(16).padStart(2, '0');
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/a/3732187
|
||||
export const withReducedSaturation = hex => {
|
||||
const saturationReduction = 0.70;
|
||||
// To RGB
|
||||
let r = parseInt(hex.substr(1, 2), 16); // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10).
|
||||
let g = parseInt(hex.substr(3, 2), 16);
|
||||
let b = parseInt(hex.substr(5, 2), 16);
|
||||
export const hexToRgb = (hexString) => {
|
||||
const r = parseInt(hexString.substring(1, 3), 16);
|
||||
const g = parseInt(hexString.substring(3, 5), 16);
|
||||
const b = parseInt(hexString.substring(5, 7), 16);
|
||||
return [r, g, b];
|
||||
}
|
||||
|
||||
// To HSL
|
||||
// https://css-tricks.com/converting-color-spaces-in-javascript/
|
||||
export const rgbToHsl = ([r, g, b]) => {
|
||||
r /= 255;
|
||||
g /= 255;
|
||||
b /= 255;
|
||||
@ -222,6 +244,15 @@ export const withReducedSaturation = hex => {
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return [h, s, l];
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/a/3732187
|
||||
export const withReducedSaturation = (hex, reduceSaturationPercentage) => {
|
||||
const saturationReduction = reduceSaturationPercentage ? reduceSaturationPercentage : 0.70;
|
||||
|
||||
const rgb = hexToRgb(hex);
|
||||
const [h, s, l] = rgbToHsl(rgb);
|
||||
|
||||
// To css property
|
||||
return 'hsl(' + h * 360 + ',' + s * 100 * saturationReduction + '%,' + l * 95 + '%)';
|
||||
|
@ -23,7 +23,7 @@ const NetworkPerformance = () => {
|
||||
const [selectedOptions, setSelectedOptions] = useState([]);
|
||||
const [visualizedServers, setVisualizedServers] = useState([]);
|
||||
|
||||
const initializeServerOptions = () => {
|
||||
useEffect(() => {
|
||||
if (networkMetadata) {
|
||||
const options = networkMetadata.servers;
|
||||
setServerOptions(options);
|
||||
@ -34,8 +34,7 @@ const NetworkPerformance = () => {
|
||||
setSelectedOptions([indexOfProxy]);
|
||||
setVisualizedServers([indexOfProxy]);
|
||||
}
|
||||
};
|
||||
useEffect(initializeServerOptions, [networkMetadata, setVisualizedServers]);
|
||||
}, [networkMetadata, setVisualizedServers]);
|
||||
|
||||
const applySelected = () => {
|
||||
setVisualizedServers(selectedOptions);
|
||||
@ -85,7 +84,8 @@ const NetworkPerformance = () => {
|
||||
loadPerformanceData();
|
||||
}, [loadPerformanceData, visualizedServers, updateRequested]);
|
||||
|
||||
const isUpToDate = visualizedServers.every((s, i) => s === selectedOptions[i]);
|
||||
const isUpToDate = selectedOptions.length === visualizedServers.length && selectedOptions.every(
|
||||
(s, i) => s === visualizedServers[i]);
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className={"network-performance"}>
|
||||
@ -96,7 +96,8 @@ const NetworkPerformance = () => {
|
||||
</ExtendableRow>
|
||||
<ExtendableRow id={'row-network-performance-1'}>
|
||||
<Col md={8}>
|
||||
<PerformanceAsNumbersCard data={performanceData?.overview?.numbers}/>
|
||||
<PerformanceAsNumbersCard data={performanceData?.overview?.numbers}
|
||||
servers={performanceData.servers || []}/>
|
||||
</Col>
|
||||
<Col md={4}>
|
||||
<Card>
|
||||
@ -104,7 +105,8 @@ const NetworkPerformance = () => {
|
||||
<MultiSelect options={serverOptions.map(server => server.serverName)}
|
||||
selectedIndexes={selectedOptions}
|
||||
setSelectedIndexes={setSelectedOptions}/>
|
||||
<button className={'btn bg-transparent'} onClick={applySelected} disabled={isUpToDate}>
|
||||
<button className={'btn ' + (isUpToDate ? 'bg-transparent' : 'bg-theme')}
|
||||
onClick={applySelected} disabled={isUpToDate}>
|
||||
{t('html.label.apply')}
|
||||
</button>
|
||||
</Card>
|
||||
|
@ -57,4 +57,9 @@ public class VelocitySensor implements ServerSensor<Object> {
|
||||
public List<String> getOnlinePlayerNames() {
|
||||
return getPlayers.get().stream().map(Player::getUsername).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usingRedisBungee() {
|
||||
return VelocityRedisCheck.isClassAvailable();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import com.djrapitops.plan.processing.Processing;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
import com.djrapitops.plan.settings.locale.lang.PluginLang;
|
||||
import net.playeranalytics.plugin.server.PluginLogger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@ -73,17 +74,16 @@ public class VelocityServerInfo extends ServerInfo {
|
||||
@Override
|
||||
public void loadServerInfo() {
|
||||
logger.info(locale.getString(PluginLang.LOADING_SERVER_INFO));
|
||||
checkIfDefaultIP();
|
||||
|
||||
this.server = fromFile.load(null).orElseGet(() -> fromDatabase.load(null)
|
||||
.orElseGet(this::registerServer));
|
||||
this.server = fromFile.load(null)
|
||||
.orElseGet(this::registerServer);
|
||||
this.server.setProxy(true); // Ensure isProxy if loaded from file
|
||||
|
||||
processing.submitNonCritical(this::updateStorage);
|
||||
}
|
||||
|
||||
private void updateStorage() {
|
||||
String address = addresses.getAccessAddress().orElseGet(addresses::getFallbackLocalhostAddress);
|
||||
String address = getAddress();
|
||||
|
||||
server.setWebAddress(address);
|
||||
|
||||
@ -92,36 +92,27 @@ public class VelocityServerInfo extends ServerInfo {
|
||||
fromFile.save(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EnableException If IP setting is unset
|
||||
*/
|
||||
private void checkIfDefaultIP() {
|
||||
String ip = serverProperties.getIp();
|
||||
if ("0.0.0.0".equals(ip)) {
|
||||
logger.error("IP setting still 0.0.0.0 - Configure Alternative_IP/IP that connects to the Proxy server.");
|
||||
logger.info("Player Analytics partially enabled (Use /planproxy reload to reload config)");
|
||||
throw new EnableException("IP setting still 0.0.0.0 - Configure Alternative_IP/IP that connects to the Proxy server.");
|
||||
}
|
||||
}
|
||||
|
||||
private Server registerServer() {
|
||||
Server proxy = createServerObject();
|
||||
|
||||
fromDatabase.save(proxy);
|
||||
Server stored = fromDatabase.load(null)
|
||||
.orElseThrow(() -> new EnableException("Velocity registration failed (DB)"));
|
||||
Server stored = fromDatabase.load(proxy.getUuid())
|
||||
.orElseThrow(() -> new EnableException("Server registration to database failed"));
|
||||
|
||||
fromFile.save(stored);
|
||||
return stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EnableException If IP setting is unset
|
||||
*/
|
||||
private Server createServerObject() {
|
||||
ServerUUID serverUUID = generateNewUUID();
|
||||
String accessAddress = addresses.getAccessAddress().orElseThrow(() -> new EnableException("Velocity can not have '0.0.0.0' or '' as an address. Set up 'Server.IP' setting."));
|
||||
String accessAddress = getAddress();
|
||||
|
||||
return new Server(-1, serverUUID, "Velocity", accessAddress, true, currentVersion);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getAddress() {
|
||||
return addresses.getAccessAddress()
|
||||
.orElse(addresses.isWebserverEnabled() ? addresses.getFallbackLocalhostAddress() : null);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user