Plan/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java

349 lines
17 KiB
Java

/*
* 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.rendering.json;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.delivery.domain.RetentionData;
import com.djrapitops.plan.delivery.domain.datatransfer.PlayerJoinAddresses;
import com.djrapitops.plan.delivery.domain.datatransfer.ServerDto;
import com.djrapitops.plan.delivery.domain.mutators.PlayerKillMutator;
import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator;
import com.djrapitops.plan.delivery.domain.mutators.TPSMutator;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerTableDataQuery;
import com.djrapitops.plan.gathering.ServerUptimeCalculator;
import com.djrapitops.plan.gathering.cache.SessionCache;
import com.djrapitops.plan.gathering.domain.*;
import com.djrapitops.plan.identification.Server;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.GenericLang;
import com.djrapitops.plan.settings.locale.lang.HtmlLang;
import com.djrapitops.plan.settings.theme.Theme;
import com.djrapitops.plan.settings.theme.ThemeVal;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries;
import com.djrapitops.plan.storage.database.queries.analysis.PlayerRetentionQueries;
import com.djrapitops.plan.storage.database.queries.objects.*;
import com.djrapitops.plan.storage.database.queries.objects.playertable.NetworkTablePlayersQuery;
import com.djrapitops.plan.storage.database.queries.objects.playertable.ServerTablePlayersQuery;
import com.djrapitops.plan.utilities.comparators.SessionStartComparator;
import com.djrapitops.plan.utilities.dev.Untrusted;
import com.djrapitops.plan.utilities.java.Maps;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Factory with different JSON creation methods placed to a single class.
*
* @author AuroraLS3
*/
@Singleton
public class JSONFactory {
private final PlanConfig config;
private final Locale locale;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final ServerUptimeCalculator serverUptimeCalculator;
private final Theme theme;
private final Graphs graphs;
private final Formatters formatters;
@Inject
public JSONFactory(
PlanConfig config,
Locale locale,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerUptimeCalculator serverUptimeCalculator,
Theme theme,
Graphs graphs,
Formatters formatters
) {
this.config = config;
this.locale = locale;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.serverUptimeCalculator = serverUptimeCalculator;
this.theme = theme;
this.graphs = graphs;
this.formatters = formatters;
}
public PlayersTableJSONCreator serverPlayersTableJSON(ServerUUID serverUUID) {
Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE);
Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
boolean openPlayerLinksInNewTab = config.isTrue(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB);
Database database = dbSystem.getDatabase();
return new PlayersTableJSONCreator(
database.query(new ServerTablePlayersQuery(serverUUID, System.currentTimeMillis(), playtimeThreshold, xMostRecentPlayers)),
database.query(new ExtensionServerTableDataQuery(serverUUID, xMostRecentPlayers)),
openPlayerLinksInNewTab,
formatters, locale
);
}
public PlayersTableJSONCreator networkPlayersTableJSON() {
Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_PLAYERS_PAGE);
Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
boolean openPlayerLinksInNewTab = config.isTrue(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB);
Database database = dbSystem.getDatabase();
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)),
allPluginData,
openPlayerLinksInNewTab,
formatters, locale,
true // players page
);
}
public List<RetentionData> playerRetentionAsJSONMap(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
return db.query(PlayerRetentionQueries.fetchRetentionData(serverUUID));
}
public List<RetentionData> networkPlayerRetentionAsJSONMap() {
Database db = dbSystem.getDatabase();
return db.query(PlayerRetentionQueries.fetchRetentionData());
}
public PlayerJoinAddresses playerJoinAddresses(ServerUUID serverUUID, boolean includeByPlayerMap) {
Database db = dbSystem.getDatabase();
if (includeByPlayerMap) {
Map<UUID, String> addresses = db.query(JoinAddressQueries.latestJoinAddressesOfPlayers(serverUUID));
return new PlayerJoinAddresses(
addresses.values().stream().distinct().sorted().collect(Collectors.toList()),
addresses
);
} else {
return new PlayerJoinAddresses(db.query(JoinAddressQueries.uniqueJoinAddresses(serverUUID)), null);
}
}
public PlayerJoinAddresses playerJoinAddresses(boolean includeByPlayerMap) {
Database db = dbSystem.getDatabase();
return new PlayerJoinAddresses(
db.query(JoinAddressQueries.uniqueJoinAddresses()),
includeByPlayerMap ? db.query(JoinAddressQueries.latestJoinAddressesOfPlayers()) : null
);
}
public List<Map<String, Object>> serverSessionsAsJSONMap(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
Integer perPageLimit = config.get(DisplaySettings.SESSIONS_PER_PAGE);
List<FinishedSession> sessions = db.query(SessionQueries.fetchLatestSessionsOfServer(serverUUID, perPageLimit));
// Add online sessions
if (serverUUID.equals(serverInfo.getServerUUID())) {
addActiveSessions(sessions);
sessions.sort(new SessionStartComparator());
while (true) {
int size = sessions.size();
if (size <= perPageLimit) break;
sessions.remove(size - 1); // Remove last until it fits.
}
}
return new SessionsMutator(sessions).toPlayerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters);
}
public List<Map<String, Object>> networkSessionsAsJSONMap() {
Database db = dbSystem.getDatabase();
Integer perPageLimit = config.get(DisplaySettings.SESSIONS_PER_PAGE);
List<FinishedSession> sessions = db.query(SessionQueries.fetchLatestSessions(perPageLimit));
// Add online sessions
if (serverInfo.getServer().isProxy()) {
addActiveSessions(sessions);
sessions.sort(new SessionStartComparator());
while (true) {
int size = sessions.size();
if (size <= perPageLimit) break;
sessions.remove(size - 1); // Remove last until it fits.
}
}
List<Map<String, Object>> sessionMaps = new SessionsMutator(sessions).toPlayerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters);
// Add network_server property so that sessions have a server page link
sessionMaps.forEach(map -> map.put("network_server", map.get("server_name")));
return sessionMaps;
}
public void addActiveSessions(List<FinishedSession> sessions) {
for (ActiveSession activeSession : SessionCache.getActiveSessions()) {
sessions.add(activeSession.toFinishedSessionFromStillActive());
}
}
public List<Map<String, Object>> serverPlayerKillsAsJSONMaps(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
List<PlayerKill> kills = db.query(KillQueries.fetchPlayerKillsOnServer(serverUUID, 100));
return new PlayerKillMutator(kills).toJSONAsMap(formatters);
}
public Optional<ServerDto> serverForIdentifier(@Untrusted String identifier) {
return dbSystem.getDatabase()
.query(ServerQueries.fetchServerMatchingIdentifier(identifier))
.map(ServerDto::fromServer);
}
public Map<String, Object> serversAsJSONMaps() {
Database db = dbSystem.getDatabase();
long now = System.currentTimeMillis();
long weekAgo = now - TimeUnit.DAYS.toMillis(7L);
Formatter<Long> year = formatters.yearLong();
Formatter<Double> decimals = formatters.decimals();
Formatter<Long> timeAmount = formatters.timeAmount();
Map<ServerUUID, Server> serverInformation = db.query(ServerQueries.fetchPlanServerInformation());
ServerUUID proxyUUID = serverInformation.values().stream()
.filter(Server::isProxy)
.findFirst()
.map(Server::getUuid).orElse(null);
Map<ServerUUID, Integer> serverUuidToId = new HashMap<>();
for (Server server : serverInformation.values()) {
server.getId().ifPresent(serverId -> serverUuidToId.put(server.getUuid(), serverId));
}
Map<Integer, List<TPS>> tpsDataByServerId = db.query(
TPSQueries.fetchTPSDataOfAllServersBut(weekAgo, now, proxyUUID)
);
Map<ServerUUID, Integer> totalPlayerCounts = db.query(PlayerCountQueries.newPlayerCounts(0, now));
Map<ServerUUID, Integer> newPlayerCounts = db.query(PlayerCountQueries.newPlayerCounts(weekAgo, now));
Map<ServerUUID, Integer> uniquePlayerCounts = db.query(PlayerCountQueries.uniquePlayerCounts(weekAgo, now));
List<Map<String, Object>> servers = new ArrayList<>();
serverInformation.entrySet()
.stream() // Sort alphabetically
.sorted(Comparator.comparing(entry -> entry.getValue().getIdentifiableName().toLowerCase()))
.filter(entry -> entry.getValue().isNotProxy())
.forEach(entry -> {
ServerUUID serverUUID = entry.getKey();
Map<String, Object> server = new HashMap<>();
server.put("name", entry.getValue().getIdentifiableName());
server.put("serverUUID", entry.getValue().getUuid().toString());
server.put("playersOnlineColor", theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE));
Optional<DateObj<Integer>> recentPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, now - TimeUnit.DAYS.toMillis(2L)));
Optional<DateObj<Integer>> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID));
server.put("last_peak_date", recentPeak.map(DateObj::getDate).map(year).orElse("-"));
server.put("best_peak_date", allTimePeak.map(DateObj::getDate).map(year).orElse("-"));
server.put("last_peak_players", recentPeak.map(DateObj::getValue).orElse(0));
server.put("best_peak_players", allTimePeak.map(DateObj::getValue).orElse(0));
TPSMutator tpsMonth = new TPSMutator(tpsDataByServerId.getOrDefault(serverUuidToId.get(serverUUID), Collections.emptyList()));
server.put("playersOnline", tpsMonth.all().stream()
.map(tps -> new double[]{tps.getDate(), tps.getPlayers()})
.toArray(double[][]::new));
server.put("players", totalPlayerCounts.getOrDefault(serverUUID, 0));
server.put("new_players", newPlayerCounts.getOrDefault(serverUUID, 0));
server.put("unique_players", uniquePlayerCounts.getOrDefault(serverUUID, 0));
TPSMutator tpsWeek = tpsMonth.filterDataBetween(weekAgo, now);
double averageTPS = tpsWeek.averageTPS();
server.put("avg_tps", averageTPS != -1 ? decimals.apply(averageTPS) : HtmlLang.UNIT_NO_DATA.getKey());
server.put("low_tps_spikes", tpsWeek.lowTpsSpikeCount(config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED)));
server.put("downtime", timeAmount.apply(tpsWeek.serverDownTime()));
server.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID).map(timeAmount)
.orElse(GenericLang.UNAVAILABLE.getKey()));
Optional<TPS> online = tpsWeek.getLast();
server.put("online", online.map(point -> point.getDate() >= now - TimeUnit.MINUTES.toMillis(3L) ? point.getPlayers() : HtmlLang.LABEL_POSSIBLY_OFFLINE.getKey())
.orElse(HtmlLang.UNIT_NO_DATA.getKey()));
servers.add(server);
});
return Collections.singletonMap("servers", servers);
}
public Map<String, Object> pingPerGeolocation(ServerUUID serverUUID) {
Map<String, Ping> pingByGeolocation = dbSystem.getDatabase().query(PingQueries.fetchPingDataOfServerByGeolocation(serverUUID));
return Maps.builder(String.class, Object.class)
.put("table", turnToTableEntries(pingByGeolocation))
.build();
}
public Map<String, Object> pingPerGeolocation() {
Map<String, Ping> pingByGeolocation = dbSystem.getDatabase().query(PingQueries.fetchPingDataOfNetworkByGeolocation());
return Maps.builder(String.class, Object.class)
.put("table", turnToTableEntries(pingByGeolocation))
.build();
}
private List<Map<String, Object>> turnToTableEntries(Map<String, Ping> pingByGeolocation) {
List<Map<String, Object>> tableEntries = new ArrayList<>();
for (Map.Entry<String, Ping> entry : pingByGeolocation.entrySet()) {
String geolocation = entry.getKey();
Ping ping = entry.getValue();
tableEntries.add(Maps.builder(String.class, Object.class)
.put("country", geolocation)
.put("avg_ping", ping.getAverage())
.put("min_ping", ping.getMin())
.put("max_ping", ping.getMax())
.build());
}
return tableEntries;
}
public Map<String, List<ServerDto>> listServers() {
Collection<Server> servers = dbSystem.getDatabase().query(ServerQueries.fetchPlanServerInformationCollection());
return Collections.singletonMap("servers", servers.stream()
.map(ServerDto::fromServer)
.collect(Collectors.toList()));
}
}