Merge pull request #634 from Rsl1122/4.3.3

PR for 4.4.0
This commit is contained in:
Rsl1122 2018-07-18 13:24:10 +03:00 committed by GitHub
commit d95b3b567b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
340 changed files with 10715 additions and 10918 deletions

View File

@ -5,7 +5,7 @@ before_install:
- pwd
- cd PlanPluginBridge
- ls
- mvn install:install-file -Dfile=./PlanPluginBridge-4.3.0.jar -DpomFile=./pom.xml
- mvn install:install-file -Dfile=./PlanPluginBridge-4.4.0.jar -DpomFile=./pom.xml
- cd ../Plan
- ls
install:

View File

@ -23,7 +23,7 @@ import org.spongepowered.api.plugin.Plugin;
import java.io.File;
import java.io.InputStream;
@Plugin(id = "plan", name = "Plan", version = "4.3.2", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"})
@Plugin(id = "plan", name = "Plan", version = "4.4.0", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"})
public class PlanSponge extends SpongePlugin implements PlanPlugin {
@Inject
@ -114,7 +114,7 @@ public class PlanSponge extends SpongePlugin implements PlanPlugin {
@Override
public String getVersion() {
return "4.3.2";
return "4.4.0";
}
public SpongeSystem getSystem() {

View File

@ -6,16 +6,15 @@ package com.djrapitops.plan;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.Actions;
import com.djrapitops.plan.data.container.Action;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.system.cache.CacheSystem;
import com.djrapitops.plan.system.cache.DataCache;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plugin.api.utility.log.Log;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
/**
@ -54,7 +53,6 @@ public class ShutdownHook extends Thread {
Map<UUID, Session> activeSessions = SessionCache.getActiveSessions();
long now = System.currentTimeMillis();
db = Database.getActive();
saveFirstSessionInformation(db, now);
saveActiveSessions(db, activeSessions, now);
} catch (IllegalStateException ignored) {
/* Database is not initialized */
@ -71,37 +69,20 @@ public class ShutdownHook extends Thread {
}
}
private void saveFirstSessionInformation(Database db, long now) throws DBInitException {
DataCache dataCache = CacheSystem.getInstance().getDataCache();
for (Map.Entry<UUID, Integer> entry : dataCache.getFirstSessionMsgCounts().entrySet()) {
if (!db.isOpen()) {
db.init();
}
try {
UUID uuid = entry.getKey();
int messagesSent = entry.getValue();
db.save().action(uuid, new Action(now, Actions.FIRST_LOGOUT, "Messages sent: " + messagesSent));
} catch (DBException e) {
Log.toLog(this.getClass(), e);
}
}
}
private void saveActiveSessions(Database db, Map<UUID, Session> activeSessions, long now) throws DBInitException {
for (Map.Entry<UUID, Session> entry : activeSessions.entrySet()) {
UUID uuid = entry.getKey();
Session session = entry.getValue();
long sessionEnd = session.getSessionEnd();
if (sessionEnd == -1) {
Optional<Long> end = session.getValue(SessionKeys.END);
if (!end.isPresent()) {
session.endSession(now);
}
if (!db.isOpen()) {
db.init();
}
try {
Log.debug("Shutdown: Saving a session: " + session.getSessionStart());
db.save().session(uuid, session);
} catch (DBException e) {
} catch (DBOpException e) {
Log.toLog(this.getClass(), e);
}
}

View File

@ -4,7 +4,7 @@
*/
package com.djrapitops.plan.api;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.utilities.uuid.UUIDUtility;
import com.djrapitops.plugin.api.utility.log.Log;
@ -38,7 +38,7 @@ public abstract class CommonAPI implements PlanAPI {
public Map<UUID, String> getKnownPlayerNames() {
try {
return fetchFromPlanDB().getPlayerNames();
} catch (DBException e) {
} catch (DBOpException e) {
Log.toLog(this.getClass(), e);
return new HashMap<>();
}

View File

@ -4,8 +4,6 @@
*/
package com.djrapitops.plan.api.exceptions.connection;
import com.djrapitops.plan.api.exceptions.database.DBException;
/**
* Thrown when DBException occurs during InfoRequest#placeIntoDatabase.
*
@ -13,7 +11,7 @@ import com.djrapitops.plan.api.exceptions.database.DBException;
*/
public class TransferDatabaseException extends WebException {
public TransferDatabaseException(DBException cause) {
public TransferDatabaseException(Exception cause) {
super(cause);
}
}

View File

@ -0,0 +1,33 @@
package com.djrapitops.plan.api.exceptions.database;
import java.sql.SQLException;
/**
* Runtime exception for wrapping database errors.
*
* @author Rsl1122
*/
public class DBOpException extends RuntimeException {
private boolean fatal = false;
public DBOpException(String message) {
super(message);
}
public DBOpException(String message, Throwable cause) {
super(message, cause);
}
public static DBOpException forCause(String sql, SQLException e) {
return new DBOpException("SQL Failed: " + sql + "; " + e.getMessage(), e);
}
public boolean isFatal() {
return fatal;
}
public void setFatal(boolean fatal) {
this.fatal = fatal;
}
}

View File

@ -48,6 +48,7 @@ public class PlanBungeeCommand extends TreeCmdNode {
new ManageConDebugCommand(),
new BungeeSetupToggleCommand(),
new ReloadCommand(plugin),
new DisableCommand(),
new StatusCommand<>(plugin, Permissions.MANAGE.getPermission(), plugin.getColorScheme()),
// (Settings.ALLOW_UPDATE.isTrue() ? new UpdateCommand() : null)
}

View File

@ -1,7 +1,7 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.InfoSystem;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
@ -12,7 +12,6 @@ import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.settings.locale.Locale;
import com.djrapitops.plan.system.settings.locale.Msg;
import com.djrapitops.plan.system.webserver.WebServerSystem;
import com.djrapitops.plan.utilities.analysis.Analysis;
import com.djrapitops.plugin.api.utility.log.Log;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
@ -46,12 +45,12 @@ public class AnalyzeCommand extends CommandNode {
try {
Server server = getServer(args).orElseGet(ServerInfo::getServer);
UUID serverUUID = server.getUuid();
if (!ServerInfo.getServerUUID().equals(serverUUID) || !Analysis.isAnalysisBeingRun()) {
InfoSystem.getInstance().generateAnalysisPage(serverUUID);
}
InfoSystem.getInstance().generateAnalysisPage(serverUUID);
sendWebUserNotificationIfNecessary(sender);
sendLink(server, sender);
} catch (DBException | WebException e) {
} catch (DBOpException | WebException e) {
sender.sendMessage("§cError occurred: " + e.toString());
Log.toLog(this.getClass(), e);
}
@ -74,7 +73,7 @@ public class AnalyzeCommand extends CommandNode {
sender.sendMessage(Locale.get(Msg.CMD_CONSTANT_FOOTER).toString());
}
private void sendWebUserNotificationIfNecessary(ISender sender) throws DBException {
private void sendWebUserNotificationIfNecessary(ISender sender) {
if (WebServerSystem.getInstance().getWebServer().isAuthRequired() && CommandUtils.isPlayer(sender)) {
boolean senderHasWebUser = Database.getActive().check().doesWebUserExists(sender.getName());
@ -84,7 +83,7 @@ public class AnalyzeCommand extends CommandNode {
}
}
private Optional<Server> getServer(String[] args) throws DBException {
private Optional<Server> getServer(String[] args) {
if (args.length >= 1 && ConnectionSystem.getInstance().isServerAvailable()) {
Map<UUID, Server> bukkitServers = Database.getActive().fetch().getBukkitServers();
String serverIdentifier = getGivenIdentifier(args);

View File

@ -0,0 +1,22 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
import com.djrapitops.plugin.command.ISender;
public class DisableCommand extends CommandNode {
public DisableCommand() {
super("disable", "plan.reload", CommandType.ALL);
}
@Override
public void onCommand(ISender sender, String commandLabel, String[] args) {
PlanPlugin.getInstance().onDisable();
sender.sendMessage(
"§aPlan systems are now disabled. " +
"You can still use /planbungee reload to restart the plugin."
);
}
}

View File

@ -1,7 +1,6 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.FatalDBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.info.InspectCacheRequestProcessor;
@ -60,24 +59,29 @@ public class InspectCommand extends CommandNode {
return;
}
if (CommandUtils.isPlayer(sender) && WebServer.getInstance().isAuthRequired()) {
boolean senderHasWebUser = activeDB.check().doesWebUserExists(sender.getName());
if (!senderHasWebUser) {
sender.sendMessage("§e[Plan] You might not have a web user, use /plan register <password>");
}
}
checkWebUserAndNotify(activeDB, sender);
Processing.submit(new InspectCacheRequestProcessor(uuid, sender, playerName));
} catch (FatalDBException ex) {
Log.toLog(this.getClass(), ex);
sender.sendMessage("§cFatal database exception occurred: " + ex.getMessage());
} catch (DBException ex) {
Log.toLog(this.getClass(), ex);
sender.sendMessage("§eNon-Fatal database exception occurred: " + ex.getMessage());
} catch (DBOpException e) {
if (e.isFatal()) {
sender.sendMessage("§cFatal database exception occurred: " + e.getMessage());
} else {
sender.sendMessage("§eNon-Fatal database exception occurred: " + e.getMessage());
}
Log.toLog(this.getClass(), e);
} finally {
this.cancel();
}
}
}).runTaskAsynchronously();
}
private void checkWebUserAndNotify(Database activeDB, ISender sender) {
if (CommandUtils.isPlayer(sender) && WebServer.getInstance().isAuthRequired()) {
boolean senderHasWebUser = activeDB.check().doesWebUserExists(sender.getName());
if (!senderHasWebUser) {
sender.sendMessage("§e[Plan] You might not have a web user, use /plan register <password>");
}
}
}
}

View File

@ -1,7 +1,7 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.settings.Permissions;
@ -44,7 +44,7 @@ public class ListServersCommand extends CommandNode {
sender.sendMessage(" " + tCol + server.getId() + sCol + " : " + server.getName() + " : " + server.getWebAddress());
}
sender.sendMessage(Locale.get(Msg.CMD_CONSTANT_FOOTER).toString());
} catch (DBException e) {
} catch (DBOpException e) {
sender.sendMessage("§cDatabase Exception occurred.");
Log.toLog(this.getClass(), e);
}

View File

@ -1,14 +1,20 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.data.PlayerProfile;
import com.djrapitops.plan.data.calculation.ActivityIndex;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
import com.djrapitops.plan.data.store.mutators.GeoInfoMutator;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.settings.locale.Locale;
import com.djrapitops.plan.system.settings.locale.Msg;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plan.utilities.uuid.UUIDUtility;
import com.djrapitops.plugin.api.utility.log.Log;
@ -16,10 +22,12 @@ import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
import com.djrapitops.plugin.command.ISender;
import com.djrapitops.plugin.settings.ColorScheme;
import com.djrapitops.plugin.settings.DefaultMessages;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
@ -64,17 +72,20 @@ public class QInspectCommand extends CommandNode {
return;
}
Database database = Database.getActive();
if (!database.check().isPlayerRegistered(uuid)) {
PlayerContainer container = Database.getActive().fetch().getPlayerContainer(uuid);
if (!container.getValue(PlayerKeys.REGISTERED).isPresent()) {
sender.sendMessage(Locale.get(Msg.CMD_FAIL_USERNAME_NOT_KNOWN).toString());
return;
}
PlayerProfile playerProfile = database.fetch().getPlayerProfile(uuid);
sendMessages(sender, playerProfile);
} catch (DBException ex) {
Log.toLog(this.getClass(), ex);
sendMessages(sender, container);
} catch (DBOpException e) {
if (e.isFatal()) {
sender.sendMessage("§cFatal database exception occurred: " + e.getMessage());
} else {
sender.sendMessage("§eNon-Fatal database exception occurred: " + e.getMessage());
}
Log.toLog(this.getClass(), e);
} finally {
this.cancel();
}
@ -82,7 +93,7 @@ public class QInspectCommand extends CommandNode {
}).runTaskAsynchronously();
}
private void sendMessages(ISender sender, PlayerProfile profile) {
private void sendMessages(ISender sender, PlayerContainer player) {
long now = System.currentTimeMillis();
ColorScheme colorScheme = plugin.getColorScheme();
@ -90,24 +101,30 @@ public class QInspectCommand extends CommandNode {
String colM = colorScheme.getMainColor();
String colS = colorScheme.getSecondaryColor();
String colT = colorScheme.getTertiaryColor();
Formatter<DateHolder> timestamp = Formatters.year();
Formatter<Long> length = Formatters.timeAmount();
String ball = DefaultMessages.BALL.parse();
sender.sendMessage(Locale.get(Msg.CMD_HEADER_INSPECT).toString() + ": " + colT + player.getValue(PlayerKeys.NAME).orElse("Unknown"));
sender.sendMessage(Locale.get(Msg.CMD_HEADER_INSPECT).toString() + ": " + colT + profile.getName());
ActivityIndex activityIndex = player.getActivityIndex(now);
Long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L);
Long lastSeen = player.getValue(PlayerKeys.LAST_SEEN).orElse(0L);
List<GeoInfo> geoInfo = player.getValue(PlayerKeys.GEO_INFO).orElse(new ArrayList<>());
Optional<GeoInfo> mostRecentGeoInfo = new GeoInfoMutator(geoInfo).mostRecent();
String loginLocation = mostRecentGeoInfo.isPresent() ? mostRecentGeoInfo.get().getGeolocation() : "-";
SessionsMutator sessionsMutator = SessionsMutator.forContainer(player);
ActivityIndex activityIndex = profile.getActivityIndex(now);
sender.sendMessage(colT + ball + " " + colM + " Activity Index: " + colS + activityIndex.getFormattedValue() + " | " + activityIndex.getGroup());
sender.sendMessage(colT + ball + " " + colM + " Registered: " + colS + FormatUtils.formatTimeStampYear(profile.getRegistered()));
sender.sendMessage(colT + ball + " " + colM + " Last Seen: " + colS + FormatUtils.formatTimeStampYear(profile.getLastSeen()));
sender.sendMessage(colT + ball + " " + colM + " Logged in from: " + colS + profile.getMostRecentGeoInfo().getGeolocation());
sender.sendMessage(colT + ball + " " + colM + " Playtime: " + colS + FormatUtils.formatTimeAmount(profile.getTotalPlaytime()));
sender.sendMessage(colT + ball + " " + colM + " Longest Session: " + colS + FormatUtils.formatTimeAmount(profile.getLongestSession(0, now)));
sender.sendMessage(colT + ball + " " + colM + " Times Kicked: " + colS + profile.getTimesKicked());
sender.sendMessage(colM + " Activity Index: " + colS + activityIndex.getFormattedValue() + " | " + activityIndex.getGroup());
sender.sendMessage(colM + " Registered: " + colS + timestamp.apply(() -> registered));
sender.sendMessage(colM + " Last Seen: " + colS + timestamp.apply(() -> lastSeen));
sender.sendMessage(colM + " Logged in from: " + colS + loginLocation);
sender.sendMessage(colM + " Playtime: " + colS + length.apply(sessionsMutator.toPlaytime()));
sender.sendMessage(colM + " Longest Session: " + colS + length.apply(sessionsMutator.toLongestSessionLength()));
sender.sendMessage(colM + " Times Kicked: " + colS + player.getValue(PlayerKeys.KICK_COUNT).orElse(0));
sender.sendMessage("");
sender.sendMessage(colT + ball + " " + colM + " Player Kills : " + colS + profile.getPlayerKills().count());
sender.sendMessage(colT + ball + " " + colM + " Mob Kills : " + colS + profile.getMobKillCount());
sender.sendMessage(colT + ball + " " + colM + " Deaths : " + colS + profile.getDeathCount());
sender.sendMessage(colM + " Player Kills : " + colS + sessionsMutator.toPlayerKillCount());
sender.sendMessage(colM + " Mob Kills : " + colS + sessionsMutator.toMobKillCount());
sender.sendMessage(colM + " Deaths : " + colS + sessionsMutator.toDeathCount());
sender.sendMessage(Locale.get(Msg.CMD_CONSTANT_FOOTER).toString());
}

View File

@ -1,9 +1,11 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.settings.locale.Locale;
import com.djrapitops.plan.system.settings.locale.Msg;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.api.utility.log.Log;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
import com.djrapitops.plugin.command.ISender;
@ -57,6 +59,9 @@ public class SearchCommand extends CommandNode {
}
sender.sendMessage(Locale.get(Msg.CMD_CONSTANT_FOOTER).toString());
} catch (DBOpException e) {
sender.sendMessage("§cDatabase error occurred: " + e.getMessage());
Log.toLog(this.getClass(), e);
} finally {
this.cancel();
}

View File

@ -1,7 +1,7 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.connection.*;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.command.commands.manage.ManageConDebugCommand;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
@ -101,7 +101,7 @@ public class UpdateCommand extends CommandNode {
try {
cancel(sender, Database.getActive().fetch().getServers());
sender.sendMessage("§aCancel operation performed.");
} catch (DBException e) {
} catch (DBOpException e) {
sender.sendMessage("§cDatabase error occurred, cancel could not be performed.");
Log.toLog(this.getClass().getName(), e);
}
@ -120,7 +120,7 @@ public class UpdateCommand extends CommandNode {
try {
List<Server> servers = Database.getActive().fetch().getServers();
update(sender, servers, args);
} catch (DBException e) {
} catch (DBOpException e) {
Log.toLog(this.getClass().getName(), e);
}
}
@ -228,7 +228,7 @@ public class UpdateCommand extends CommandNode {
success = false;
}
return success;
} catch (DBException e) {
} catch (DBOpException e) {
sender.sendMessage("§cDatabase error occurred, update has been cancelled.");
Log.toLog(this.getClass().getName(), e);
return false;

View File

@ -2,13 +2,13 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.settings.locale.Locale;
import com.djrapitops.plan.system.settings.locale.Msg;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plugin.api.utility.log.Log;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
@ -17,7 +17,6 @@ import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import com.djrapitops.plugin.utilities.Verify;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.UUID;
@ -85,10 +84,10 @@ public class ManageBackupCommand extends CommandNode {
* @param dbName Name of database (mysql/sqlite)
* @param copyFromDB Database you want to backup.
*/
private void createNewBackup(String dbName, Database copyFromDB) throws SQLException {
private void createNewBackup(String dbName, Database copyFromDB) {
SQLiteDB backupDB = null;
try {
String timeStamp = FormatUtils.formatTimeStampISO8601NoClock(System.currentTimeMillis());
String timeStamp = Formatters.iso8601NoClock().apply(System::currentTimeMillis);
String fileName = dbName + "-backup-" + timeStamp;
backupDB = new SQLiteDB(fileName);
Collection<UUID> uuids = copyFromDB.fetch().getSavedUUIDs();

View File

@ -1,8 +1,7 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.FatalDBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.settings.Permissions;
@ -66,12 +65,13 @@ public class ManageClearCommand extends CommandNode {
database.remove().everything();
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_CLEAR_SUCCESS).toString());
} catch (FatalDBException e) {
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_FAIL).toString()
+ " Error was fatal, so all information may not have been removed.");
Log.toLog(this.getClass(), e);
} catch (DBException e) {
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_FAIL).toString());
} catch (DBOpException e) {
if (e.isFatal()) {
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_FAIL).toString()
+ " Error was fatal, so all information may not have been removed.");
} else {
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_FAIL).toString());
}
Log.toLog(this.getClass(), e);
} finally {
this.cancel();

View File

@ -2,7 +2,6 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.connection.*;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.InfoSystem;
import com.djrapitops.plan.system.info.request.CheckConnectionRequest;
@ -11,7 +10,6 @@ import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.webserver.WebServerSystem;
import com.djrapitops.plugin.api.utility.log.Log;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
import com.djrapitops.plugin.command.ISender;
@ -44,24 +42,19 @@ public class ManageConDebugCommand extends CommandNode {
}
private void testServers(ISender sender) {
try {
List<Server> servers = Database.getActive().fetch().getServers();
List<Server> servers = Database.getActive().fetch().getServers();
if (servers.isEmpty()) {
sender.sendMessage("§cNo Servers found in the database.");
if (servers.isEmpty()) {
sender.sendMessage("§cNo Servers found in the database.");
}
String accessAddress = WebServerSystem.getInstance().getWebServer().getAccessAddress();
UUID thisServer = ServerInfo.getServerUUID();
for (Server server : servers) {
if (thisServer.equals(server.getUuid())) {
continue;
}
String accessAddress = WebServerSystem.getInstance().getWebServer().getAccessAddress();
UUID thisServer = ServerInfo.getServerUUID();
for (Server server : servers) {
if (thisServer.equals(server.getUuid())) {
continue;
}
testServer(sender, accessAddress, server);
}
} catch (DBException e) {
Log.toLog(this.getClass().getName(), e);
testServer(sender, accessAddress, server);
}
}

View File

@ -1,6 +1,6 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.settings.locale.Locale;
@ -70,7 +70,7 @@ public class ManageRemoveCommand extends CommandNode {
database.remove().player(uuid);
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_REMOVE_SUCCESS).parse(playerName, Database.getActive().getConfigName()));
} catch (DBException e) {
} catch (DBOpException e) {
Log.toLog(this.getClass(), e);
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_FAIL).toString());
} finally {

View File

@ -1,44 +0,0 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package com.djrapitops.plan.data;
import org.apache.commons.text.WordUtils;
/**
* IDs of various actions
*
* @author Rsl1122
*/
public enum Actions {
UNKNOWN(-1),
FIRST_SESSION(1),
FIRST_LOGOUT(2),
NEW_NICKNAME(3),
KILLED(-2); // Not stored in ActionsTable.
private final int id;
Actions(int id) {
this.id = id;
}
public static Actions getById(int id) {
for (Actions a : values()) {
if (a.getId() == id) {
return a;
}
}
return UNKNOWN;
}
public int getId() {
return id;
}
@Override
public String toString() {
return WordUtils.capitalizeFully(name(), '_').replace('_', ' ');
}
}

View File

@ -1,462 +0,0 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package com.djrapitops.plan.data;
import com.djrapitops.plan.data.calculation.ActivityIndex;
import com.djrapitops.plan.data.container.Action;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.container.PlayerKill;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.utilities.comparators.ActionComparator;
import com.djrapitops.plan.utilities.comparators.GeoInfoComparator;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Data container object for a single player.
* <p>
* Created to streamline analysis and to make it easier to understand.
*
* @author Rsl1122
*/
public class PlayerProfile {
// Identification
private final UUID uuid;
private final String name;
// Basic information
private final long registered;
private Map<UUID, Long> registeredMap;
private Set<UUID> bannedOnServers;
private Set<UUID> oppedOnServers;
private int timesKicked;
// Activity related information
private Map<UUID, List<Session>> sessions;
private List<Action> actions;
private Map<UUID, WorldTimes> worldTimesMap;
// Extra information
private Map<UUID, List<String>> nicknames;
private List<GeoInfo> geoInformation;
// Plugin data
private Map<String, String> pluginReplaceMap;
// Value that requires lot of processing
private Map<Long, ActivityIndex> activityIndexCache;
public PlayerProfile(UUID uuid, String name, long registered) {
this.uuid = uuid;
this.name = name;
this.registered = registered;
registeredMap = new HashMap<>();
bannedOnServers = new HashSet<>();
oppedOnServers = new HashSet<>();
sessions = new HashMap<>();
actions = new ArrayList<>();
worldTimesMap = new HashMap<>();
nicknames = new HashMap<>();
geoInformation = new ArrayList<>();
pluginReplaceMap = new HashMap<>();
activityIndexCache = new HashMap<>();
}
public static long getPlaytime(Stream<Session> s) {
return s.mapToLong(Session::getLength).sum();
}
public static long getLongestSession(Stream<Session> s) {
OptionalLong longestSession = s.mapToLong(Session::getLength).max();
if (longestSession.isPresent()) {
return longestSession.getAsLong();
}
return -1;
}
public static long getSessionMedian(Stream<Session> s) {
List<Long> sessionLenghts = s.map(Session::getLength)
.sorted()
.collect(Collectors.toList());
if (sessionLenghts.isEmpty()) {
return 0;
}
return sessionLenghts.get(sessionLenghts.size() / 2);
}
public static long getSessionAverage(Stream<Session> s) {
OptionalDouble average = s.map(Session::getLength)
.mapToLong(i -> i)
.average();
if (average.isPresent()) {
return (long) average.getAsDouble();
}
return 0L;
}
public static Stream<PlayerKill> getPlayerKills(Stream<Session> s) {
return s.map(Session::getPlayerKills)
.flatMap(Collection::stream);
}
public static long getDeathCount(Stream<Session> s) {
return s.mapToLong(Session::getDeaths)
.sum();
}
public static long getMobKillCount(Stream<Session> s) {
return s.mapToLong(Session::getMobKills)
.sum();
}
// Calculating Getters
public ActivityIndex getActivityIndex(long date) {
return activityIndexCache.computeIfAbsent(date, dateValue -> new ActivityIndex(this, dateValue));
}
/**
* Get the total world times of the player.
*
* @return returns the WorldTimes in the "null" key of the map.
*/
public WorldTimes getWorldTimes() {
return worldTimesMap.getOrDefault(null, new WorldTimes(new HashMap<>()));
}
public void setWorldTimes(Map<UUID, WorldTimes> worldTimes) {
worldTimesMap.putAll(worldTimes);
}
/**
* Get world times per server for this player.
*
* @return a copy of the WorldTimes Map without the "null" key.
*/
public Map<UUID, WorldTimes> getWorldTimesPerServer() {
Map<UUID, WorldTimes> map = new HashMap<>(worldTimesMap);
map.remove(null);
return map;
}
public UUID getFavoriteServer() {
long max = 0L;
UUID maxServer = null;
for (Map.Entry<UUID, WorldTimes> entry : getWorldTimesPerServer().entrySet()) {
long playTime = entry.getValue().getTotal();
if (playTime > max) {
max = playTime;
maxServer = entry.getKey();
}
}
return maxServer;
}
public long getLastSeen() {
return getLastSeen(getAllSessions());
}
public long getLastSeen(Stream<Session> s) {
OptionalLong max = s.mapToLong(session -> Math.max(session.getSessionStart(), session.getSessionEnd())).max();
if (max.isPresent()) {
return max.getAsLong();
}
return 0;
}
public long getTotalPlaytime() {
return getPlaytime(-1, System.currentTimeMillis() + 1L);
}
public long getPlaytime(long after, long before) {
return getPlaytime(getSessions(after, before));
}
public long getPlaytime(UUID serverUUID) {
return getPlaytime(getSessions(serverUUID).stream());
}
public long getLongestSession() {
return getLongestSession(-1, System.currentTimeMillis() + 1L);
}
public long getLongestSession(int after, long before) {
return getLongestSession(getSessions(after, before));
}
public long getLongestSession(UUID serverUUID) {
return getLongestSession(getSessions(serverUUID).stream());
}
public long getSessionMedian() {
return getSessionMedian(-1, System.currentTimeMillis() + 1L);
}
public long getSessionMedian(int after, long before) {
return getSessionMedian(getSessions(after, before));
}
public long getSessionMedian(UUID serverUUID) {
return getSessionMedian(getSessions(serverUUID).stream());
}
// Special Getters
public long getSessionAverage() {
return getSessionAverage(-1, System.currentTimeMillis() + 1L);
}
public long getSessionAverage(int after, long before) {
return getSessionAverage(getSessions(after, before));
}
public long getSessionAverage(UUID serverUUID) {
return getSessionAverage(getSessions(serverUUID).stream());
}
public boolean playedBetween(long after, long before) {
return getSessions(after, before).findFirst().isPresent();
}
public Stream<Session> getAllSessions() {
return sessions.values().stream().flatMap(Collection::stream);
}
public Stream<Session> getSessions(long after, long before) {
return getAllSessions()
.filter(session -> session.getSessionStart() >= after && session.getSessionStart() <= before);
}
public GeoInfo getMostRecentGeoInfo() {
if (geoInformation.isEmpty()) {
return new GeoInfo("-", "Not Known", System.currentTimeMillis(), "");
}
geoInformation.sort(new GeoInfoComparator());
return geoInformation.get(0);
}
public List<Action> getAllActions() {
List<Action> actions = new ArrayList<>(this.actions);
getPlayerKills().map(PlayerKill::convertToAction).forEach(actions::add);
actions.sort(new ActionComparator());
return actions;
}
public Stream<PlayerKill> getPlayerKills() {
return getPlayerKills(getAllSessions());
}
public Stream<PlayerKill> getPlayerKills(UUID serverUUID) {
return getPlayerKills(getSessions(serverUUID).stream());
}
public long getPlayerKillCount() {
return getPlayerKills().count();
}
public long getPlayerKillCount(UUID serverUUID) {
return getPlayerKills(serverUUID).count();
}
public long getDeathCount() {
return getDeathCount(getAllSessions());
}
public long getDeathCount(UUID serverUUID) {
return getDeathCount(getSessions(serverUUID).stream());
}
public long getMobKillCount() {
return getMobKillCount(getAllSessions());
}
public long getMobKillCount(UUID serverUUID) {
return getMobKillCount(getSessions(serverUUID).stream());
}
public long getSessionCount() {
return getAllSessions().count();
}
public long getSessionCount(UUID serverUUID) {
return getSessions(serverUUID).size();
}
// Setters & Adders
public long getRegistered(UUID serverUUID) {
return registeredMap.getOrDefault(serverUUID, -1L);
}
public void bannedOnServer(UUID serverUUID) {
bannedOnServers.add(serverUUID);
}
public void oppedOnServer(UUID serverUUID) {
oppedOnServers.add(serverUUID);
}
public void bannedOnServer(Collection<UUID> serverUUIDs) {
bannedOnServers.addAll(serverUUIDs);
}
public void oppedOnServer(Collection<UUID> serverUUIDs) {
oppedOnServers.addAll(serverUUIDs);
}
public void setSessions(UUID serverUUID, List<Session> sessions) {
this.sessions.put(serverUUID, sessions);
}
public void addActiveSession(Session activeSession) {
UUID serverUUID = ServerInfo.getServerUUID();
List<Session> sessions = getSessions(serverUUID);
sessions.add(activeSession);
this.sessions.put(serverUUID, sessions);
}
public List<Session> getSessions(UUID serverUUID) {
return this.sessions.getOrDefault(serverUUID, new ArrayList<>());
}
public void addReplaceValue(String placeholder, Serializable value) {
pluginReplaceMap.put(placeholder, value.toString());
}
public void setWorldTimes(UUID serverUUID, WorldTimes worldTimes) {
worldTimesMap.put(serverUUID, worldTimes);
}
public void setTotalWorldTimes(WorldTimes worldTimes) {
worldTimesMap.put(null, worldTimes);
}
public void setRegistered(UUID serverUUID, long registered) {
registeredMap.put(serverUUID, registered);
}
public int getTimesKicked() {
return timesKicked;
}
// Default Setters
public void setTimesKicked(int timesKicked) {
this.timesKicked = timesKicked;
}
public Map<UUID, List<String>> getNicknames() {
return nicknames;
}
public void setNicknames(Map<UUID, List<String>> nicknames) {
this.nicknames = nicknames;
}
public List<GeoInfo> getGeoInformation() {
return geoInformation;
}
// Default Getters
public void setGeoInformation(List<GeoInfo> geoInformation) {
this.geoInformation = geoInformation;
}
public UUID getUuid() {
return uuid;
}
public String getName() {
return name;
}
public long getRegistered() {
return registered;
}
public Set<UUID> getBannedOnServers() {
return bannedOnServers;
}
public Set<UUID> getOppedOnServers() {
return oppedOnServers;
}
public Map<UUID, List<Session>> getSessions() {
return sessions;
}
public void setSessions(Map<UUID, List<Session>> sessions) {
this.sessions.putAll(sessions);
}
public List<Action> getActions() {
return actions;
}
public void setActions(List<Action> actions) {
this.actions = actions;
}
public Map<String, String> getPluginReplaceMap() {
return pluginReplaceMap;
}
/**
* Get the WorldTimes map.
*
* @return Map that contains WorldTimes for each server and a total in the "null" key.
*/
public Map<UUID, WorldTimes> getWorldTimesMap() {
return worldTimesMap;
}
// OfflinePlayer methods for possible PluginData analysis
public boolean isBanned() {
return bannedOnServers.size() != 0;
}
public boolean isOp() {
return oppedOnServers.contains(ServerInfo.getServerUUID());
}
public static long getAFKTime(Stream<Session> sessions) {
return sessions.mapToLong(Session::getAfkLength).sum();
}
public static long getActivePlaytime(Stream<Session> sessions) {
return sessions.mapToLong(Session::getActiveLength).sum();
}
public void calculateWorldTimesPerServer() {
if (worldTimesMap.containsKey(ServerInfo.getServerUUID())) {
return;
}
for (Map.Entry<UUID, List<Session>> entry : sessions.entrySet()) {
UUID serverUUID = entry.getKey();
List<Session> sessions = entry.getValue();
WorldTimes times = worldTimesMap.getOrDefault(serverUUID, new WorldTimes(new HashMap<>()));
for (Session session : sessions) {
WorldTimes worldTimes = session.getWorldTimes();
times.add(worldTimes);
}
worldTimesMap.put(serverUUID, times);
}
}
}

View File

@ -1,391 +0,0 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package com.djrapitops.plan.data;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.container.PlayerKill;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.utilities.analysis.AnalysisUtils;
import com.djrapitops.plan.utilities.analysis.MathUtils;
import com.djrapitops.plan.utilities.comparators.PlayerProfileLastPlayedComparator;
import com.djrapitops.plan.utilities.comparators.TPSComparator;
import com.djrapitops.plan.utilities.html.tables.PlayersTableCreator;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Data class for streamlining Analysis data.
* <p>
* Most of the methods are not the most efficient when multiple of them are used.
*
* @author Rsl1122
*/
public class ServerProfile {
private final UUID serverUUID;
// Database information
private List<PlayerProfile> players;
private List<TPS> tps;
private Map<String, Integer> commandUsage;
// Information calculated with SQL
private WorldTimes serverWorldtimes;
private long lastPeakDate;
private int lastPeakPlayers;
private long allTimePeak;
private int allTimePeakPlayers;
// Calculated once
private Map<UUID, PlayerProfile> playerMap;
public ServerProfile(UUID serverUUID) {
this.serverUUID = serverUUID;
players = new ArrayList<>();
tps = new ArrayList<>();
commandUsage = new HashMap<>();
allTimePeak = -1;
allTimePeakPlayers = -1;
lastPeakDate = -1;
lastPeakPlayers = -1;
}
public static long getLowSpikeCount(List<TPS> tpsData) {
int mediumThreshold = Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber();
boolean wasLow = false;
long spikeCount = 0L;
for (TPS tpsObj : tpsData) {
double tps = tpsObj.getTicksPerSecond();
if (tps < mediumThreshold) {
if (!wasLow) {
spikeCount++;
wasLow = true;
}
} else {
wasLow = false;
}
}
return spikeCount;
}
public static List<PlayerKill> getPlayerKills(List<Session> s) {
List<PlayerKill> kills = new ArrayList<>();
for (Session session : s) {
kills.addAll(session.getPlayerKills());
}
return kills;
}
public static long getMobKillCount(List<Session> s) {
long total = 0;
for (Session session : s) {
total += session.getMobKills();
}
return total;
}
public static long getDeathCount(List<Session> s) {
long total = 0;
for (Session session : s) {
total += session.getDeaths();
}
return total;
}
public static long serverDownTime(List<TPS> tpsData) {
long lastDate = -1;
long downTime = 0;
for (TPS tps : tpsData) {
long date = tps.getDate();
if (lastDate == -1) {
lastDate = date;
continue;
}
long diff = date - lastDate;
if (diff > TimeAmount.MINUTE.ms() * 3L) {
downTime += diff;
}
lastDate = date;
}
return downTime;
}
public static long serverIdleTime(List<TPS> tpsData) {
long lastDate = -1;
int lastPlayers = 0;
long idleTime = 0;
for (TPS tps : tpsData) {
long date = tps.getDate();
int players = tps.getPlayers();
if (lastDate == -1) {
lastDate = date;
lastPlayers = players;
continue;
}
long diff = date - lastDate;
if (lastPlayers == 0 && players == 0) {
idleTime += diff;
}
lastDate = date;
lastPlayers = players;
}
return idleTime;
}
public static double aboveLowThreshold(List<TPS> tpsData) {
if (tpsData.isEmpty()) {
return 1;
}
int threshold = Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber();
long count = 0;
for (TPS tps : tpsData) {
if (tps.getTicksPerSecond() >= threshold) {
count++;
}
}
return count * 1.0 / tpsData.size();
}
public List<PlayerProfile> getPlayers() {
return players;
}
public void setPlayers(List<PlayerProfile> players) {
this.players = players;
}
public List<TPS> getTps() {
return tps;
}
public void setTps(List<TPS> tps) {
this.tps = tps;
}
public Map<String, Integer> getCommandUsage() {
return commandUsage;
}
public void setCommandUsage(Map<String, Integer> commandUsage) {
this.commandUsage = commandUsage;
}
public double getAverageTPS(long after, long before) {
OptionalDouble average = getTPSData(after, before)
.mapToDouble(TPS::getTicksPerSecond)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double getAverageCPU(long after, long before) {
OptionalDouble average = getTPSData(after, before)
.mapToDouble(TPS::getCPUUsage)
.filter(num -> num >= 0)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double getAverageRAM(long after, long before) {
OptionalDouble average = getTPSData(after, before)
.mapToDouble(TPS::getUsedMemory)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double getAverageEntities(long after, long before) {
OptionalDouble average = getTPSData(after, before)
.mapToDouble(TPS::getEntityCount)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double getAverageChunks(long after, long before) {
OptionalDouble average = getTPSData(after, before)
.mapToDouble(TPS::getChunksLoaded)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public long getNewPlayers(long after, long before) {
return getPlayersWhoRegistered(after, before).count();
}
public long getUniquePlayers(long after, long before) {
return getPlayersWhoPlayedBetween(after, before).count();
}
public double getNewPlayersPerDay(long after, long before) {
long days = AnalysisUtils.getNumberOfDaysBetween(after, before);
long newPlayers = getNewPlayers(after, before);
return days == 0 ? newPlayers : newPlayers * 1.0 / days;
}
public Stream<PlayerProfile> getPlayersWhoPlayedBetween(long after, long before) {
return players.stream()
.filter(player -> player.playedBetween(after, before));
}
public Stream<PlayerProfile> getPlayersWhoRegistered(long after, long before) {
return players.stream()
.filter(player -> player.getRegistered() >= after && player.getRegistered() <= before);
}
public Stream<TPS> getTPSData(long after, long before) {
return tps.stream().filter(tps -> tps.getDate() >= after && tps.getDate() <= before);
}
public String createPlayersTableBody() {
players.sort(new PlayerProfileLastPlayedComparator());
return PlayersTableCreator.createTable(players);
}
public List<String> getGeoLocations() {
return players.stream()
.map(PlayerProfile::getMostRecentGeoInfo)
.map(GeoInfo::getGeolocation)
.collect(Collectors.toList());
}
// Default setters & getters
public long getTotalPlaytime() {
return serverWorldtimes.getTotal();
}
public long getAveragePlayTime() {
return MathUtils.averageLong(getTotalPlaytime(), getPlayerCount());
}
public long getPlayerCount() {
return players.size();
}
public Map<UUID, List<Session>> getSessions() {
return players.stream().collect(Collectors.toMap(PlayerProfile::getUuid, p -> p.getSessions(serverUUID)));
}
public List<Session> getAllSessions() {
return players.stream().map(p -> p.getSessions(serverUUID)).flatMap(Collection::stream).collect(Collectors.toList());
}
public WorldTimes getServerWorldtimes() {
return serverWorldtimes;
}
public void setServerWorldtimes(WorldTimes serverWorldtimes) {
this.serverWorldtimes = serverWorldtimes;
}
public long getLastPeakDate() {
return lastPeakDate;
}
public void setLastPeakDate(long lastPeakDate) {
this.lastPeakDate = lastPeakDate;
}
public int getLastPeakPlayers() {
return lastPeakPlayers;
}
public void setLastPeakPlayers(int lastPeakPlayers) {
this.lastPeakPlayers = lastPeakPlayers;
}
public long getAllTimePeak() {
return allTimePeak;
}
public void setAllTimePeak(long allTimePeak) {
this.allTimePeak = allTimePeak;
}
public int getAllTimePeakPlayers() {
return allTimePeakPlayers;
}
public void setAllTimePeakPlayers(int allTimePeakPlayers) {
this.allTimePeakPlayers = allTimePeakPlayers;
}
public Stream<PlayerProfile> getOps() {
return players.stream().filter(PlayerProfile::isOp);
}
public Set<UUID> getUuids() {
Set<UUID> uuids = new HashSet<>();
for (PlayerProfile player : players) {
uuids.add(player.getUuid());
}
return uuids;
}
public long serverDownTime(long after, long before) {
return serverDownTime(getTPSData(after, before)
.sorted(new TPSComparator())
.collect(Collectors.toList()));
}
public long serverIdleTime(long after, long before) {
return serverIdleTime(getTPSData(after, before)
.sorted(new TPSComparator())
.collect(Collectors.toList()));
}
public PlayerProfile getPlayer(UUID uuid) {
if (playerMap == null) {
playerMap = players.stream().collect(Collectors.toMap(PlayerProfile::getUuid, Function.identity()));
}
return playerMap.get(uuid);
}
public void addActiveSessions(Map<UUID, Session> activeSessions) {
for (Map.Entry<UUID, Session> entry : activeSessions.entrySet()) {
UUID uuid = entry.getKey();
Session session = entry.getValue();
session.setSessionID((int) session.getSessionStart());
PlayerProfile player = getPlayer(uuid);
if (player != null) {
player.addActiveSession(session);
}
}
}
}

View File

@ -1,407 +0,0 @@
package com.djrapitops.plan.data.calculation;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.data.PlayerProfile;
import com.djrapitops.plan.data.ServerProfile;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.container.StickyData;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.element.AnalysisContainer;
import com.djrapitops.plan.data.plugin.PluginData;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.info.server.ServerProperties;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.theme.Theme;
import com.djrapitops.plan.system.settings.theme.ThemeVal;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plan.utilities.analysis.AnalysisUtils;
import com.djrapitops.plan.utilities.analysis.MathUtils;
import com.djrapitops.plan.utilities.comparators.SessionStartComparator;
import com.djrapitops.plan.utilities.html.Html;
import com.djrapitops.plan.utilities.html.graphs.ActivityStackGraph;
import com.djrapitops.plan.utilities.html.graphs.PunchCardGraph;
import com.djrapitops.plan.utilities.html.graphs.WorldMap;
import com.djrapitops.plan.utilities.html.graphs.calendar.ServerCalendar;
import com.djrapitops.plan.utilities.html.graphs.line.*;
import com.djrapitops.plan.utilities.html.graphs.pie.ActivityPie;
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
import com.djrapitops.plan.utilities.html.structure.AnalysisPluginsTabContentCreator;
import com.djrapitops.plan.utilities.html.structure.SessionTabStructureCreator;
import com.djrapitops.plan.utilities.html.tables.CommandUseTable;
import com.djrapitops.plan.utilities.html.tables.SessionsTableCreator;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.*;
import java.util.stream.Collectors;
/**
* Big container object for Data.
* <p>
* Contains parts that can be analysed. Each part has their own purpose.
* <p>
* Parts contain variables that can be added to. These variables are then
* analysed using the analysis method.
* <p>
* After being analysed the ReplaceMap can be retrieved for replacing
* placeholders on the server.html file.
*
* @author Rsl1122
* @since 3.5.2
*/
public class AnalysisData extends RawData {
private long refreshDate;
private Map<String, Long> analyzedValues;
private Set<StickyData> stickyMonthData;
private List<PlayerProfile> players;
public AnalysisData() {
analyzedValues = new HashMap<>();
stickyMonthData = new HashSet<>();
}
public void parsePluginsSection(Map<PluginData, AnalysisContainer> containers) {
String[] navAndTabs = AnalysisPluginsTabContentCreator.createContent(containers);
addValue("navPluginsTabs", navAndTabs[0]);
addValue("tabsPlugins", navAndTabs[1]);
}
private void addConstants() {
addValue("version", PlanPlugin.getInstance().getVersion());
addValue("worldPieColors", Theme.getValue(ThemeVal.GRAPH_WORLD_PIE));
addValue("gmPieColors", Theme.getValue(ThemeVal.GRAPH_GM_PIE));
addValue("serverName", Settings.SERVER_NAME.toString().replaceAll("[^a-zA-Z0-9_\\s]", "_"));
addValue("timeZone", MiscUtils.getTimeZoneOffsetHours());
addValue("refresh", FormatUtils.formatTimeStampClock(refreshDate));
addValue("activityPieColors", Theme.getValue(ThemeVal.GRAPH_ACTIVITY_PIE));
addValue("playersGraphColor", Theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE));
addValue("tpsHighColor", Theme.getValue(ThemeVal.GRAPH_TPS_HIGH));
addValue("tpsMediumColor", Theme.getValue(ThemeVal.GRAPH_TPS_MED));
addValue("tpsLowColor", Theme.getValue(ThemeVal.GRAPH_TPS_LOW));
addValue("worldMapColLow", Theme.getValue(ThemeVal.WORLD_MAP_LOW));
addValue("worldMapColHigh", Theme.getValue(ThemeVal.WORLD_MAP_HIGH));
addValue("tpsMedium", Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber());
addValue("tpsHigh", Settings.THEME_GRAPH_TPS_THRESHOLD_HIGH.getNumber());
ServerProperties serverProperties = ServerInfo.getServerProperties();
addValue("playersMax", serverProperties.getMaxPlayers());
addValue("playersOnline", serverProperties.getOnlinePlayers());
}
public void analyze(ServerProfile profile) {
long now = System.currentTimeMillis();
refreshDate = now;
long dayAgo = now - TimeAmount.DAY.ms();
long weekAgo = now - TimeAmount.WEEK.ms();
long monthAgo = now - TimeAmount.MONTH.ms();
addConstants();
got("now", now);
got("dayAgo", dayAgo);
got("weekAgo", weekAgo);
got("monthAgo", monthAgo);
Map<UUID, List<Session>> sessions = profile.getSessions();
List<Session> allSessions = profile.getAllSessions();
allSessions.sort(new SessionStartComparator());
players = profile.getPlayers();
List<PlayerProfile> ops = profile.getOps().collect(Collectors.toList());
long playersTotal = got("playersTotal", players.size());
List<TPS> tpsData = profile.getTPSData(0, now).collect(Collectors.toList());
List<TPS> tpsDataDay = profile.getTPSData(dayAgo, now).collect(Collectors.toList());
List<TPS> tpsDataWeek = profile.getTPSData(weekAgo, now).collect(Collectors.toList());
List<TPS> tpsDataMonth = profile.getTPSData(monthAgo, now).collect(Collectors.toList());
List<String> geoLocations = profile.getGeoLocations();
Map<String, Integer> commandUsage = profile.getCommandUsage();
directProfileVariables(profile);
performanceTab(tpsData, tpsDataDay, tpsDataWeek, tpsDataMonth);
sessionData(monthAgo, sessions, allSessions);
onlineActivityNumbers(profile, sessions, players);
geolocationsTab(geoLocations);
commandUsage(commandUsage);
List<Long> registered = profile.getPlayers().stream().map(PlayerProfile::getRegistered).collect(Collectors.toList());
ServerCalendar serverCalendar = new ServerCalendar(registered, sessions);
addValue("calendarSeries", serverCalendar.toCalendarSeries());
addValue("firstDay", 1);
addValue("ops", ops.size());
addValue("playersTotal", playersTotal);
healthTab(now, players, tpsDataMonth);
long totalPlaytime = profile.getTotalPlaytime();
addValue("playtimeTotal", playersTotal != 0 ? FormatUtils.formatTimeAmount(totalPlaytime) : "No Players");
addValue("playtimeAverage", playersTotal != 0 ? FormatUtils.formatTimeAmount(MathUtils.averageLong(totalPlaytime, playersTotal)) : "-");
}
private void healthTab(long now, List<PlayerProfile> players, List<TPS> tpsDataMonth) {
TreeMap<Long, Map<String, Set<UUID>>> activityData = AnalysisUtils.turnToActivityDataMap(now, players);
Map<String, Set<UUID>> activityNow = activityData.getOrDefault(now, new HashMap<>());
ActivityStackGraph activityStackGraph = new ActivityStackGraph(activityData);
String activityPieSeries = new ActivityPie(activityNow).toHighChartsSeries();
addValue("activityStackCategories", activityStackGraph.toHighChartsLabels());
addValue("activityStackSeries", activityStackGraph.toHighChartsSeries());
addValue("activityPieSeries", activityPieSeries);
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
addValue("playersRegular", (veryActiveNow.size() + activeNow.size() + regularNow.size()));
HealthNotes healthNotes = new HealthNotes(this, activityData, tpsDataMonth, now);
healthNotes.analyzeHealth();
addValue("healthNotes", healthNotes.parse());
addValue("healthIndex", healthNotes.getServerHealth());
}
private void commandUsage(Map<String, Integer> commandUsage) {
addValue("commandUniqueCount", String.valueOf(commandUsage.size()));
addValue("commandCount", MathUtils.sumInt(commandUsage.values().stream().map(i -> (int) i)));
addValue("tableBodyCommands", new CommandUseTable(commandUsage).parseBody());
}
private void geolocationsTab(List<String> geoLocations) {
addValue("geoMapSeries", new WorldMap(geoLocations).toHighChartsSeries());
}
private void onlineActivityNumbers(ServerProfile profile, Map<UUID, List<Session>> sessions, List<PlayerProfile> players) {
long now = value("now");
long dayAgo = value("dayAgo");
long weekAgo = value("weekAgo");
long monthAgo = value("monthAgo");
List<PlayerProfile> newDay = profile.getPlayersWhoRegistered(dayAgo, now).collect(Collectors.toList());
List<PlayerProfile> newWeek = profile.getPlayersWhoRegistered(weekAgo, now).collect(Collectors.toList());
List<PlayerProfile> newMonth = profile.getPlayersWhoRegistered(monthAgo, now).collect(Collectors.toList());
List<PlayerProfile> uniqueDay = profile.getPlayersWhoPlayedBetween(dayAgo, now).collect(Collectors.toList());
List<PlayerProfile> uniqueWeek = profile.getPlayersWhoPlayedBetween(weekAgo, now).collect(Collectors.toList());
List<PlayerProfile> uniqueMonth = profile.getPlayersWhoPlayedBetween(monthAgo, now).collect(Collectors.toList());
int uniqD = uniqueDay.size();
int uniqW = uniqueWeek.size();
int uniqM = uniqueMonth.size();
long newD = got("newD", newDay.size());
long newW = got("newW", newWeek.size());
long newM = got("newM", newMonth.size());
long playersTotal = value("playersTotal");
addValue("playersDay", uniqD);
addValue("playersWeek", uniqW);
addValue("playersMonth", uniqM);
addValue("playersNewDay", newD);
addValue("playersNewWeek", newW);
addValue("playersNewMonth", newM);
addValue("playersAverage", AnalysisUtils.getUniqueJoinsPerDay(sessions, -1));
addValue("playersAverageDay", AnalysisUtils.getUniqueJoinsPerDay(sessions, dayAgo));
addValue("playersAverageWeek", AnalysisUtils.getUniqueJoinsPerDay(sessions, weekAgo));
addValue("playersAverageMonth", AnalysisUtils.getUniqueJoinsPerDay(sessions, monthAgo));
addValue("playersNewAverage", AnalysisUtils.getNewUsersPerDay(toRegistered(players), -1, playersTotal));
addValue("playersNewAverageDay", AnalysisUtils.getNewUsersPerDay(toRegistered(newDay), -1, newD));
addValue("playersNewAverageWeek", AnalysisUtils.getNewUsersPerDay(toRegistered(newWeek), -1, newW));
addValue("playersNewAverageMonth", AnalysisUtils.getNewUsersPerDay(toRegistered(newMonth), -1, newM));
stickiness(now, weekAgo, monthAgo, newDay, newWeek, newMonth);
}
private void stickiness(long now, long weekAgo, long monthAgo,
List<PlayerProfile> newDay, List<PlayerProfile> newWeek, List<PlayerProfile> newMonth) {
long newD = value("newD");
long newW = value("newW");
long newM = value("newM");
List<PlayerProfile> playersStuckPerMonth = newMonth.stream()
.filter(p -> {
long backLimit = Math.max(monthAgo, p.getRegistered());
long half = backLimit + ((now - backLimit) / 2L);
return p.playedBetween(backLimit, half) && p.playedBetween(half, now);
})
.collect(Collectors.toList());
List<PlayerProfile> playersStuckPerWeek = newWeek.stream()
.filter(p -> {
long backLimit = Math.max(weekAgo, p.getRegistered());
long half = backLimit + ((now - backLimit) / 2L);
return p.playedBetween(backLimit, half) && p.playedBetween(half, now);
})
.collect(Collectors.toList());
int stuckPerM = playersStuckPerMonth.size();
int stuckPerW = playersStuckPerWeek.size();
got("stuckPerM", stuckPerM);
got("stuckPerW", stuckPerW);
stickyMonthData = newMonth.stream().map(StickyData::new).distinct().collect(Collectors.toSet());
addValue("playersStuckMonth", stuckPerM);
addValue("playersStuckWeek", stuckPerW);
addValue("playersStuckPercMonth", newM != 0 ? FormatUtils.cutDecimals(MathUtils.averageDouble(stuckPerM, newM) * 100.0) + "%" : "-");
addValue("playersStuckPercWeek", newW != 0 ? FormatUtils.cutDecimals(MathUtils.averageDouble(stuckPerW, newW) * 100.0) + "%" : "-");
stuckPerDay(newDay, newD, monthAgo);
}
private void stuckPerDay(List<PlayerProfile> newDay, long newD, long monthAgo) {
if (newD != 0) {
List<PlayerProfile> stuckAfterMonth = new ArrayList<>();
List<PlayerProfile> notStuckAfterMonth = new ArrayList<>();
for (PlayerProfile player : players) {
long registered = player.getRegistered();
// Discard uncertain data
if (registered > monthAgo) {
continue;
}
long monthAfterRegister = registered + TimeAmount.MONTH.ms();
long half = registered + (TimeAmount.MONTH.ms() / 2L);
if (player.playedBetween(registered, half) && player.playedBetween(half, monthAfterRegister)) {
stuckAfterMonth.add(player);
} else {
notStuckAfterMonth.add(player);
}
}
if (stuckAfterMonth.isEmpty() || notStuckAfterMonth.isEmpty()) {
addValue("playersStuckDay", 0);
addValue("playersStuckPercDay", "Not enough data");
return;
}
List<StickyData> stuck = stuckAfterMonth.stream().map(StickyData::new).collect(Collectors.toList());
List<StickyData> nonStuck = notStuckAfterMonth.stream().map(StickyData::new).collect(Collectors.toList());
StickyData avgStuck = AnalysisUtils.average(stuck);
StickyData avgNonStuck = AnalysisUtils.average(nonStuck);
int stuckPerD = 0;
for (PlayerProfile player : newDay) {
StickyData stickyData = new StickyData(player);
if (stickyData.distance(avgStuck) < stickyData.distance(avgNonStuck)) {
stuckPerD++;
}
}
addValue("playersStuckDay", stuckPerD);
addValue("playersStuckPercDay", FormatUtils.cutDecimals(MathUtils.averageDouble(stuckPerD, newD) * 100.0) + "%");
} else {
addValue("playersStuckDay", 0);
addValue("playersStuckPercDay", "-");
}
}
private List<Long> toRegistered(List<PlayerProfile> players) {
return players.stream().map(PlayerProfile::getRegistered).collect(Collectors.toList());
}
private void sessionData(long monthAgo, Map<UUID, List<Session>> sessions, List<Session> allSessions) {
List<Session> sessionsMonth = allSessions.stream()
.filter(s -> s.getSessionStart() >= monthAgo)
.collect(Collectors.toList());
String[] tables = SessionsTableCreator.createTable(sessions, allSessions);
String[] sessionContent = SessionTabStructureCreator.createStructure(sessions, allSessions);
addValue("sessionCount", allSessions.size());
addValue("accordionSessions", sessionContent[0]);
addValue("sessionTabGraphViewFunctions", sessionContent[1]);
addValue("tableBodySessions", tables[0]);
addValue("listRecentLogins", tables[1]);
addValue("sessionAverage", FormatUtils.formatTimeAmount(MathUtils.averageLong(allSessions.stream().map(Session::getLength))));
addValue("punchCardSeries", new PunchCardGraph(sessionsMonth).toHighChartsSeries());
addValue("deaths", ServerProfile.getDeathCount(allSessions));
addValue("mobKillCount", ServerProfile.getMobKillCount(allSessions));
addValue("killCount", ServerProfile.getPlayerKills(allSessions).size());
}
private void directProfileVariables(ServerProfile profile) {
WorldTimes worldTimes = profile.getServerWorldtimes();
long allTimePeak = profile.getAllTimePeak();
long lastPeak = profile.getLastPeakDate();
String playersTableBody = profile.createPlayersTableBody();
addValue("tablePlayerlist", Settings.PLAYERTABLE_FOOTER.isTrue() ?
Html.TABLE_PLAYERS_FOOTER.parse(playersTableBody)
: Html.TABLE_PLAYERS.parse(playersTableBody));
addValue("worldTotal", FormatUtils.formatTimeAmount(worldTimes.getTotal()));
WorldPie worldPie = new WorldPie(worldTimes);
addValue("worldSeries", worldPie.toHighChartsSeries());
addValue("gmSeries", worldPie.toHighChartsDrilldown());
addValue("lastPeakTime", lastPeak != -1 ? FormatUtils.formatTimeStampYear(lastPeak) : "No Data");
addValue("playersLastPeak", lastPeak != -1 ? profile.getLastPeakPlayers() : "-");
addValue("bestPeakTime", allTimePeak != -1 ? FormatUtils.formatTimeStampYear(allTimePeak) : "No Data");
addValue("playersBestPeak", allTimePeak != -1 ? profile.getAllTimePeakPlayers() : "-");
}
private void performanceTab(List<TPS> tpsData, List<TPS> tpsDataDay, List<TPS> tpsDataWeek, List<TPS> tpsDataMonth) {
got("tpsSpikeMonth", ServerProfile.getLowSpikeCount(tpsDataMonth));
got("tpsSpikeWeek", ServerProfile.getLowSpikeCount(tpsDataWeek));
got("tpsSpikeDay", ServerProfile.getLowSpikeCount(tpsDataDay));
addValue("tpsSpikeMonth", value("tpsSpikeMonth"));
addValue("tpsSpikeWeek", value("tpsSpikeWeek"));
addValue("tpsSpikeDay", value("tpsSpikeDay"));
addValue("playersOnlineSeries", new OnlineActivityGraph(tpsData).toHighChartsSeries());
addValue("tpsSeries", new TPSGraph(tpsData).toHighChartsSeries());
addValue("cpuSeries", new CPUGraph(tpsData).toHighChartsSeries());
addValue("ramSeries", new RamGraph(tpsData).toHighChartsSeries());
addValue("entitySeries", new EntityGraph(tpsData).toHighChartsSeries());
addValue("chunkSeries", new ChunkGraph(tpsData).toHighChartsSeries());
double averageCPUMonth = MathUtils.averageDouble(tpsDataMonth.stream().map(TPS::getCPUUsage).filter(i -> i != 0));
double averageCPUWeek = MathUtils.averageDouble(tpsDataWeek.stream().map(TPS::getCPUUsage).filter(i -> i != 0));
double averageCPUDay = MathUtils.averageDouble(tpsDataDay.stream().map(TPS::getCPUUsage).filter(i -> i != 0));
addValue("tpsAverageMonth", FormatUtils.cutDecimals(MathUtils.averageDouble(tpsDataMonth.stream().map(TPS::getTicksPerSecond))));
addValue("tpsAverageWeek", FormatUtils.cutDecimals(MathUtils.averageDouble(tpsDataWeek.stream().map(TPS::getTicksPerSecond))));
addValue("tpsAverageDay", FormatUtils.cutDecimals(MathUtils.averageDouble(tpsDataDay.stream().map(TPS::getTicksPerSecond))));
addValue("cpuAverageMonth", averageCPUMonth >= 0 ? FormatUtils.cutDecimals(averageCPUMonth) + "%" : "Unavailable");
addValue("cpuAverageWeek", averageCPUWeek >= 0 ? FormatUtils.cutDecimals(averageCPUWeek) + "%" : "Unavailable");
addValue("cpuAverageDay", averageCPUDay >= 0 ? FormatUtils.cutDecimals(averageCPUDay) + "%" : "Unavailable");
addValue("ramAverageMonth", FormatUtils.cutDecimals(MathUtils.averageLong(tpsDataMonth.stream().map(TPS::getUsedMemory).filter(i -> i != 0))));
addValue("ramAverageWeek", FormatUtils.cutDecimals(MathUtils.averageLong(tpsDataWeek.stream().map(TPS::getUsedMemory).filter(i -> i != 0))));
addValue("ramAverageDay", FormatUtils.cutDecimals(MathUtils.averageLong(tpsDataDay.stream().map(TPS::getUsedMemory).filter(i -> i != 0))));
addValue("entityAverageMonth", FormatUtils.cutDecimals(MathUtils.averageInt(tpsDataMonth.stream().map(TPS::getEntityCount).filter(i -> i != 0))));
addValue("entityAverageWeek", FormatUtils.cutDecimals(MathUtils.averageInt(tpsDataWeek.stream().map(TPS::getEntityCount).filter(i -> i != 0))));
addValue("entityAverageDay", FormatUtils.cutDecimals(MathUtils.averageInt(tpsDataDay.stream().map(TPS::getEntityCount).filter(i -> i != 0))));
addValue("chunkAverageMonth", FormatUtils.cutDecimals(MathUtils.averageInt(tpsDataMonth.stream().map(TPS::getChunksLoaded).filter(i -> i != 0))));
addValue("chunkAverageWeek", FormatUtils.cutDecimals(MathUtils.averageInt(tpsDataWeek.stream().map(TPS::getChunksLoaded).filter(i -> i != 0))));
addValue("chunkAverageDay", FormatUtils.cutDecimals(MathUtils.averageInt(tpsDataDay.stream().map(TPS::getChunksLoaded).filter(i -> i != 0))));
}
private long got(String key, long v) {
analyzedValues.put(key, v);
return v;
}
public long value(String key) {
return analyzedValues.getOrDefault(key, 0L);
}
public Set<StickyData> getStickyMonthData() {
return stickyMonthData;
}
public List<PlayerProfile> getPlayers() {
return players;
}
}

View File

@ -1,250 +0,0 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package com.djrapitops.plan.data.calculation;
import com.djrapitops.plan.data.PlayerProfile;
import com.djrapitops.plan.data.ServerProfile;
import com.djrapitops.plan.data.container.StickyData;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.analysis.MathUtils;
import com.djrapitops.plan.utilities.html.Html;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.*;
import java.util.stream.Collectors;
/**
* Class in charge of Server health analysis.
*
* @author Rsl1122
*/
public class HealthNotes {
private final List<String> notes;
private final AnalysisData analysisData;
private final SortedMap<Long, Map<String, Set<UUID>>> activityData;
private final List<TPS> tpsDataMonth;
private final long now;
private final long fourWeeksAgo;
private double serverHealth;
public HealthNotes(AnalysisData analysisData, SortedMap<Long, Map<String, Set<UUID>>> activityData, List<TPS> tpsDataMonth, long now) {
this.notes = new ArrayList<>();
serverHealth = 100.0;
this.analysisData = analysisData;
this.activityData = activityData;
this.tpsDataMonth = tpsDataMonth;
this.now = now;
this.fourWeeksAgo = now - TimeAmount.WEEK.ms() * 4L;
}
public void analyzeHealth() {
activityChangeNote();
newPlayerNote();
activePlayerPlaytimeChange();
lowPerformance();
}
public String parse() {
StringBuilder healthNoteBuilder = new StringBuilder();
for (String healthNote : notes) {
healthNoteBuilder.append(healthNote);
}
return healthNoteBuilder.toString();
}
public double getServerHealth() {
return serverHealth;
}
private void activityChangeNote() {
Map<String, Set<UUID>> activityNow = activityData.getOrDefault(now, new HashMap<>());
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
Map<String, Set<UUID>> activityFourWAgo = activityData.getOrDefault(fourWeeksAgo, new HashMap<>());
Set<UUID> veryActiveFWAG = activityFourWAgo.getOrDefault("Very Active", new HashSet<>());
Set<UUID> activeFWAG = activityFourWAgo.getOrDefault("Active", new HashSet<>());
Set<UUID> regularFWAG = activityFourWAgo.getOrDefault("Regular", new HashSet<>());
Set<UUID> regularRemainCompareSet = new HashSet<>(regularFWAG);
regularRemainCompareSet.addAll(activeFWAG);
regularRemainCompareSet.addAll(veryActiveFWAG);
int activeFWAGNum = regularRemainCompareSet.size();
regularRemainCompareSet.removeAll(regularNow);
regularRemainCompareSet.removeAll(activeNow);
regularRemainCompareSet.removeAll(veryActiveNow);
int notRegularAnymore = regularRemainCompareSet.size();
int remain = activeFWAGNum - notRegularAnymore;
double percRemain = remain * 100.0 / activeFWAGNum;
int newActive = getNewActive(veryActiveNow, activeNow, regularNow, veryActiveFWAG, activeFWAG, regularFWAG);
int change = newActive - notRegularAnymore;
String remainNote = "";
if (activeFWAGNum != 0) {
remainNote = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
if (percRemain > 50) {
remainNote += Html.GREEN_THUMB.parse();
} else if (percRemain > 20) {
remainNote += Html.YELLOW_FLAG.parse();
} else {
remainNote += Html.RED_WARN.parse();
serverHealth -= 2.5;
}
remainNote += " " + FormatUtils.cutDecimals(percRemain) + "% of regular players have remained active ("
+ remain + "/" + activeFWAGNum + ")";
}
if (change > 0) {
notes.add(
"<p>" + Html.GREEN_THUMB.parse() + " Number of regular players has increased (+" + change + ")<br>" +
remainNote + "</p>");
} else if (change == 0) {
notes.add(
"<p>" + Html.GREEN_THUMB.parse() + " Number of regular players has stayed the same (+" + change + ")<br>" +
remainNote + "</p>");
} else if (change > -20) {
notes.add(
"<p>" + Html.YELLOW_FLAG.parse() + " Number of regular players has decreased (" + change + ")<br>" +
remainNote + "</p>");
serverHealth -= 5;
} else {
notes.add(
"<p>" + Html.RED_WARN.parse() + " Number of regular players has decreased (" + change + ")<br>" +
remainNote + "</p>");
serverHealth -= 10;
}
}
private void newPlayerNote() {
double avgOnlineOnRegister = MathUtils.averageDouble(analysisData.getStickyMonthData().stream().map(StickyData::getOnlineOnJoin));
if (avgOnlineOnRegister >= 1) {
notes.add("<p>" + Html.GREEN_THUMB.parse() + " New Players have players to play with when they join ("
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
} else {
notes.add("<p>" + Html.YELLOW_FLAG.parse() + " New Players may not have players to play with when they join ("
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
serverHealth -= 5;
}
long newM = analysisData.value("newM");
long stuckPerM = analysisData.value("stuckPerM");
if (newM != 0) {
double stuckPerc = MathUtils.averageDouble(stuckPerM, newM) * 100;
if (stuckPerc >= 25) {
notes.add("<p>" + Html.GREEN_THUMB.parse() + " " + FormatUtils.cutDecimals(stuckPerc)
+ "% of new players have stuck around (" + stuckPerM + "/" + newM + ")</p>");
} else {
notes.add("<p>" + Html.YELLOW_FLAG.parse() + " " + FormatUtils.cutDecimals(stuckPerc)
+ "% of new players have stuck around (" + stuckPerM + "/" + newM + ")</p>");
}
}
}
private void activePlayerPlaytimeChange() {
List<PlayerProfile> currentActivePlayers = analysisData.getPlayers().stream()
.filter(player -> player.getActivityIndex(now).getValue() >= 1.75)
.collect(Collectors.toList());
long twoWeeksAgo = now - TimeAmount.WEEK.ms() * 2L;
long totalFourToTwoWeeks = 0;
long totalLastTwoWeeks = 0;
for (PlayerProfile activePlayer : currentActivePlayers) {
totalFourToTwoWeeks += activePlayer.getPlaytime(analysisData.value("monthAgo"), twoWeeksAgo);
totalLastTwoWeeks += activePlayer.getPlaytime(twoWeeksAgo, now);
}
int currentlyActive = currentActivePlayers.size();
if (currentlyActive != 0) {
long avgFourToTwoWeeks = MathUtils.averageLong(totalFourToTwoWeeks, currentlyActive);
long avgLastTwoWeeks = MathUtils.averageLong(totalLastTwoWeeks, currentlyActive);
String avgLastTwoWeeksString = FormatUtils.formatTimeAmount(avgLastTwoWeeks);
String avgFourToTwoWeeksString = FormatUtils.formatTimeAmount(avgFourToTwoWeeks);
if (avgFourToTwoWeeks >= avgLastTwoWeeks) {
notes.add("<p>" + Html.GREEN_THUMB.parse() + " Active players seem to have things to do (Played "
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
+ ", last two weeks vs weeks 2-4)</p>");
} else if (avgFourToTwoWeeks - avgLastTwoWeeks > TimeAmount.HOUR.ms() * 2L) {
notes.add("<p>" + Html.RED_WARN.parse() + " Active players might be running out of things to do (Played "
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
+ ", last two weeks vs weeks 2-4)</p>");
serverHealth -= 5;
} else {
notes.add("<p>" + Html.YELLOW_FLAG.parse() + " Active players might be running out of things to do (Played "
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
+ ", last two weeks vs weeks 2-4)</p>");
}
}
}
private void lowPerformance() {
long serverDownTime = ServerProfile.serverDownTime(tpsDataMonth);
double aboveThreshold = ServerProfile.aboveLowThreshold(tpsDataMonth);
long tpsSpikeMonth = analysisData.value("tpsSpikeMonth");
String avgLowThresholdString = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
if (aboveThreshold >= 0.96) {
avgLowThresholdString += Html.GREEN_THUMB.parse();
} else if (aboveThreshold >= 0.9) {
avgLowThresholdString += Html.YELLOW_FLAG.parse();
serverHealth *= 0.9;
} else {
avgLowThresholdString += Html.RED_WARN.parse();
serverHealth *= 0.6;
}
avgLowThresholdString += " Average TPS was above Low Threshold "
+ FormatUtils.cutDecimals(aboveThreshold * 100.0) + "% of the time";
if (tpsSpikeMonth <= 5) {
notes.add("<p>" + Html.GREEN_THUMB.parse()
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
" " + tpsSpikeMonth + " times<br>" +
avgLowThresholdString + "</p>");
} else if (tpsSpikeMonth <= 25) {
notes.add("<p>" + Html.YELLOW_FLAG.parse()
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
" " + tpsSpikeMonth + " times<br>" +
avgLowThresholdString + "</p>");
serverHealth *= 0.95;
} else {
notes.add("<p>" + Html.RED_WARN.parse()
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
" " + tpsSpikeMonth + " times<br>" +
avgLowThresholdString + "</p>");
serverHealth *= 0.8;
}
if (serverDownTime <= TimeAmount.DAY.ms()) {
notes.add("<p>" + Html.GREEN_THUMB.parse() + " Total Server downtime (No Data) was "
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
} else if (serverDownTime <= TimeAmount.WEEK.ms()) {
notes.add("<p>" + Html.YELLOW_FLAG.parse() + " Total Server downtime (No Data) was "
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
serverHealth *= (TimeAmount.WEEK.ms() - serverDownTime) * 1.0 / TimeAmount.WEEK.ms();
} else {
notes.add("<p>" + Html.RED_WARN.parse() + " Total Server downtime (No Data) was "
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
serverHealth *= (TimeAmount.MONTH.ms() - serverDownTime) * 1.0 / TimeAmount.MONTH.ms();
}
}
private int getNewActive(Set<UUID> veryActiveNow, Set<UUID> activeNow, Set<UUID> regularNow, Set<UUID> veryActiveFWAG, Set<UUID> activeFWAG, Set<UUID> regularFWAG) {
Set<UUID> regularNewCompareSet = new HashSet<>(regularNow);
regularNewCompareSet.addAll(activeNow);
regularNewCompareSet.addAll(veryActiveNow);
regularNewCompareSet.removeAll(regularFWAG);
regularNewCompareSet.removeAll(activeFWAG);
regularNewCompareSet.removeAll(veryActiveFWAG);
return regularNewCompareSet.size();
}
}

View File

@ -1,64 +0,0 @@
package com.djrapitops.plan.data.calculation;
import com.djrapitops.plugin.utilities.Verify;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* Extending objects should represent, add together and analyse data.
*
* @author Rsl1122
* @since 3.5.2
*/
public abstract class RawData {
private final Map<String, Serializable> replaceMap;
/**
* Only used by subclasses.
*/
public RawData() {
replaceMap = new HashMap<>();
}
/**
* Adds values from an existing replaceMap.
*
* @param values Map that contains place-holders.
*/
public void addValues(Map<String, Serializable> values) {
Verify.nullCheck(values);
replaceMap.putAll(values);
}
/**
* Adds a placeholder to the replaceMap.
*
* @param placeholder placeholder, without prefix and suffix
* @param value Any value the placeholder should be replaced with.
*/
public void addValue(String placeholder, Serializable value) {
replaceMap.put(placeholder, value);
}
/**
* Used to get the placeholders and values.
*
* @return Map containing the placeholders and values.
*/
public Map<String, Serializable> getReplaceMap() {
return replaceMap;
}
/**
* Used to get the value for a placeholder without the placeholder prefix and suffix.
*
* @param key placeholder name without ${ and }
* @return Value the placeholder should be replaced with or null.
*/
public Serializable get(String key) {
return replaceMap.get(key);
}
}

View File

@ -1,78 +0,0 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.Actions;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.html.Html;
import java.util.Objects;
/**
* Class that represents an action made by a player.
*
* @author Rsl1122
*/
public class Action {
private final long date;
private final Actions doneAction;
private final String additionalInfo;
private int serverID;
public Action(long date, Actions doneAction, String additionalInfo) {
this.date = date;
this.doneAction = doneAction;
this.additionalInfo = additionalInfo;
}
public Action(long date, Actions doneAction, String additionalInfo, int serverID) {
this.date = date;
this.doneAction = doneAction;
this.additionalInfo = additionalInfo;
this.serverID = serverID;
}
public long getDate() {
return date;
}
public Actions getDoneAction() {
return doneAction;
}
public String getAdditionalInfo() {
return additionalInfo;
}
/**
* Can only be used on Action classes returned by the ActionsTable.
*
* @return ID of the server the action occurred on.
*/
public int getServerID() {
return serverID;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Action action = (Action) o;
return date == action.date &&
serverID == action.serverID &&
doneAction == action.doneAction &&
Objects.equals(additionalInfo, action.additionalInfo);
}
@Override
public int hashCode() {
return Objects.hash(date, doneAction, additionalInfo, serverID);
}
@Override
public String toString() {
return Html.TABLELINE_3.parse(FormatUtils.formatTimeStampYear(date), doneAction.toString(), additionalInfo);
}
}

View File

@ -4,11 +4,14 @@
*/
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plan.data.store.objects.DateMap;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.SHA256Hash;
import com.google.common.base.Objects;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.security.NoSuchAlgorithmException;
/**
@ -16,25 +19,33 @@ import java.security.NoSuchAlgorithmException;
*
* @author Rsl1122
*/
public class GeoInfo {
public class GeoInfo implements DateHolder {
private final String ip;
private final String geolocation;
private final String ipHash;
private final long lastUsed;
private final long date;
public GeoInfo(String ip, String geolocation, long lastUsed)
public GeoInfo(InetAddress address, String geolocation, long lastUsed)
throws UnsupportedEncodingException, NoSuchAlgorithmException {
this(FormatUtils.formatIP(ip), geolocation, lastUsed, new SHA256Hash(ip).create());
this(FormatUtils.formatIP(address), geolocation, lastUsed, new SHA256Hash(address.getHostAddress()).create());
}
public GeoInfo(String ip, String geolocation, long lastUsed, String ipHash) {
public GeoInfo(String ip, String geolocation, long date, String ipHash) {
this.ip = ip;
this.geolocation = geolocation;
this.lastUsed = lastUsed;
this.date = date;
this.ipHash = ipHash;
}
public static DateMap<GeoInfo> intoDateMap(Iterable<GeoInfo> geoInfo) {
DateMap<GeoInfo> map = new DateMap<>();
for (GeoInfo info : geoInfo) {
map.put(info.date, info);
}
return map;
}
public String getIp() {
return ip;
}
@ -43,8 +54,9 @@ public class GeoInfo {
return geolocation;
}
public long getLastUsed() {
return lastUsed;
@Override
public long getDate() {
return date;
}
public String getIpHash() {
@ -57,11 +69,22 @@ public class GeoInfo {
if (o == null || getClass() != o.getClass()) return false;
GeoInfo geoInfo = (GeoInfo) o;
return Objects.equal(ip, geoInfo.ip) &&
Objects.equal(geolocation, geoInfo.geolocation);
Objects.equal(geolocation, geoInfo.geolocation) &&
Objects.equal(ipHash, geoInfo.ipHash);
}
@Override
public int hashCode() {
return Objects.hashCode(ip, geolocation);
return Objects.hashCode(ip, geolocation, ipHash);
}
}
@Override
public String toString() {
return "GeoInfo{" +
"ip='" + ip + '\'' +
", geolocation='" + geolocation + '\'' +
", ipHash='" + ipHash + '\'' +
", date=" + date +
'}';
}
}

View File

@ -0,0 +1,37 @@
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.objects.DateObj;
import java.util.UUID;
public class Ping extends DateObj<Double> {
private final UUID serverUUID;
private final double average;
private final int min;
private final int max;
public Ping(long date, UUID serverUUID, int min, int max, double average) {
super(date, average);
this.serverUUID = serverUUID;
this.average = average;
this.min = min;
this.max = max;
}
public UUID getServerUUID() {
return serverUUID;
}
public double getAverage() {
return average;
}
public int getMin() {
return min;
}
public int getMax() {
return max;
}
}

View File

@ -0,0 +1,31 @@
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.objects.DateHolder;
import java.util.UUID;
public class PlayerDeath implements DateHolder {
private final UUID killer;
private final long date;
private final String weapon;
public PlayerDeath(UUID killer, String weapon, long date) {
this.killer = killer;
this.date = date;
this.weapon = weapon;
}
public UUID getKiller() {
return killer;
}
@Override
public long getDate() {
return date;
}
public String getWeapon() {
return weapon;
}
}

View File

@ -1,7 +1,6 @@
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.Actions;
import com.djrapitops.plan.system.cache.DataCache;
import com.djrapitops.plan.data.store.objects.DateHolder;
import java.util.Objects;
import java.util.UUID;
@ -12,10 +11,10 @@ import java.util.UUID;
*
* @author Rsl1122
*/
public class PlayerKill {
public class PlayerKill implements DateHolder {
private final UUID victim;
private final long time;
private final long date;
private final String weapon;
/**
@ -23,12 +22,12 @@ public class PlayerKill {
*
* @param victim UUID of the victim.
* @param weapon Weapon used.
* @param time Epoch millisecond at which the kill occurred.
* @param date Epoch millisecond at which the kill occurred.
*/
public PlayerKill(UUID victim, String weapon, long time) {
public PlayerKill(UUID victim, String weapon, long date) {
this.victim = victim;
this.weapon = weapon;
this.time = time;
this.date = date;
}
/**
@ -40,13 +39,9 @@ public class PlayerKill {
return victim;
}
/**
* Get the Epoch millisecond the kill occurred.
*
* @return long in ms.
*/
public long getTime() {
return time;
@Override
public long getDate() {
return date;
}
/**
@ -58,31 +53,26 @@ public class PlayerKill {
return weapon;
}
public Action convertToAction() {
String name = DataCache.getInstance().getName(victim);
return new Action(time, Actions.KILLED, name + " with " + weapon);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PlayerKill that = (PlayerKill) o;
return time == that.time &&
return date == that.date &&
Objects.equals(victim, that.victim) &&
Objects.equals(weapon, that.weapon);
}
@Override
public int hashCode() {
return Objects.hash(victim, time, weapon);
return Objects.hash(victim, date, weapon);
}
@Override
public String toString() {
return "PlayerKill{" +
"victim=" + victim + ", " +
"time=" + time + ", " +
"date=" + date + ", " +
"weapon='" + weapon + "'}";
}
}

View File

@ -1,77 +1,102 @@
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.info.server.ServerInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.*;
/**
* Object for storing various information about a player's play session.
* <p>
* Includes:
* <ul>
* <li>World and GameMode playtimes</li>
* <li>Player and Mob kills</li>
* <li>Deaths</li>
* </ul>
* <p>
* Following data can be derived from Sessions in the database (Between any time span):
* <ul>
* <li>Playtime</li>
* <li>LoginTimes</li>
* </ul>
* DataContainer for information about a player's play session.
*
* @author Rsl1122
* @see SessionKeys for Key objects.
*/
public class Session {
public class Session extends DataContainer implements DateHolder {
private final long sessionStart;
private Integer sessionID;
private WorldTimes worldTimes;
private long sessionEnd;
private List<PlayerKill> playerKills;
private int mobKills;
private int deaths;
private long afkTime;
/**
* Creates a new session with given start and end of -1.
* Creates a new session.
*
* @param sessionStart Epoch millisecond the session was started.
* @param uuid UUID of the Player.
* @param sessionStart Epoch ms the session started.
* @param world Starting world.
* @param gm Starting GameMode.
*/
public Session(long sessionStart, String world, String gm) {
this.worldTimes = new WorldTimes(world, gm);
this.sessionStart = sessionStart;
this.sessionEnd = -1;
playerKills = new ArrayList<>();
public Session(UUID uuid, long sessionStart, String world, String gm) {
mobKills = 0;
deaths = 0;
afkTime = 0;
putRawData(SessionKeys.UUID, uuid);
putRawData(SessionKeys.START, sessionStart);
putRawData(SessionKeys.WORLD_TIMES, new WorldTimes(world, gm));
putRawData(SessionKeys.PLAYER_KILLS, new ArrayList<>());
putRawData(SessionKeys.PLAYER_DEATHS, new ArrayList<>());
putSupplier(SessionKeys.MOB_KILL_COUNT, () -> mobKills);
putSupplier(SessionKeys.DEATH_COUNT, () -> deaths);
putSupplier(SessionKeys.AFK_TIME, () -> afkTime);
putSupplier(SessionKeys.PLAYER_KILL_COUNT, getUnsafe(SessionKeys.PLAYER_KILLS)::size);
putSupplier(SessionKeys.LENGTH, () ->
getValue(SessionKeys.END).orElse(System.currentTimeMillis()) - getUnsafe(SessionKeys.START));
putSupplier(SessionKeys.ACTIVE_TIME, () -> getUnsafe(SessionKeys.LENGTH) - getUnsafe(SessionKeys.AFK_TIME));
putSupplier(SessionKeys.SERVER_UUID, ServerInfo::getServerUUID);
}
public Session(int id, long sessionStart, long sessionEnd, int mobKills, int deaths, long afkTime) {
this.sessionID = id;
this.sessionStart = sessionStart;
this.sessionEnd = sessionEnd;
this.worldTimes = new WorldTimes(new HashMap<>());
this.playerKills = new ArrayList<>();
/**
* Recreates a Session found in the database.
* <p>
* WorldTimes and Player kills need to be set separately.
*
* @param id ID in the database (Used for fetching world times and player kills.
* @param uuid UUID of the Player.
* @param serverUUID UUID of the Server.
* @param sessionStart Epoch ms the session started.
* @param sessionEnd Epoch ms the session ended.
* @param mobKills Mobs killed during the session.
* @param deaths Death count during the session.
* @param afkTime Time spent AFK during the session.
*/
public Session(int id, UUID uuid, UUID serverUUID, long sessionStart, long sessionEnd, int mobKills, int deaths, long afkTime) {
putRawData(SessionKeys.DB_ID, id);
putRawData(SessionKeys.UUID, uuid);
putRawData(SessionKeys.SERVER_UUID, serverUUID);
putRawData(SessionKeys.START, sessionStart);
putRawData(SessionKeys.END, sessionEnd);
putRawData(SessionKeys.WORLD_TIMES, new WorldTimes(new HashMap<>()));
putRawData(SessionKeys.PLAYER_KILLS, new ArrayList<>());
putRawData(SessionKeys.PLAYER_DEATHS, new ArrayList<>());
putSupplier(SessionKeys.MOB_KILL_COUNT, () -> mobKills);
putSupplier(SessionKeys.DEATH_COUNT, () -> deaths);
putSupplier(SessionKeys.AFK_TIME, () -> afkTime);
this.mobKills = mobKills;
this.deaths = deaths;
this.afkTime = afkTime;
putSupplier(SessionKeys.PLAYER_KILL_COUNT, () -> getUnsafe(SessionKeys.PLAYER_KILLS).size());
putSupplier(SessionKeys.LENGTH, () ->
getValue(SessionKeys.END).orElse(System.currentTimeMillis()) - getUnsafe(SessionKeys.START));
putSupplier(SessionKeys.ACTIVE_TIME, () -> getUnsafe(SessionKeys.LENGTH) - getUnsafe(SessionKeys.AFK_TIME));
}
/**
* Ends the session with given end point.
* <p>
* (Changes the end to the parameter.).
* Updates world times to the latest value.
*
* @param endOfSession Epoch millisecond the session ended.
*/
public void endSession(long endOfSession) {
sessionEnd = endOfSession;
putRawData(SessionKeys.END, endOfSession);
WorldTimes worldTimes = getValue(SessionKeys.WORLD_TIMES)
.orElseThrow(() -> new IllegalStateException("World times have not been defined"));
worldTimes.updateState(endOfSession);
}
@ -83,10 +108,14 @@ public class Session {
* @param time Epoch ms of the event.
*/
public void changeState(String world, String gm, long time) {
WorldTimes worldTimes = getValue(SessionKeys.WORLD_TIMES)
.orElseThrow(() -> new IllegalStateException("World times is not defined"));
worldTimes.updateState(world, gm, time);
}
public void playerKilled(PlayerKill kill) {
List<PlayerKill> playerKills = getValue(SessionKeys.PLAYER_KILLS)
.orElseThrow(() -> new IllegalStateException("Player kills is not defined."));
playerKills.add(kill);
}
@ -104,82 +133,32 @@ public class Session {
* @return Long in ms.
*/
public long getLength() {
if (sessionEnd == -1) {
return System.currentTimeMillis() - sessionStart;
}
return sessionEnd - sessionStart;
return getValue(SessionKeys.LENGTH).orElse(0L);
}
/**
* Get the start of the session.
*
* @return Epoch millisecond the session started.
*/
public long getSessionStart() {
return sessionStart;
}
/**
* Get the end of the session.
*
* @return Epoch millisecond the session ended.
*/
public long getSessionEnd() {
return sessionEnd;
}
public WorldTimes getWorldTimes() {
return worldTimes;
@Override
public long getDate() {
return getUnsafe(SessionKeys.START);
}
public void setWorldTimes(WorldTimes worldTimes) {
this.worldTimes = worldTimes;
}
public List<PlayerKill> getPlayerKills() {
return playerKills;
putRawData(SessionKeys.WORLD_TIMES, worldTimes);
}
public void setPlayerKills(List<PlayerKill> playerKills) {
this.playerKills = playerKills;
}
public int getMobKills() {
return mobKills;
}
public int getDeaths() {
return deaths;
putRawData(SessionKeys.PLAYER_KILLS, playerKills);
}
public boolean isFetchedFromDB() {
return sessionID != null;
return supports(SessionKeys.DB_ID);
}
public void addAFKTime(long timeAFK) {
afkTime += timeAFK;
}
public long getAfkLength() {
return afkTime;
}
public long getActiveLength() {
return getLength() - getAfkLength();
}
/**
* Used to get the ID of the session in the Database.
*
* @return ID if present.
* @throws NullPointerException if Session was not fetched from DB. Check {@code isFetchedFromDB} first.
*/
public int getSessionID() {
return sessionID != null ? sessionID : -1;
}
public void setSessionID(int sessionID) {
this.sessionID = sessionID;
putRawData(SessionKeys.DB_ID, sessionID);
}
@Override
@ -187,28 +166,17 @@ public class Session {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Session session = (Session) o;
return sessionStart == session.sessionStart &&
sessionEnd == session.sessionEnd &&
return getUnsafe(SessionKeys.START).equals(session.getUnsafe(SessionKeys.START)) &&
getValue(SessionKeys.END).orElse(-1L).equals(session.getValue(SessionKeys.END).orElse(-1L)) &&
mobKills == session.mobKills &&
deaths == session.deaths &&
Objects.equals(worldTimes, session.worldTimes) &&
Objects.equals(playerKills, session.playerKills);
}
@Override
public int hashCode() {
return Objects.hash(sessionStart, sessionID, worldTimes, sessionEnd, playerKills, mobKills, deaths);
}
@Override
public String toString() {
return "Session{" +
"sessionStart=" + sessionStart + ", " +
"sessionID=" + sessionID + ", " +
"worldTimes=" + worldTimes + ", " +
"sessionEnd=" + sessionEnd + ", " +
"playerKills=" + playerKills + ", " +
"mobKills=" + mobKills + ", " +
"deaths=" + deaths + '}';
Objects.equals(
getValue(SessionKeys.WORLD_TIMES).orElse(null),
session.getValue(SessionKeys.WORLD_TIMES).orElse(null)
) &&
Objects.equals(
getValue(SessionKeys.PLAYER_KILLS).orElse(new ArrayList<>()),
session.getValue(SessionKeys.PLAYER_KILLS).orElse(new ArrayList<>())
);
}
}

View File

@ -1,108 +0,0 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.Actions;
import com.djrapitops.plan.data.PlayerProfile;
import com.djrapitops.plugin.api.TimeAmount;
import com.google.common.base.Objects;
import java.util.List;
public class StickyData {
private final double activityIndex;
private Double messagesSent;
private Double onlineOnJoin;
public StickyData(PlayerProfile player) {
activityIndex = player.getActivityIndex(player.getRegistered() + TimeAmount.DAY.ms()).getValue();
loadActionVariables(player.getActions());
}
public StickyData(double activityIndex, Double messagesSent, Double onlineOnJoin) {
this.activityIndex = activityIndex;
this.messagesSent = messagesSent;
this.onlineOnJoin = onlineOnJoin;
}
private void loadActionVariables(List<Action> actions) {
for (Action action : actions) {
try {
if (messagesSent == null && action.getDoneAction() == Actions.FIRST_LOGOUT) {
messagesSent = (double) loadSentMessages(action);
}
if (onlineOnJoin == null && action.getDoneAction() == Actions.FIRST_SESSION) {
onlineOnJoin = (double) loadOnlineOnJoin(action);
}
} catch (IllegalArgumentException ignore) {
/* continue */
}
}
setDefaultValuesIfNull();
}
private void setDefaultValuesIfNull() {
if (messagesSent == null) {
messagesSent = 0.0;
}
if (onlineOnJoin == null) {
onlineOnJoin = 0.0;
}
}
private int loadOnlineOnJoin(Action action) {
String additionalInfo = action.getAdditionalInfo();
String[] split = additionalInfo.split(" ");
if (split.length == 3) {
return Integer.parseInt(split[1]);
}
throw new IllegalArgumentException("Improper Action");
}
private int loadSentMessages(Action action) {
String additionalInfo = action.getAdditionalInfo();
String[] split = additionalInfo.split(": ");
if (split.length == 2) {
return Integer.parseInt(split[1]);
}
throw new IllegalArgumentException("Improper Action");
}
public double distance(StickyData data) {
double num = 0;
num += Math.abs(data.activityIndex - activityIndex) * 2.0;
num += Math.abs(data.onlineOnJoin - onlineOnJoin) / 10.0;
num += Math.abs(data.messagesSent - messagesSent) / 10.0;
return num;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StickyData that = (StickyData) o;
return Double.compare(that.activityIndex, activityIndex) == 0 &&
Objects.equal(messagesSent, that.messagesSent) &&
Objects.equal(onlineOnJoin, that.onlineOnJoin);
}
@Override
public int hashCode() {
return Objects.hashCode(activityIndex, messagesSent, onlineOnJoin);
}
public double getOnlineOnJoin() {
return onlineOnJoin;
}
public double getActivityIndex() {
return activityIndex;
}
public Double getMessagesSent() {
return messagesSent;
}
}

View File

@ -5,6 +5,8 @@
*/
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.objects.DateHolder;
import java.util.Objects;
/**
@ -13,7 +15,7 @@ import java.util.Objects;
* @author Rsl1122
* @since 3.5.0
*/
public class TPS {
public class TPS implements DateHolder {
private final long date;
private final double ticksPerSecond;
@ -44,11 +46,7 @@ public class TPS {
this.chunksLoaded = chunksLoaded;
}
/**
* Get the time of the average calculation.
*
* @return epoch ms.
*/
@Override
public long getDate() {
return date;
}

View File

@ -54,7 +54,7 @@ public class UserInfo {
return banned;
}
public boolean isOpped() {
public boolean isOperator() {
return opped;
}

View File

@ -10,7 +10,7 @@ import java.util.TreeMap;
import java.util.UUID;
/**
* Container used to parse data for Analysis page.
* Container used to parse data for Server page.
* <p>
* Similar to InspectContainer, but can contain data for each player for a bigger Player data table.
* <p>

View File

@ -4,8 +4,10 @@
*/
package com.djrapitops.plan.data.element;
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.html.Html;
import com.djrapitops.plan.utilities.html.icon.Icon;
import java.io.Serializable;
import java.util.ArrayList;
@ -19,6 +21,7 @@ import java.util.List;
public class TableContainer {
protected final String[] header;
protected final Formatter[] formatters;
private List<Serializable[]> values;
private boolean jqueryDatatable;
@ -32,11 +35,16 @@ public class TableContainer {
*/
public TableContainer(String... header) {
this.header = header;
this.formatters = new Formatter[this.header.length];
values = new ArrayList<>();
}
public TableContainer(boolean players, String... header) {
this.header = FormatUtils.mergeArrays(new String[]{Html.FONT_AWESOME_ICON.parse("user") + " Player"}, header);
this.header = FormatUtils.mergeArrays(
new String[]{Icon.called("user").build() + " Player"},
header
);
this.formatters = new Formatter[this.header.length];
values = new ArrayList<>();
}
@ -44,11 +52,11 @@ public class TableContainer {
this.values.add(values);
}
public final String parseHtml() {
public String parseHtml() {
return getTableHeader() +
parseHeader() +
parseBody() +
"</table>";
"</table>" + (jqueryDatatable ? "</div>" : "");
}
public final String parseBody() {
@ -58,21 +66,30 @@ public class TableContainer {
addRow("No Data");
}
for (Serializable[] row : values) {
int maxIndex = row.length - 1;
body.append("<tr>");
for (int i = 0; i < header.length; i++) {
body.append("<td>");
if (i > maxIndex) {
body.append("-");
} else {
body.append(row[i]);
try {
if (i > maxIndex) {
body.append("<td>-");
} else {
Serializable value = row[i];
Formatter formatter = formatters[i];
body.append("<td").append(formatter != null ? " data-order=\"" + value + "\">" : ">");
body.append(formatter != null ? formatter.apply(value) : value);
}
body.append("</td>");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("Invalid formatter given at index " + i + ": " + e.getMessage(), e);
}
body.append("</td>");
}
body.append("</tr>");
}
return Html.TABLE_BODY.parse(body.toString());
}
public final void setColor(String color) {
@ -88,6 +105,12 @@ public class TableContainer {
return header.toString();
}
public final void setFormatter(int index, Formatter formatter) {
if (index < formatters.length) {
formatters[index] = formatter;
}
}
/**
* Make use of JQuery Datatables plugin.
* <p>
@ -99,7 +122,7 @@ public class TableContainer {
private String getTableHeader() {
if (jqueryDatatable) {
return "<div class=\"table-responsive\">" + Html.TABLE_JQUERY.parse() + "</div>";
return "<div class=\"table-responsive\">" + Html.TABLE_JQUERY.parse();
} else {
return Html.TABLE_SCROLL.parse();
}

View File

@ -11,7 +11,10 @@ import java.util.UUID;
* Interface for PluginData objects that affect Ban state of players.
*
* @author Rsl1122
* @deprecated New implementations should not be created as better plugin data integration is
* being created.
*/
@Deprecated
public interface BanData {
boolean isBanned(UUID uuid);

View File

@ -10,6 +10,7 @@ import com.djrapitops.plugin.utilities.Verify;
import com.djrapitops.pluginbridge.plan.Bridge;
import java.util.*;
import java.util.stream.Collectors;
/**
* Class responsible for hooking to other plugins and managing the %plugins%
@ -87,6 +88,13 @@ public class HookHandler implements SubSystem {
return additionalDataSources;
}
public List<BanData> getBanDataSources() {
return additionalDataSources.stream()
.filter(p -> p instanceof BanData)
.map(p -> (BanData) p)
.collect(Collectors.toList());
}
public Map<PluginData, InspectContainer> getInspectContainersFor(UUID uuid) {
List<PluginData> plugins = getAdditionalDataSources();
Map<PluginData, InspectContainer> containers = new HashMap<>();

View File

@ -3,6 +3,8 @@ package com.djrapitops.plan.data.plugin;
import com.djrapitops.plan.data.element.AnalysisContainer;
import com.djrapitops.plan.data.element.InspectContainer;
import com.djrapitops.plan.utilities.html.Html;
import com.djrapitops.plan.utilities.html.icon.Color;
import com.djrapitops.plan.utilities.html.icon.Icon;
import com.google.common.base.Objects;
import java.util.Collection;
@ -23,11 +25,13 @@ public abstract class PluginData {
private final ContainerSize size;
private final String sourcePlugin;
private String pluginIcon;
private Icon pluginIcon;
private String iconColor;
private String helpText;
protected com.djrapitops.plan.data.store.containers.AnalysisContainer analysisData;
public PluginData(ContainerSize size, String sourcePlugin) {
this.size = size;
this.sourcePlugin = sourcePlugin;
@ -37,12 +41,24 @@ public abstract class PluginData {
public abstract AnalysisContainer getServerData(Collection<UUID> uuids, AnalysisContainer fillThis) throws Exception;
protected final void setPluginIcon(String pluginIcon) {
protected final void setPluginIcon(Icon pluginIcon) {
this.pluginIcon = pluginIcon;
}
/**
* @deprecated Use {@code setPluginIcon(Icon)} instead
*/
@Deprecated
protected final void setPluginIcon(String pluginIcon) {
this.pluginIcon = Icon.called(pluginIcon != null ? pluginIcon : "cube").build();
}
/**
* @deprecated Use {@code setPluginIcon(Icon)} instead
*/
@Deprecated
protected final void setIconColor(String iconColor) {
this.iconColor = iconColor;
pluginIcon.setColor(Color.matchString(iconColor));
}
public final String getHelpText() {
@ -50,7 +66,7 @@ public abstract class PluginData {
}
public final String parsePluginIcon() {
return pluginIcon != null ? Html.FA_COLORED_ICON.parse((iconColor != null ? iconColor : "black"), pluginIcon) : Html.FONT_AWESOME_ICON.parse("cube");
return (pluginIcon != null ? pluginIcon : Icon.called("cube").build()).toHtml();
}
public final ContainerSize getSize() {
@ -80,11 +96,31 @@ public abstract class PluginData {
return Objects.hashCode(size, sourcePlugin, pluginIcon);
}
/**
* @deprecated Use {@code getWithIcon(String, Icon)} instead
*/
@Deprecated
public final String getWithIcon(String text, String icon) {
return getWithIcon(text, icon, "");
return getWithIcon(text, Icon.called(icon).build());
}
/**
* @deprecated Use {@code getWithIcon(String, Icon)} instead
*/
@Deprecated
public final String getWithIcon(String text, String icon, String color) {
return Html.FA_COLORED_ICON.parse(color, icon) + " " + text;
return getWithIcon(text, Icon.called(icon).of(Color.matchString(color)).build());
}
public final String getWithIcon(String text, Icon.Builder builder) {
return getWithIcon(text, builder.build());
}
public final String getWithIcon(String text, Icon icon) {
return icon.toHtml() + " " + text;
}
public final void setAnalysisData(com.djrapitops.plan.data.store.containers.AnalysisContainer analysisData) {
this.analysisData = analysisData;
}
}

View File

@ -0,0 +1,33 @@
package com.djrapitops.plan.data.store;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.function.Supplier;
/**
* Caching layer between Supplier and caller.
*
* Refreshes the value if 30 seconds have passed since the last call.
*
* @author Rsl1122
*/
public class CachingSupplier<T> implements Supplier<T> {
private final Supplier<T> original;
private T cachedValue;
private long cacheTime;
public CachingSupplier(Supplier<T> original) {
this.original = original;
cacheTime = 0L;
}
@Override
public T get() {
if (cachedValue == null || System.currentTimeMillis() - cacheTime > TimeAmount.SECOND.ms() * 30L) {
cachedValue = original.get();
cacheTime = System.currentTimeMillis();
}
return cachedValue;
}
}

View File

@ -0,0 +1,68 @@
package com.djrapitops.plan.data.store;
import java.util.Objects;
/**
* Identifier used for storing and fetching data from DataContainers.
*
* @param <T> Type of the object returned by the Value identified by this Key.
* @author Rsl1122
*/
public class Key<T> {
private final Type<T> type;
private final String keyName;
/**
* Create a new key.
* <p>
* Example usage:
* {@code Key<String> key = new Key(String.class, "identifier");}
* <p>
* (In Keys class) {@code public static final Key<String> IDENTIFIER = new Key(String.class, "identifier");}
* {@code Key<String> key = Keys.IDENTIFIER;}
*
* @param type Class with type of the Object returned by the Value identified by this Key.
* @param keyName Name (identifier) of the Key.
*/
public Key(Class<T> type, String keyName) {
this(Type.ofClass(type), keyName);
}
public Key(Type<T> type, String keyName) {
this.type = type;
this.keyName = keyName;
}
/**
* Get the type of the key.
*
* @return specified in constructor.
*/
public Type<T> getType() {
return type;
}
/**
* Get the name (identifier) of the Key.
*
* @return For example "nickname"
*/
public String getKeyName() {
return keyName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key<?> key = (Key<?>) o;
return Objects.equals(type, key.type) &&
Objects.equals(keyName, key.keyName);
}
@Override
public int hashCode() {
return Objects.hash(type, keyName);
}
}

View File

@ -0,0 +1,25 @@
package com.djrapitops.plan.data.store;
/**
* Special Key object that can be used for placeholders when replacing values in html files.
*
* @author Rsl1122
*/
public class PlaceholderKey<T> extends Key<T> {
private final String placeholder;
public PlaceholderKey(Class<T> type, String placeholder) {
super(type, placeholder);
this.placeholder = placeholder;
}
public PlaceholderKey(Type<T> type, String placeholder) {
super(type, placeholder);
this.placeholder = placeholder;
}
public String getPlaceholder() {
return placeholder;
}
}

View File

@ -0,0 +1,45 @@
package com.djrapitops.plan.data.store;
import java.util.Objects;
/**
* Similar to Google's TypeToken but without requiring whole gson package.
* <p>
* Create new instance with {@code new Type<YourObject>() {}}.
*
* @author Rsl1122
*/
public abstract class Type<T> {
private final String genericsSuperClass;
public Type() {
genericsSuperClass = getGenericsClass().getGenericSuperclass().getTypeName();
}
public static <K> Type<K> ofClass(Class<K> of) {
return new Type<K>() {};
}
public static <K> Type<K> of(K object) {
return new Type<K>() {};
}
public Class<Type<T>> getGenericsClass() {
return (Class<Type<T>>) getClass();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Type)) return false;
Type<?> type = (Type<?>) o;
return Objects.equals(genericsSuperClass, type.genericsSuperClass);
}
@Override
public int hashCode() {
return Objects.hash(genericsSuperClass);
}
}

View File

@ -0,0 +1,439 @@
package com.djrapitops.plan.data.store.containers;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.Type;
import com.djrapitops.plan.data.store.keys.AnalysisKeys;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.data.store.keys.ServerKeys;
import com.djrapitops.plan.data.store.mutators.*;
import com.djrapitops.plan.data.store.mutators.combiners.MultiBanCombiner;
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
import com.djrapitops.plan.data.store.mutators.health.HealthInformation;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.info.server.ServerProperties;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.theme.Theme;
import com.djrapitops.plan.system.settings.theme.ThemeVal;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plan.utilities.analysis.ServerBanDataReader;
import com.djrapitops.plan.utilities.html.graphs.ActivityStackGraph;
import com.djrapitops.plan.utilities.html.graphs.PunchCardGraph;
import com.djrapitops.plan.utilities.html.graphs.WorldMap;
import com.djrapitops.plan.utilities.html.graphs.bar.GeolocationBarGraph;
import com.djrapitops.plan.utilities.html.graphs.calendar.ServerCalendar;
import com.djrapitops.plan.utilities.html.graphs.line.*;
import com.djrapitops.plan.utilities.html.graphs.pie.ActivityPie;
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
import com.djrapitops.plan.utilities.html.structure.AnalysisPluginsTabContentCreator;
import com.djrapitops.plan.utilities.html.structure.RecentLoginList;
import com.djrapitops.plan.utilities.html.structure.SessionAccordion;
import com.djrapitops.plan.utilities.html.tables.CommandUseTable;
import com.djrapitops.plan.utilities.html.tables.PingTable;
import com.djrapitops.plan.utilities.html.tables.PlayersTable;
import com.djrapitops.plan.utilities.html.tables.ServerSessionTable;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.*;
import java.util.stream.Collectors;
/**
* Container used for analysis.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.keys.AnalysisKeys for Key objects
* @see com.djrapitops.plan.data.store.PlaceholderKey for placeholder information
*/
public class AnalysisContainer extends DataContainer {
private final ServerContainer serverContainer;
private static final Key<Map<UUID, String>> serverNames = new Key<>(new Type<Map<UUID, String>>() {}, "SERVER_NAMES");
public AnalysisContainer(ServerContainer serverContainer) {
this.serverContainer = serverContainer;
addAnalysisSuppliers();
}
public ServerContainer getServerContainer() {
return serverContainer;
}
private void addAnalysisSuppliers() {
putSupplier(AnalysisKeys.SESSIONS_MUTATOR, () -> SessionsMutator.forContainer(serverContainer));
putSupplier(AnalysisKeys.TPS_MUTATOR, () -> TPSMutator.forContainer(serverContainer));
putSupplier(AnalysisKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(serverContainer));
addConstants();
addPlayerSuppliers();
addSessionSuppliers();
addGraphSuppliers();
addTPSAverageSuppliers();
addCommandSuppliers();
addServerHealth();
addPluginSuppliers();
runCombiners();
}
private void runCombiners() {
new MultiBanCombiner(this.serverContainer).combine(getUnsafe(AnalysisKeys.BAN_DATA));
}
private void addConstants() {
long now = System.currentTimeMillis();
putRawData(AnalysisKeys.ANALYSIS_TIME, now);
putRawData(AnalysisKeys.ANALYSIS_TIME_DAY_AGO, now - TimeAmount.DAY.ms());
putRawData(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO, now - TimeAmount.WEEK.ms());
putRawData(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO, now - TimeAmount.MONTH.ms());
putSupplier(AnalysisKeys.REFRESH_TIME_F, () -> Formatters.second().apply(() -> getUnsafe(AnalysisKeys.ANALYSIS_TIME)));
putRawData(AnalysisKeys.VERSION, PlanPlugin.getInstance().getVersion());
putSupplier(AnalysisKeys.TIME_ZONE, MiscUtils::getTimeZoneOffsetHours);
putRawData(AnalysisKeys.FIRST_DAY, 1);
putRawData(AnalysisKeys.TPS_MEDIUM, Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber());
putRawData(AnalysisKeys.TPS_HIGH, Settings.THEME_GRAPH_TPS_THRESHOLD_HIGH.getNumber());
addServerProperties();
addThemeColors();
}
private void addServerProperties() {
putSupplier(AnalysisKeys.SERVER_NAME, () ->
getUnsafe(serverNames).getOrDefault(serverContainer.getUnsafe(ServerKeys.SERVER_UUID), "Plan")
);
ServerProperties serverProperties = ServerInfo.getServerProperties();
putRawData(AnalysisKeys.PLAYERS_MAX, serverProperties.getMaxPlayers());
putRawData(AnalysisKeys.PLAYERS_ONLINE, serverProperties.getOnlinePlayers());
}
private void addThemeColors() {
putRawData(AnalysisKeys.ACTIVITY_PIE_COLORS, Theme.getValue(ThemeVal.GRAPH_ACTIVITY_PIE));
putRawData(AnalysisKeys.GM_PIE_COLORS, Theme.getValue(ThemeVal.GRAPH_GM_PIE));
putRawData(AnalysisKeys.PLAYERS_GRAPH_COLOR, Theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE));
putRawData(AnalysisKeys.TPS_LOW_COLOR, Theme.getValue(ThemeVal.GRAPH_TPS_LOW));
putRawData(AnalysisKeys.TPS_MEDIUM_COLOR, Theme.getValue(ThemeVal.GRAPH_TPS_MED));
putRawData(AnalysisKeys.TPS_HIGH_COLOR, Theme.getValue(ThemeVal.GRAPH_TPS_HIGH));
putRawData(AnalysisKeys.WORLD_MAP_LOW_COLOR, Theme.getValue(ThemeVal.WORLD_MAP_LOW));
putRawData(AnalysisKeys.WORLD_MAP_HIGH_COLOR, Theme.getValue(ThemeVal.WORLD_MAP_HIGH));
putRawData(AnalysisKeys.WORLD_PIE_COLORS, Theme.getValue(ThemeVal.GRAPH_WORLD_PIE));
putRawData(AnalysisKeys.AVG_PING_COLOR, Theme.getValue(ThemeVal.GRAPH_AVG_PING));
putRawData(AnalysisKeys.MAX_PING_COLOR, Theme.getValue(ThemeVal.GRAPH_MAX_PING));
putRawData(AnalysisKeys.MIN_PING_COLOR, Theme.getValue(ThemeVal.GRAPH_MIN_PING));
}
private void addPlayerSuppliers() {
putSupplier(AnalysisKeys.PLAYER_NAMES, () -> serverContainer.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>())
.stream().collect(Collectors.toMap(
p -> p.getUnsafe(PlayerKeys.UUID), p -> p.getValue(PlayerKeys.NAME).orElse("?"))
)
);
putSupplier(AnalysisKeys.PLAYERS_TOTAL, () -> serverContainer.getValue(ServerKeys.PLAYER_COUNT).orElse(0));
putSupplier(AnalysisKeys.PLAYERS_LAST_PEAK, () ->
serverContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS)
.map(dateObj -> Integer.toString(dateObj.getValue())).orElse("-")
);
putSupplier(AnalysisKeys.PLAYERS_ALL_TIME_PEAK, () ->
serverContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS)
.map(dateObj -> Integer.toString(dateObj.getValue())).orElse("-")
);
putSupplier(AnalysisKeys.LAST_PEAK_TIME_F, () ->
serverContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS)
.map(dateObj -> Formatters.year().apply(dateObj)).orElse("-")
);
putSupplier(AnalysisKeys.ALL_TIME_PEAK_TIME_F, () ->
serverContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS)
.map(dateObj -> Formatters.year().apply(dateObj)).orElse("-")
);
putSupplier(AnalysisKeys.OPERATORS, () -> serverContainer.getValue(ServerKeys.OPERATORS).map(List::size).orElse(0));
putSupplier(AnalysisKeys.PLAYERS_TABLE, () ->
PlayersTable.forServerPage(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).all()).parseHtml()
);
putSupplier(AnalysisKeys.PING_TABLE, () ->
new PingTable(
getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)
.getPingPerCountry(serverContainer.getUnsafe(ServerKeys.SERVER_UUID))
).parseHtml()
);
newAndUniquePlayerCounts();
}
private void newAndUniquePlayerCounts() {
Key<PlayersMutator> newDay = new Key<>(PlayersMutator.class, "NEW_DAY");
Key<PlayersMutator> newWeek = new Key<>(PlayersMutator.class, "NEW_WEEK");
Key<PlayersMutator> newMonth = new Key<>(PlayersMutator.class, "NEW_MONTH");
Key<PlayersMutator> uniqueDay = new Key<>(PlayersMutator.class, "UNIQUE_DAY");
Key<PlayersMutator> uniqueWeek = new Key<>(PlayersMutator.class, "UNIQUE_WEEK");
Key<PlayersMutator> uniqueMonth = new Key<>(PlayersMutator.class, "UNIQUE_MONTH");
putSupplier(newDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)
.filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(newWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)
.filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(newMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)
.filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(uniqueDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)
.filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(uniqueWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)
.filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(uniqueMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)
.filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(AnalysisKeys.PLAYERS_NEW_DAY, () -> getUnsafe(newDay).count());
putSupplier(AnalysisKeys.PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).count());
putSupplier(AnalysisKeys.PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).count());
putSupplier(AnalysisKeys.PLAYERS_DAY, () -> getUnsafe(uniqueDay).count());
putSupplier(AnalysisKeys.PLAYERS_WEEK, () -> getUnsafe(uniqueWeek).count());
putSupplier(AnalysisKeys.PLAYERS_MONTH, () -> getUnsafe(uniqueMonth).count());
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).averageNewPerDay());
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_DAY, () -> getUnsafe(newDay).averageNewPerDay());
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).averageNewPerDay());
putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).averageNewPerDay());
putSupplier(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).uniqueJoinsPerDay());
putSupplier(AnalysisKeys.NEW_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).newPerDay());
putSupplier(AnalysisKeys.UNIQUE_PLAYERS_SERIES, () ->
new AbstractLineGraph(MutatorFunctions.toPoints(
getUnsafe(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY))
).toHighChartsSeries()
);
putSupplier(AnalysisKeys.NEW_PLAYERS_SERIES, () ->
new AbstractLineGraph(MutatorFunctions.toPoints(
getUnsafe(AnalysisKeys.NEW_PLAYERS_PER_DAY))
).toHighChartsSeries()
);
Key<Integer> retentionDay = new Key<>(Integer.class, "RETENTION_DAY");
// compareAndFindThoseLikelyToBeRetained can throw exception.
putSupplier(retentionDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).compareAndFindThoseLikelyToBeRetained(
getUnsafe(newDay).all(), getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO),
getUnsafe(AnalysisKeys.PLAYERS_ONLINE_RESOLVER)
).count()
);
putSupplier(AnalysisKeys.PLAYERS_RETAINED_DAY, () -> {
try {
return getUnsafe(retentionDay);
} catch (IllegalStateException noPlayersAfterDateFiltering) {
return 0;
}
});
putSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK, () ->
getUnsafe(newWeek).filterRetained(
getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO),
getUnsafe(AnalysisKeys.ANALYSIS_TIME)
).count()
);
putSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH, () ->
getUnsafe(newMonth).filterRetained(
getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO),
getUnsafe(AnalysisKeys.ANALYSIS_TIME)
).count()
);
putSupplier(AnalysisKeys.PLAYERS_RETAINED_DAY_PERC, () -> {
try {
Integer playersNewDay = getUnsafe(AnalysisKeys.PLAYERS_NEW_DAY);
return playersNewDay != 0 ? Formatters.percentage().apply(1.0 * getUnsafe(retentionDay) / playersNewDay) : "-";
} catch (IllegalStateException noPlayersAfterDateFiltering) {
return "Not enough data";
}
});
putSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK_PERC, () -> {
Integer playersNewWeek = getUnsafe(AnalysisKeys.PLAYERS_NEW_WEEK);
return playersNewWeek != 0 ? Formatters.percentage().apply(
1.0 * getUnsafe(AnalysisKeys.PLAYERS_RETAINED_WEEK) / playersNewWeek) : "-";
}
);
putSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH_PERC, () -> {
Integer playersNewMonth = getUnsafe(AnalysisKeys.PLAYERS_NEW_MONTH);
return playersNewMonth != 0 ? Formatters.percentage().apply(
1.0 * getUnsafe(AnalysisKeys.PLAYERS_RETAINED_MONTH) / playersNewMonth) : "-";
}
);
}
private void addSessionSuppliers() {
Key<SessionAccordion> sessionAccordion = new Key<>(SessionAccordion.class, "SESSION_ACCORDION");
putSupplier(serverNames, () -> Database.getActive().fetch().getServerNames());
putSupplier(sessionAccordion, () -> SessionAccordion.forServer(
getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).all(),
getSupplier(serverNames),
() -> getUnsafe(AnalysisKeys.PLAYER_NAMES)
));
putSupplier(AnalysisKeys.SESSION_ACCORDION_HTML, () -> getUnsafe(sessionAccordion).toHtml());
putSupplier(AnalysisKeys.SESSION_ACCORDION_FUNCTIONS, () -> getUnsafe(sessionAccordion).toViewScript());
putSupplier(AnalysisKeys.RECENT_LOGINS, () -> new RecentLoginList(
serverContainer.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>())
).toHtml()
);
putSupplier(AnalysisKeys.SESSION_TABLE, () -> new ServerSessionTable(
getUnsafe(AnalysisKeys.PLAYER_NAMES), getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).all()).parseHtml()
);
putSupplier(AnalysisKeys.AVERAGE_SESSION_LENGTH_F, () -> Formatters.timeAmount()
.apply(getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageSessionLength())
);
putSupplier(AnalysisKeys.SESSION_COUNT, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).count());
putSupplier(AnalysisKeys.PLAYTIME_TOTAL, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toPlaytime());
putSupplier(AnalysisKeys.DEATHS, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toDeathCount());
putSupplier(AnalysisKeys.MOB_KILL_COUNT, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toMobKillCount());
putSupplier(AnalysisKeys.PLAYER_KILL_COUNT, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toPlayerKillCount());
putSupplier(AnalysisKeys.PLAYTIME_F, () -> Formatters.timeAmount()
.apply(getUnsafe(AnalysisKeys.PLAYTIME_TOTAL))
);
putSupplier(AnalysisKeys.AVERAGE_PLAYTIME_F, () -> {
long players = getUnsafe(AnalysisKeys.PLAYERS_TOTAL);
return players != 0 ? Formatters.timeAmount()
.apply(getUnsafe(AnalysisKeys.PLAYTIME_TOTAL) / players) : "-";
}
);
putSupplier(AnalysisKeys.AVERAGE_SESSION_LENGTH_F, () -> Formatters.timeAmount()
.apply(getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageSessionLength())
);
Key<SessionsMutator> sessionsDay = new Key<>(SessionsMutator.class, "SESSIONS_DAY");
Key<SessionsMutator> sessionsWeek = new Key<>(SessionsMutator.class, "SESSIONS_WEEK");
Key<SessionsMutator> sessionsMonth = new Key<>(SessionsMutator.class, "SESSIONS_MONTH");
putSupplier(sessionsDay, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR)
.filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(sessionsWeek, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR)
.filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(sessionsMonth, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR)
.filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(AnalysisKeys.PUNCHCARD_SERIES, () -> new PunchCardGraph(getUnsafe(sessionsMonth).all()).toHighChartsSeries());
putSupplier(AnalysisKeys.AVG_PLAYERS, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageUniqueJoinsPerDay());
putSupplier(AnalysisKeys.AVG_PLAYERS_DAY, () -> getUnsafe(sessionsDay).toAverageUniqueJoinsPerDay());
putSupplier(AnalysisKeys.AVG_PLAYERS_WEEK, () -> getUnsafe(sessionsWeek).toAverageUniqueJoinsPerDay());
putSupplier(AnalysisKeys.AVG_PLAYERS_MONTH, () -> getUnsafe(sessionsMonth).toAverageUniqueJoinsPerDay());
}
private void addGraphSuppliers() {
Key<WorldPie> worldPie = new Key<>(WorldPie.class, "WORLD_PIE");
putSupplier(worldPie, () -> new WorldPie(serverContainer.getValue(ServerKeys.WORLD_TIMES).orElse(new WorldTimes(new HashMap<>()))));
putSupplier(AnalysisKeys.WORLD_PIE_SERIES, () -> getUnsafe(worldPie).toHighChartsSeries());
putSupplier(AnalysisKeys.GM_PIE_SERIES, () -> getUnsafe(worldPie).toHighChartsDrilldown());
putSupplier(AnalysisKeys.PLAYERS_ONLINE_SERIES, () ->
new OnlineActivityGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries()
);
putSupplier(AnalysisKeys.TPS_SERIES, () -> new TPSGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries());
putSupplier(AnalysisKeys.CPU_SERIES, () -> new CPUGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries());
putSupplier(AnalysisKeys.RAM_SERIES, () -> new RamGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries());
putSupplier(AnalysisKeys.ENTITY_SERIES, () -> new EntityGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries());
putSupplier(AnalysisKeys.CHUNK_SERIES, () -> new ChunkGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries());
putSupplier(AnalysisKeys.WORLD_MAP_SERIES, () ->
new WorldMap(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).getGeolocations()).toHighChartsSeries()
);
Key<GeolocationBarGraph> geolocationBarChart = new Key<>(GeolocationBarGraph.class, "GEOLOCATION_BAR_CHART");
putSupplier(geolocationBarChart, () -> new GeolocationBarGraph(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)));
putSupplier(AnalysisKeys.COUNTRY_CATEGORIES, () -> getUnsafe(geolocationBarChart).toHighChartsCategories());
putSupplier(AnalysisKeys.COUNTRY_SERIES, () -> getUnsafe(geolocationBarChart).toHighChartsSeries());
Key<PingGraph> pingGraph = new Key<>(PingGraph.class, "PING_GRAPH");
putSupplier(pingGraph, () -> new PingGraph(
PingMutator.forContainer(serverContainer).mutateToByMinutePings().all()
));
putSupplier(AnalysisKeys.AVG_PING_SERIES, () -> getUnsafe(pingGraph).toAvgSeries());
putSupplier(AnalysisKeys.MAX_PING_SERIES, () -> getUnsafe(pingGraph).toMaxSeries());
putSupplier(AnalysisKeys.MIN_PING_SERIES, () -> getUnsafe(pingGraph).toMinSeries());
putSupplier(AnalysisKeys.CALENDAR_SERIES, () -> new ServerCalendar(
getUnsafe(AnalysisKeys.PLAYERS_MUTATOR),
getUnsafe(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY),
getUnsafe(AnalysisKeys.NEW_PLAYERS_PER_DAY)
).toCalendarSeries());
putSupplier(AnalysisKeys.ACTIVITY_DATA, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).toActivityDataMap(getUnsafe(AnalysisKeys.ANALYSIS_TIME)));
Key<ActivityStackGraph> activityStackGraph = new Key<>(ActivityStackGraph.class, "ACTIVITY_STACK_GRAPH");
putSupplier(activityStackGraph, () -> new ActivityStackGraph(getUnsafe(AnalysisKeys.ACTIVITY_DATA)));
putSupplier(AnalysisKeys.ACTIVITY_STACK_CATEGORIES, () -> getUnsafe(activityStackGraph).toHighChartsLabels());
putSupplier(AnalysisKeys.ACTIVITY_STACK_SERIES, () -> getUnsafe(activityStackGraph).toHighChartsSeries());
putSupplier(AnalysisKeys.ACTIVITY_PIE_SERIES, () ->
new ActivityPie(getUnsafe(AnalysisKeys.ACTIVITY_DATA).get(getUnsafe(AnalysisKeys.ANALYSIS_TIME))).toHighChartsSeries()
);
putSupplier(AnalysisKeys.PLAYERS_REGULAR, () -> {
Map<String, Set<UUID>> activityNow = getUnsafe(AnalysisKeys.ACTIVITY_DATA)
.floorEntry(getUnsafe(AnalysisKeys.ANALYSIS_TIME)).getValue();
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
return veryActiveNow.size() + activeNow.size() + regularNow.size();
});
}
private void addTPSAverageSuppliers() {
Key<TPSMutator> tpsMonth = new Key<>(TPSMutator.class, "TPS_MONTH");
Key<TPSMutator> tpsWeek = new Key<>(TPSMutator.class, "TPS_WEEK");
Key<TPSMutator> tpsDay = new Key<>(TPSMutator.class, "TPS_DAY");
putSupplier(tpsMonth, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR)
.filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(tpsWeek, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR)
.filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(tpsDay, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR)
.filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME))
);
putSupplier(AnalysisKeys.PLAYERS_ONLINE_RESOLVER, () -> new PlayersOnlineResolver(getUnsafe(AnalysisKeys.TPS_MUTATOR)));
putSupplier(AnalysisKeys.TPS_SPIKE_MONTH, () -> getUnsafe(tpsMonth).lowTpsSpikeCount());
putSupplier(AnalysisKeys.AVG_TPS_MONTH, () -> getUnsafe(tpsMonth).averageTPS());
putSupplier(AnalysisKeys.AVG_CPU_MONTH, () -> getUnsafe(tpsMonth).averageCPU());
putSupplier(AnalysisKeys.AVG_RAM_MONTH, () -> getUnsafe(tpsMonth).averageRAM());
putSupplier(AnalysisKeys.AVG_ENTITY_MONTH, () -> getUnsafe(tpsMonth).averageEntities());
putSupplier(AnalysisKeys.AVG_CHUNK_MONTH, () -> getUnsafe(tpsMonth).averageChunks());
putSupplier(AnalysisKeys.TPS_SPIKE_WEEK, () -> getUnsafe(tpsWeek).lowTpsSpikeCount());
putSupplier(AnalysisKeys.AVG_TPS_WEEK, () -> getUnsafe(tpsWeek).averageTPS());
putSupplier(AnalysisKeys.AVG_CPU_WEEK, () -> getUnsafe(tpsWeek).averageCPU());
putSupplier(AnalysisKeys.AVG_RAM_WEEK, () -> getUnsafe(tpsWeek).averageRAM());
putSupplier(AnalysisKeys.AVG_ENTITY_WEEK, () -> getUnsafe(tpsWeek).averageEntities());
putSupplier(AnalysisKeys.AVG_CHUNK_WEEK, () -> getUnsafe(tpsWeek).averageChunks());
putSupplier(AnalysisKeys.TPS_SPIKE_DAY, () -> getUnsafe(tpsDay).lowTpsSpikeCount());
putSupplier(AnalysisKeys.AVG_TPS_DAY, () -> getUnsafe(tpsDay).averageTPS());
putSupplier(AnalysisKeys.AVG_CPU_DAY, () -> getUnsafe(tpsDay).averageCPU());
putSupplier(AnalysisKeys.AVG_RAM_DAY, () -> getUnsafe(tpsDay).averageRAM());
putSupplier(AnalysisKeys.AVG_ENTITY_DAY, () -> getUnsafe(tpsDay).averageEntities());
putSupplier(AnalysisKeys.AVG_CHUNK_DAY, () -> getUnsafe(tpsDay).averageChunks());
}
private void addCommandSuppliers() {
putSupplier(AnalysisKeys.COMMAND_USAGE_TABLE, () -> new CommandUseTable(serverContainer).parseHtml());
putSupplier(AnalysisKeys.COMMAND_COUNT_UNIQUE, () -> serverContainer.getValue(ServerKeys.COMMAND_USAGE).map(Map::size).orElse(0));
putSupplier(AnalysisKeys.COMMAND_COUNT, () -> CommandUseMutator.forContainer(serverContainer).commandUsageCount());
}
private void addServerHealth() {
Key<HealthInformation> healthInformation = new Key<>(HealthInformation.class, "HEALTH_INFORMATION");
putSupplier(healthInformation, () -> new HealthInformation(this));
putSupplier(AnalysisKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth());
putSupplier(AnalysisKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml());
}
private void addPluginSuppliers() {
// TODO Refactor into a system that supports running the analysis on Bungee
Key<String[]> navAndTabs = new Key<>(new Type<String[]>() {}, "NAV_AND_TABS");
putSupplier(navAndTabs, () ->
AnalysisPluginsTabContentCreator.createContent(
getUnsafe(AnalysisKeys.PLAYERS_MUTATOR),
this
)
);
putSupplier(AnalysisKeys.BAN_DATA, () -> new ServerBanDataReader().readBanDataForContainer(this));
putSupplier(AnalysisKeys.PLUGINS_TAB_NAV, () -> getUnsafe(navAndTabs)[0]);
putSupplier(AnalysisKeys.PLUGINS_TAB, () -> getUnsafe(navAndTabs)[1]);
}
}

View File

@ -0,0 +1,119 @@
package com.djrapitops.plan.data.store.containers;
import com.djrapitops.plan.data.store.CachingSupplier;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
import java.util.HashMap;
import java.util.Optional;
import java.util.function.Supplier;
/**
* Abstract representation of an object that holds the Values for different Keys.
* <p>
* The methods in this object are used for placing and fetching the data from the container.
* Methods to use depend on your use case.
*
* @author Rsl1122
*/
public class DataContainer extends HashMap<Key, Supplier> {
/**
* Place your data inside the container.
*
* @param key Key of type T that identifies the data and will be used later when the data needs to be fetched.
* @param obj object to store
* @param <T> Type of the object
*/
public <T> void putRawData(Key<T> key, T obj) {
putSupplier(key, () -> obj);
}
public <T> void putSupplier(Key<T> key, Supplier<T> supplier) {
super.put(key, new CachingSupplier<>(supplier));
}
public <T> Supplier<T> getSupplier(Key<T> key) {
return (Supplier<T>) super.get(key);
}
/**
* Check if a Value with the given Key has been placed into the container.
*
* @param key Key that identifies the data.
* @param <T> Type of the object returned by the Value if it is present.
* @return true if found, false if not.
*/
public <T> boolean supports(Key<T> key) {
return containsKey(key);
}
/**
* Get an Optional of the data identified by the Key.
* <p>
* Since Value is a functional interface, its method may call blocking methods via Value implementations,
* It is therefore recommended to not call this method on the server thread.
* <p>
* It is recommended to check if the Optional is present as null values returned by plugins will be empty.
*
* @param key Key that identifies the Value
* @param <T> Type of the object returned by Value
* @return Optional of the object if the key is registered and key matches the type of the object. Otherwise empty.
*/
public <T> Optional<T> getValue(Key<T> key) {
Supplier<T> supplier = getSupplier(key);
if (supplier == null) {
return Optional.empty();
}
try {
return Optional.ofNullable(supplier.get());
} catch (ClassCastException e) {
return Optional.empty();
}
}
public <T> T getUnsafe(Key<T> key) {
Supplier supplier = get(key);
if (supplier == null) {
throw new IllegalArgumentException("Unsupported Key: " + key.getKeyName());
}
return (T) supplier.get();
}
public <T> String getFormatted(Key<T> key, Formatter<Optional<T>> formatter) {
Optional<T> value = getValue(key);
return formatter.apply(value);
}
public <T> String getFormattedUnsafe(Key<T> key, Formatter<T> formatter) {
T value = getUnsafe(key);
return formatter.apply(value);
}
/**
* Normal put method.
*
* @param key Key.
* @param value Supplier
* @return the previous value.
* @deprecated Use putSupplier instead for type safety.
*/
@Override
@Deprecated
public Supplier put(Key key, Supplier value) {
return super.put(key, value);
}
/**
* Normal get method.
*
* @param key Key.
* @return Supplier
* @deprecated Use getSupplier instead for types.
*/
@Override
@Deprecated
public Supplier get(Object key) {
return super.get(key);
}
}

View File

@ -0,0 +1,176 @@
package com.djrapitops.plan.data.store.containers;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.keys.NetworkKeys;
import com.djrapitops.plan.data.store.keys.ServerKeys;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.data.store.mutators.TPSMutator;
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
import com.djrapitops.plan.data.store.mutators.health.NetworkHealthInformation;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.theme.Theme;
import com.djrapitops.plan.system.settings.theme.ThemeVal;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plan.utilities.html.graphs.ActivityStackGraph;
import com.djrapitops.plan.utilities.html.graphs.WorldMap;
import com.djrapitops.plan.utilities.html.graphs.bar.GeolocationBarGraph;
import com.djrapitops.plan.utilities.html.graphs.line.OnlineActivityGraph;
import com.djrapitops.plan.utilities.html.graphs.pie.ActivityPie;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.api.utility.log.Log;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
/**
* DataContainer for the whole network.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.keys.NetworkKeys for Key objects
* @see com.djrapitops.plan.data.store.PlaceholderKey for placeholder information
*/
public class NetworkContainer extends DataContainer {
private final ServerContainer bungeeContainer;
private final Map<UUID, AnalysisContainer> serverContainers;
public NetworkContainer(ServerContainer bungeeContainer) {
this.bungeeContainer = bungeeContainer;
serverContainers = new HashMap<>();
putSupplier(NetworkKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(bungeeContainer));
addConstants();
addPlayerInformation();
addNetworkHealth();
}
private void addNetworkHealth() {
Key<NetworkHealthInformation> healthInformation = new Key<>(NetworkHealthInformation.class, "HEALTH_INFORMATION");
putSupplier(healthInformation, () -> new NetworkHealthInformation(this));
putSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth());
putSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml());
}
public void putAnalysisContainer(AnalysisContainer analysisContainer) {
serverContainers.put(analysisContainer.getServerContainer().getUnsafe(ServerKeys.SERVER_UUID), analysisContainer);
}
public Optional<AnalysisContainer> getAnalysisContainer(UUID serverUUID) {
AnalysisContainer container = serverContainers.get(serverUUID);
if (container != null) {
return Optional.of(container);
}
try {
AnalysisContainer analysisContainer = new AnalysisContainer(Database.getActive().fetch().getServerContainer(serverUUID));
serverContainers.put(serverUUID, analysisContainer);
return Optional.of(analysisContainer);
} catch (DBOpException e) {
Log.toLog(this.getClass(), e);
}
return Optional.empty();
}
private void addConstants() {
long now = System.currentTimeMillis();
putRawData(NetworkKeys.REFRESH_TIME, now);
putRawData(NetworkKeys.REFRESH_TIME_DAY_AGO, getUnsafe(NetworkKeys.REFRESH_TIME) - TimeAmount.DAY.ms());
putRawData(NetworkKeys.REFRESH_TIME_WEEK_AGO, getUnsafe(NetworkKeys.REFRESH_TIME) - TimeAmount.WEEK.ms());
putRawData(NetworkKeys.REFRESH_TIME_MONTH_AGO, getUnsafe(NetworkKeys.REFRESH_TIME) - TimeAmount.MONTH.ms());
putSupplier(NetworkKeys.REFRESH_TIME_F, () -> Formatters.second().apply(() -> getUnsafe(NetworkKeys.REFRESH_TIME)));
putRawData(NetworkKeys.VERSION, PlanPlugin.getInstance().getVersion());
putSupplier(NetworkKeys.TIME_ZONE, MiscUtils::getTimeZoneOffsetHours);
putSupplier(NetworkKeys.NETWORK_NAME, () ->
Check.isBungeeAvailable() ?
Settings.BUNGEE_NETWORK_NAME.toString() :
bungeeContainer.getValue(ServerKeys.NAME).orElse("Plan")
);
putSupplier(NetworkKeys.PLAYERS_ONLINE, ServerInfo.getServerProperties()::getOnlinePlayers);
putRawData(NetworkKeys.WORLD_MAP_LOW_COLOR, Theme.getValue(ThemeVal.WORLD_MAP_LOW));
putRawData(NetworkKeys.WORLD_MAP_HIGH_COLOR, Theme.getValue(ThemeVal.WORLD_MAP_HIGH));
putRawData(NetworkKeys.PLAYERS_GRAPH_COLOR, Theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE));
}
private void addPlayerInformation() {
putSupplier(NetworkKeys.PLAYERS_TOTAL, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR).count());
putSupplier(NetworkKeys.WORLD_MAP_SERIES, () ->
new WorldMap(PlayersMutator.forContainer(bungeeContainer)).toHighChartsSeries()
);
Key<GeolocationBarGraph> geolocationBarChart = new Key<>(GeolocationBarGraph.class, "GEOLOCATION_BAR_CHART");
putSupplier(geolocationBarChart, () -> new GeolocationBarGraph(getUnsafe(NetworkKeys.PLAYERS_MUTATOR)));
putSupplier(NetworkKeys.COUNTRY_CATEGORIES, () -> getUnsafe(geolocationBarChart).toHighChartsCategories());
putSupplier(NetworkKeys.COUNTRY_SERIES, () -> getUnsafe(geolocationBarChart).toHighChartsSeries());
putSupplier(NetworkKeys.PLAYERS_ONLINE_SERIES, () ->
new OnlineActivityGraph(TPSMutator.forContainer(bungeeContainer)).toHighChartsSeries()
);
Key<ActivityStackGraph> activityStackGraph = new Key<>(ActivityStackGraph.class, "ACTIVITY_STACK_GRAPH");
putSupplier(NetworkKeys.ACTIVITY_DATA, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR).toActivityDataMap(getUnsafe(NetworkKeys.REFRESH_TIME)));
putSupplier(activityStackGraph, () -> new ActivityStackGraph(getUnsafe(NetworkKeys.ACTIVITY_DATA)));
putSupplier(NetworkKeys.ACTIVITY_STACK_CATEGORIES, () -> getUnsafe(activityStackGraph).toHighChartsLabels());
putSupplier(NetworkKeys.ACTIVITY_STACK_SERIES, () -> getUnsafe(activityStackGraph).toHighChartsSeries());
putSupplier(NetworkKeys.ACTIVITY_PIE_SERIES, () ->
new ActivityPie(getUnsafe(NetworkKeys.ACTIVITY_DATA).get(getUnsafe(NetworkKeys.REFRESH_TIME))).toHighChartsSeries()
);
putSupplier(NetworkKeys.ALL_TIME_PEAK_TIME_F, () ->
bungeeContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS).map(Formatters.year()::apply).orElse("No data")
);
putSupplier(NetworkKeys.RECENT_PEAK_TIME_F, () ->
bungeeContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS).map(Formatters.year()::apply).orElse("No data")
);
putSupplier(NetworkKeys.PLAYERS_ALL_TIME_PEAK, () ->
bungeeContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS).map(dateObj -> "" + dateObj.getValue()).orElse("-")
);
putSupplier(NetworkKeys.PLAYERS_RECENT_PEAK, () ->
bungeeContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS).map(dateObj -> "" + dateObj.getValue()).orElse("-")
);
addPlayerCounts();
}
private void addPlayerCounts() {
Key<PlayersMutator> newDay = new Key<>(PlayersMutator.class, "NEW_DAY");
Key<PlayersMutator> newWeek = new Key<>(PlayersMutator.class, "NEW_WEEK");
Key<PlayersMutator> newMonth = new Key<>(PlayersMutator.class, "NEW_MONTH");
Key<PlayersMutator> uniqueDay = new Key<>(PlayersMutator.class, "UNIQUE_DAY");
Key<PlayersMutator> uniqueWeek = new Key<>(PlayersMutator.class, "UNIQUE_WEEK");
Key<PlayersMutator> uniqueMonth = new Key<>(PlayersMutator.class, "UNIQUE_MONTH");
putSupplier(newDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR)
.filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_DAY_AGO), getUnsafe(NetworkKeys.REFRESH_TIME))
);
putSupplier(newWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR)
.filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_WEEK_AGO), getUnsafe(NetworkKeys.REFRESH_TIME))
);
putSupplier(newMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR)
.filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO), getUnsafe(NetworkKeys.REFRESH_TIME))
);
putSupplier(uniqueDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR)
.filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_DAY_AGO), getUnsafe(NetworkKeys.REFRESH_TIME))
);
putSupplier(uniqueWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR)
.filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_WEEK_AGO), getUnsafe(NetworkKeys.REFRESH_TIME))
);
putSupplier(uniqueMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR)
.filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO), getUnsafe(NetworkKeys.REFRESH_TIME))
);
putSupplier(NetworkKeys.PLAYERS_NEW_DAY, () -> getUnsafe(newDay).count());
putSupplier(NetworkKeys.PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).count());
putSupplier(NetworkKeys.PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).count());
putSupplier(NetworkKeys.PLAYERS_DAY, () -> getUnsafe(uniqueDay).count());
putSupplier(NetworkKeys.PLAYERS_WEEK, () -> getUnsafe(uniqueWeek).count());
putSupplier(NetworkKeys.PLAYERS_MONTH, () -> getUnsafe(uniqueMonth).count());
}
}

View File

@ -0,0 +1,13 @@
package com.djrapitops.plan.data.store.containers;
import java.util.HashMap;
import java.util.UUID;
/**
* Container for data about a player linked to a single server.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.keys.PerServerKeys For Key objects.
*/
public class PerServerContainer extends HashMap<UUID, DataContainer> {
}

View File

@ -0,0 +1,38 @@
package com.djrapitops.plan.data.store.containers;
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import java.util.HashMap;
import java.util.Map;
/**
* DataContainer about a Player.
* <p>
* Use {@code getValue(PlayerKeys.REGISTERED).isPresent()} to determine if Plan has data about the player.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.keys.PlayerKeys For Key objects.
*/
public class PlayerContainer extends DataContainer {
private Map<Long, ActivityIndex> activityIndexCache;
public PlayerContainer() {
activityIndexCache = new HashMap<>();
}
public ActivityIndex getActivityIndex(long date) {
ActivityIndex index = activityIndexCache.get(date);
if (index == null) {
index = new ActivityIndex(this, date);
activityIndexCache.put(date, index);
}
return index;
}
public boolean playedBetween(long after, long before) {
return SessionsMutator.forContainer(this).playedBetween(after, before);
}
}

View File

@ -0,0 +1,10 @@
package com.djrapitops.plan.data.store.containers;
/**
* DataContainer for a single server.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.keys.ServerKeys For Key objects.
*/
public class ServerContainer extends DataContainer {
}

View File

@ -0,0 +1,162 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.PlaceholderKey;
import com.djrapitops.plan.data.store.Type;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.data.store.mutators.PlayersOnlineResolver;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.data.store.mutators.TPSMutator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
/**
* Key objects used for Analysis.
* <p>
* PlaceholderKey objects can be used for directly replacing a value on the html.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.containers.AnalysisContainer for Suppliers for each Key.
*/
public class AnalysisKeys {
// Constants (Affected only by config settings)
public static final PlaceholderKey<String> VERSION = CommonPlaceholderKeys.VERSION;
public static final PlaceholderKey<String> SERVER_NAME = new PlaceholderKey<>(String.class, "serverName");
public static final PlaceholderKey<Integer> TIME_ZONE = CommonPlaceholderKeys.TIME_ZONE;
public static final PlaceholderKey<Integer> FIRST_DAY = new PlaceholderKey<>(Integer.class, "firstDay");
public static final PlaceholderKey<Integer> TPS_MEDIUM = new PlaceholderKey<>(Integer.class, "tpsMedium");
public static final PlaceholderKey<Integer> TPS_HIGH = new PlaceholderKey<>(Integer.class, "tpsHigh");
public static final PlaceholderKey<Integer> PLAYERS_MAX = new PlaceholderKey<>(Integer.class, "playersMax");
public static final PlaceholderKey<Integer> PLAYERS_ONLINE = CommonPlaceholderKeys.PLAYERS_ONLINE;
public static final PlaceholderKey<Integer> PLAYERS_TOTAL = CommonPlaceholderKeys.PLAYERS_TOTAL;
//
public static final PlaceholderKey<String> WORLD_PIE_COLORS = new PlaceholderKey<>(String.class, "worldPieColors");
public static final PlaceholderKey<String> GM_PIE_COLORS = new PlaceholderKey<>(String.class, "gmPieColors");
public static final PlaceholderKey<String> ACTIVITY_PIE_COLORS = new PlaceholderKey<>(String.class, "activityPieColors");
public static final PlaceholderKey<String> PLAYERS_GRAPH_COLOR = CommonPlaceholderKeys.PLAYERS_GRAPH_COLOR;
public static final PlaceholderKey<String> TPS_HIGH_COLOR = new PlaceholderKey<>(String.class, "tpsHighColor");
public static final PlaceholderKey<String> TPS_MEDIUM_COLOR = new PlaceholderKey<>(String.class, "tpsMediumColor");
public static final PlaceholderKey<String> TPS_LOW_COLOR = new PlaceholderKey<>(String.class, "tpsLowColor");
public static final PlaceholderKey<String> AVG_PING_COLOR = new PlaceholderKey<>(String.class, "avgPingColor");
public static final PlaceholderKey<String> MIN_PING_COLOR = new PlaceholderKey<>(String.class, "minPingColor");
public static final PlaceholderKey<String> MAX_PING_COLOR = new PlaceholderKey<>(String.class, "maxPingColor");
public static final PlaceholderKey<String> WORLD_MAP_HIGH_COLOR = CommonPlaceholderKeys.WORLD_MAP_HIGH_COLOR;
public static final PlaceholderKey<String> WORLD_MAP_LOW_COLOR = CommonPlaceholderKeys.WORLD_MAP_LOW_COLOR;
// Tables & other structures
public static final PlaceholderKey<String> PLAYERS_TABLE = new PlaceholderKey<>(String.class, "tablePlayerlist");
public static final PlaceholderKey<String> SESSION_ACCORDION_HTML = new PlaceholderKey<>(String.class, "accordionSessions");
public static final PlaceholderKey<String> SESSION_ACCORDION_FUNCTIONS = new PlaceholderKey<>(String.class, "sessionTabGraphViewFunctions");
public static final PlaceholderKey<String> SESSION_TABLE = new PlaceholderKey<>(String.class, "tableBodySessions");
public static final PlaceholderKey<String> PING_TABLE = new PlaceholderKey<>(String.class, "tablePing");
public static final PlaceholderKey<String> RECENT_LOGINS = new PlaceholderKey<>(String.class, "listRecentLogins");
public static final PlaceholderKey<String> COMMAND_USAGE_TABLE = new PlaceholderKey<>(String.class, "tableCommandUsage");
public static final PlaceholderKey<String> HEALTH_NOTES = CommonPlaceholderKeys.HEALTH_NOTES;
public static final PlaceholderKey<String> PLUGINS_TAB = new PlaceholderKey<>(String.class, "tabsPlugins");
public static final PlaceholderKey<String> PLUGINS_TAB_NAV = new PlaceholderKey<>(String.class, "navPluginsTabs");
// Formatted time values
public static final PlaceholderKey<String> REFRESH_TIME_F = CommonPlaceholderKeys.REFRESH_TIME_F;
public static final PlaceholderKey<String> LAST_PEAK_TIME_F = CommonPlaceholderKeys.LAST_PEAK_TIME_F;
public static final PlaceholderKey<String> ALL_TIME_PEAK_TIME_F = CommonPlaceholderKeys.ALL_TIME_PEAK_TIME_F;
public static final PlaceholderKey<String> AVERAGE_SESSION_LENGTH_F = new PlaceholderKey<>(String.class, "sessionAverage");
public static final PlaceholderKey<String> AVERAGE_PLAYTIME_F = new PlaceholderKey<>(String.class, "playtimeAverage");
public static final PlaceholderKey<String> PLAYTIME_F = new PlaceholderKey<>(String.class, "playtimeTotal");
// Direct values, possibly formatted
public static final PlaceholderKey<String> PLAYERS_LAST_PEAK = CommonPlaceholderKeys.PLAYERS_LAST_PEAK;
public static final PlaceholderKey<String> PLAYERS_ALL_TIME_PEAK = CommonPlaceholderKeys.PLAYERS_ALL_TIME_PEAK;
public static final PlaceholderKey<Integer> OPERATORS = new PlaceholderKey<>(Integer.class, "ops");
public static final PlaceholderKey<Integer> PLAYERS_REGULAR = new PlaceholderKey<>(Integer.class, "playersRegular");
public static final PlaceholderKey<Integer> SESSION_COUNT = new PlaceholderKey<>(Integer.class, "sessionCount");
public static final PlaceholderKey<Integer> DEATHS = new PlaceholderKey<>(Integer.class, "deaths");
public static final PlaceholderKey<Integer> MOB_KILL_COUNT = new PlaceholderKey<>(Integer.class, "mobKillCount");
public static final PlaceholderKey<Integer> PLAYER_KILL_COUNT = new PlaceholderKey<>(Integer.class, "killCount");
public static final PlaceholderKey<Double> HEALTH_INDEX = CommonPlaceholderKeys.HEALTH_INDEX;
public static final PlaceholderKey<Integer> COMMAND_COUNT = new PlaceholderKey<>(Integer.class, "commandCount");
public static final PlaceholderKey<Integer> COMMAND_COUNT_UNIQUE = new PlaceholderKey<>(Integer.class, "commandUniqueCount");
//
public static final PlaceholderKey<Integer> PLAYERS_DAY = CommonPlaceholderKeys.PLAYERS_DAY;
public static final PlaceholderKey<Integer> PLAYERS_WEEK = CommonPlaceholderKeys.PLAYERS_WEEK;
public static final PlaceholderKey<Integer> PLAYERS_MONTH = CommonPlaceholderKeys.PLAYERS_MONTH;
public static final PlaceholderKey<Integer> PLAYERS_NEW_DAY = CommonPlaceholderKeys.PLAYERS_NEW_DAY;
public static final PlaceholderKey<Integer> PLAYERS_NEW_WEEK = CommonPlaceholderKeys.PLAYERS_NEW_WEEK;
public static final PlaceholderKey<Integer> PLAYERS_NEW_MONTH = CommonPlaceholderKeys.PLAYERS_NEW_MONTH;
public static final PlaceholderKey<Integer> AVG_PLAYERS = new PlaceholderKey<>(Integer.class, "playersAverage");
public static final PlaceholderKey<Integer> AVG_PLAYERS_DAY = new PlaceholderKey<>(Integer.class, "playersAverageDay");
public static final PlaceholderKey<Integer> AVG_PLAYERS_WEEK = new PlaceholderKey<>(Integer.class, "playersAverageWeek");
public static final PlaceholderKey<Integer> AVG_PLAYERS_MONTH = new PlaceholderKey<>(Integer.class, "playersAverageMonth");
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW = new PlaceholderKey<>(Integer.class, "playersNewAverage");
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_DAY = new PlaceholderKey<>(Integer.class, "playersNewAverageDay");
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_WEEK = new PlaceholderKey<>(Integer.class, "playersNewAverageWeek");
public static final PlaceholderKey<Integer> AVG_PLAYERS_NEW_MONTH = new PlaceholderKey<>(Integer.class, "playersNewAverageMonth");
public static final PlaceholderKey<Integer> PLAYERS_RETAINED_DAY = new PlaceholderKey<>(Integer.class, "playersStuckDay");
public static final PlaceholderKey<String> PLAYERS_RETAINED_DAY_PERC = new PlaceholderKey<>(String.class, "playersStuckPercDay");
public static final PlaceholderKey<Integer> PLAYERS_RETAINED_WEEK = new PlaceholderKey<>(Integer.class, "playersStuckWeek");
public static final PlaceholderKey<String> PLAYERS_RETAINED_WEEK_PERC = new PlaceholderKey<>(String.class, "playersStuckPercWeek");
public static final PlaceholderKey<Integer> PLAYERS_RETAINED_MONTH = new PlaceholderKey<>(Integer.class, "playersStuckMonth");
public static final PlaceholderKey<String> PLAYERS_RETAINED_MONTH_PERC = new PlaceholderKey<>(String.class, "playersStuckPercMonth");
//
public static final PlaceholderKey<Integer> TPS_SPIKE_MONTH = new PlaceholderKey<>(Integer.class, "tpsSpikeMonth");
public static final PlaceholderKey<Integer> TPS_SPIKE_WEEK = new PlaceholderKey<>(Integer.class, "tpsSpikeWeek");
public static final PlaceholderKey<Integer> TPS_SPIKE_DAY = new PlaceholderKey<>(Integer.class, "tpsSpikeDay");
public static final PlaceholderKey<Double> AVG_TPS_MONTH = new PlaceholderKey<>(Double.class, "tpsAverageMonth");
public static final PlaceholderKey<Double> AVG_TPS_WEEK = new PlaceholderKey<>(Double.class, "tpsAverageWeek");
public static final PlaceholderKey<Double> AVG_TPS_DAY = new PlaceholderKey<>(Double.class, "tpsAverageDay");
public static final PlaceholderKey<Double> AVG_CPU_MONTH = new PlaceholderKey<>(Double.class, "cpuAverageMonth");
public static final PlaceholderKey<Double> AVG_CPU_WEEK = new PlaceholderKey<>(Double.class, "cpuAverageWeek");
public static final PlaceholderKey<Double> AVG_CPU_DAY = new PlaceholderKey<>(Double.class, "cpuAverageDay");
public static final PlaceholderKey<Double> AVG_RAM_MONTH = new PlaceholderKey<>(Double.class, "ramAverageMonth");
public static final PlaceholderKey<Double> AVG_RAM_WEEK = new PlaceholderKey<>(Double.class, "ramAverageWeek");
public static final PlaceholderKey<Double> AVG_RAM_DAY = new PlaceholderKey<>(Double.class, "ramAverageDay");
public static final PlaceholderKey<Double> AVG_ENTITY_MONTH = new PlaceholderKey<>(Double.class, "entityAverageMonth");
public static final PlaceholderKey<Double> AVG_ENTITY_WEEK = new PlaceholderKey<>(Double.class, "entityAverageWeek");
public static final PlaceholderKey<Double> AVG_ENTITY_DAY = new PlaceholderKey<>(Double.class, "entityAverageDay");
public static final PlaceholderKey<Double> AVG_CHUNK_MONTH = new PlaceholderKey<>(Double.class, "chunkAverageMonth");
public static final PlaceholderKey<Double> AVG_CHUNK_WEEK = new PlaceholderKey<>(Double.class, "chunkAverageWeek");
public static final PlaceholderKey<Double> AVG_CHUNK_DAY = new PlaceholderKey<>(Double.class, "chunkAverageDay");
// Data for Charts
public static final PlaceholderKey<String> WORLD_PIE_SERIES = new PlaceholderKey<>(String.class, "worldSeries");
public static final PlaceholderKey<String> GM_PIE_SERIES = new PlaceholderKey<>(String.class, "gmSeries");
public static final PlaceholderKey<String> PLAYERS_ONLINE_SERIES = CommonPlaceholderKeys.PLAYERS_ONLINE_SERIES;
public static final PlaceholderKey<String> TPS_SERIES = new PlaceholderKey<>(String.class, "tpsSeries");
public static final PlaceholderKey<String> CPU_SERIES = new PlaceholderKey<>(String.class, "cpuSeries");
public static final PlaceholderKey<String> RAM_SERIES = new PlaceholderKey<>(String.class, "ramSeries");
public static final PlaceholderKey<String> ENTITY_SERIES = new PlaceholderKey<>(String.class, "entitySeries");
public static final PlaceholderKey<String> CHUNK_SERIES = new PlaceholderKey<>(String.class, "chunkSeries");
public static final PlaceholderKey<String> PUNCHCARD_SERIES = new PlaceholderKey<>(String.class, "punchCardSeries");
public static final PlaceholderKey<String> WORLD_MAP_SERIES = CommonPlaceholderKeys.WORLD_MAP_SERIES;
public static final PlaceholderKey<String> ACTIVITY_STACK_SERIES = CommonPlaceholderKeys.ACTIVITY_STACK_SERIES;
public static final PlaceholderKey<String> ACTIVITY_STACK_CATEGORIES = CommonPlaceholderKeys.ACTIVITY_STACK_CATEGORIES;
public static final PlaceholderKey<String> ACTIVITY_PIE_SERIES = CommonPlaceholderKeys.ACTIVITY_PIE_SERIES;
public static final PlaceholderKey<String> CALENDAR_SERIES = new PlaceholderKey<>(String.class, "calendarSeries");
public static final PlaceholderKey<String> UNIQUE_PLAYERS_SERIES = new PlaceholderKey<>(String.class, "uniquePlayersSeries");
public static final PlaceholderKey<String> NEW_PLAYERS_SERIES = new PlaceholderKey<>(String.class, "newPlayersSeries");
public static final PlaceholderKey<String> AVG_PING_SERIES = new PlaceholderKey<>(String.class, "avgPingSeries");
public static final PlaceholderKey<String> MAX_PING_SERIES = new PlaceholderKey<>(String.class, "maxPingSeries");
public static final PlaceholderKey<String> MIN_PING_SERIES = new PlaceholderKey<>(String.class, "minPingSeries");
public static final PlaceholderKey<String> COUNTRY_CATEGORIES = CommonPlaceholderKeys.COUNTRY_CATEGORIES;
public static final PlaceholderKey<String> COUNTRY_SERIES = CommonPlaceholderKeys.COUNTRY_SERIES;
// Variables used only during analysis
public static final Key<SessionsMutator> SESSIONS_MUTATOR = CommonKeys.SESSIONS_MUTATOR;
public static final Key<TPSMutator> TPS_MUTATOR = CommonKeys.TPS_MUTATOR;
public static final Key<PlayersMutator> PLAYERS_MUTATOR = CommonKeys.PLAYERS_MUTATOR;
public static final Key<PlayersOnlineResolver> PLAYERS_ONLINE_RESOLVER = new Key<>(PlayersOnlineResolver.class, "PLAYERS_ONLINE_RESOLVER");
public static final Key<Long> PLAYTIME_TOTAL = new Key<>(Long.class, "PLAYTIME_TOTAL");
public static final Key<Long> ANALYSIS_TIME = new Key<>(Long.class, "ANALYSIS_TIME");
public static final Key<Long> ANALYSIS_TIME_DAY_AGO = new Key<>(Long.class, "ANALYSIS_TIME_DAY_AGO");
public static final Key<Long> ANALYSIS_TIME_WEEK_AGO = new Key<>(Long.class, "ANALYSIS_TIME_WEEK_AGO");
public static final Key<Long> ANALYSIS_TIME_MONTH_AGO = new Key<>(Long.class, "ANALYSIS_TIME_MONTH_AGO");
public static final Key<Map<UUID, String>> PLAYER_NAMES = new Key<>(new Type<Map<UUID, String>>() {}, "PLAYER_NAMES");
public static final Key<TreeMap<Long, Map<String, Set<UUID>>>> ACTIVITY_DATA = CommonKeys.ACTIVITY_DATA;
public static final Key<Set<UUID>> BAN_DATA = new Key<>(new Type<Set<UUID>>() {}, "BAN_DATA");
public static final Key<TreeMap<Long, Integer>> UNIQUE_PLAYERS_PER_DAY = new Key<>(new Type<TreeMap<Long, Integer>>() {}, "UNIQUE_PLAYERS_PER_DAY");
public static final Key<TreeMap<Long, Integer>> NEW_PLAYERS_PER_DAY = new Key<>(new Type<TreeMap<Long, Integer>>() {}, "NEW_PLAYERS_PER_DAY");
private AnalysisKeys() {
/* Static variable class */
}
}

View File

@ -0,0 +1,53 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.container.Ping;
import com.djrapitops.plan.data.container.PlayerDeath;
import com.djrapitops.plan.data.container.PlayerKill;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.PlaceholderKey;
import com.djrapitops.plan.data.store.Type;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.data.store.mutators.TPSMutator;
import com.djrapitops.plan.data.time.WorldTimes;
import java.util.*;
/**
* Class holding Key objects that are commonly used across multiple DataContainers.
*
* @author Rsl1122
*/
public class CommonKeys {
private CommonKeys() {
/* Static variable class */
}
public static final Key<UUID> UUID = new Key<>(UUID.class, "uuid");
public static final Key<UUID> SERVER_UUID = new Key<>(UUID.class, "server_uuid");
public static final Key<String> NAME = new Key<>(String.class, "name");
public static final PlaceholderKey<Long> REGISTERED = new PlaceholderKey<>(Long.class, "registered");
public static final Key<List<Ping>> PING = new Key<>(new Type<List<Ping>>() {}, "ping");
public static final Key<List<Session>> SESSIONS = new Key<>(new Type<List<Session>>() {}, "sessions");
public static final Key<WorldTimes> WORLD_TIMES = new Key<>(WorldTimes.class, "world_times");
public static final PlaceholderKey<Long> LAST_SEEN = new PlaceholderKey<>(Long.class, "lastSeen");
public static final Key<List<PlayerDeath>> PLAYER_DEATHS = new Key<>(new Type<List<PlayerDeath>>() {}, "player_deaths");
public static final Key<List<PlayerKill>> PLAYER_KILLS = new Key<>(new Type<List<PlayerKill>>() {}, "player_kills");
public static final Key<Integer> PLAYER_KILL_COUNT = new Key<>(Integer.class, "player_kill_count");
public static final Key<Integer> MOB_KILL_COUNT = new Key<>(Integer.class, "mob_kill_count");
public static final Key<Integer> DEATH_COUNT = new Key<>(Integer.class, "death_count");
public static final Key<Boolean> BANNED = new Key<>(Boolean.class, "banned");
public static final Key<Boolean> OPERATOR = new Key<>(Boolean.class, "operator");
public static final Key<SessionsMutator> SESSIONS_MUTATOR = new Key<>(SessionsMutator.class, "SESSIONS_MUTATOR");
public static final Key<TPSMutator> TPS_MUTATOR = new Key<>(TPSMutator.class, "TPS_MUTATOR");
public static final Key<PlayersMutator> PLAYERS_MUTATOR = new Key<>(PlayersMutator.class, "PLAYERS_MUTATOR");
public static final Key<TreeMap<Long, Map<String, Set<UUID>>>> ACTIVITY_DATA = new Key<>(new Type<TreeMap<Long, Map<String, Set<UUID>>>>() {}, "ACTIVITY_DATA");
}

View File

@ -0,0 +1,48 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.store.PlaceholderKey;
/**
* Similar to {@link CommonKeys}, but for {@link com.djrapitops.plan.data.store.PlaceholderKey}s.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.PlaceholderKey for placeholder information
*/
class CommonPlaceholderKeys {
static final PlaceholderKey<String> VERSION = new PlaceholderKey<>(String.class, "version");
static final PlaceholderKey<Integer> TIME_ZONE = new PlaceholderKey<>(Integer.class, "timeZone");
static final PlaceholderKey<String> PLAYERS_GRAPH_COLOR = new PlaceholderKey<>(String.class, "playersGraphColor");
static final PlaceholderKey<String> PLAYERS_ONLINE_SERIES = new PlaceholderKey<>(String.class, "playersOnlineSeries");
static final PlaceholderKey<String> WORLD_MAP_HIGH_COLOR = new PlaceholderKey<>(String.class, "worldMapColHigh");
static final PlaceholderKey<String> WORLD_MAP_LOW_COLOR = new PlaceholderKey<>(String.class, "worldMapColLow");
static final PlaceholderKey<Integer> PLAYERS_ONLINE = new PlaceholderKey<>(Integer.class, "playersOnline");
static final PlaceholderKey<Integer> PLAYERS_TOTAL = new PlaceholderKey<>(Integer.class, "playersTotal");
static final PlaceholderKey<String> WORLD_MAP_SERIES = new PlaceholderKey<>(String.class, "geoMapSeries");
static final PlaceholderKey<String> ACTIVITY_STACK_SERIES = new PlaceholderKey<>(String.class, "activityStackSeries");
static final PlaceholderKey<String> ACTIVITY_STACK_CATEGORIES = new PlaceholderKey<>(String.class, "activityStackCategories");
static final PlaceholderKey<String> ACTIVITY_PIE_SERIES = new PlaceholderKey<>(String.class, "activityPieSeries");
public static final PlaceholderKey<String> COUNTRY_CATEGORIES = new PlaceholderKey<>(String.class, "countryCategories");
public static final PlaceholderKey<String> COUNTRY_SERIES = new PlaceholderKey<>(String.class, "countrySeries");
static final PlaceholderKey<String> HEALTH_NOTES = new PlaceholderKey<>(String.class, "healthNotes");
static final PlaceholderKey<Double> HEALTH_INDEX = new PlaceholderKey<>(Double.class, "healthIndex");
static final PlaceholderKey<Integer> PLAYERS_DAY = new PlaceholderKey<>(Integer.class, "playersDay");
static final PlaceholderKey<Integer> PLAYERS_WEEK = new PlaceholderKey<>(Integer.class, "playersWeek");
static final PlaceholderKey<Integer> PLAYERS_MONTH = new PlaceholderKey<>(Integer.class, "playersMonth");
static final PlaceholderKey<Integer> PLAYERS_NEW_DAY = new PlaceholderKey<>(Integer.class, "playersNewDay");
static final PlaceholderKey<Integer> PLAYERS_NEW_WEEK = new PlaceholderKey<>(Integer.class, "playersNewWeek");
static final PlaceholderKey<Integer> PLAYERS_NEW_MONTH = new PlaceholderKey<>(Integer.class, "playersNewMonth");
static final PlaceholderKey<String> REFRESH_TIME_F = new PlaceholderKey<>(String.class, "refresh");
static final PlaceholderKey<String> LAST_PEAK_TIME_F = new PlaceholderKey<>(String.class, "lastPeakTime");
static final PlaceholderKey<String> ALL_TIME_PEAK_TIME_F = new PlaceholderKey<>(String.class, "bestPeakTime");
static final PlaceholderKey<String> PLAYERS_LAST_PEAK = new PlaceholderKey<>(String.class, "playersLastPeak");
static final PlaceholderKey<String> PLAYERS_ALL_TIME_PEAK = new PlaceholderKey<>(String.class, "playersBestPeak");
private CommonPlaceholderKeys() {
/* static variable class */
}
}

View File

@ -0,0 +1,63 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.PlaceholderKey;
import com.djrapitops.plan.data.store.Type;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.system.info.server.Server;
import java.util.*;
/**
* Key objects for {@link com.djrapitops.plan.data.store.containers.NetworkContainer}.
*
* @author Rsl1122
* @see com.djrapitops.plan.data.store.containers.NetworkContainer for DataContainer.
*/
public class NetworkKeys {
public static final PlaceholderKey<String> VERSION = CommonPlaceholderKeys.VERSION;
public static final PlaceholderKey<String> NETWORK_NAME = new PlaceholderKey<>(String.class, "networkName");
public static final PlaceholderKey<Integer> TIME_ZONE = CommonPlaceholderKeys.TIME_ZONE;
public static final PlaceholderKey<Integer> PLAYERS_ONLINE = CommonPlaceholderKeys.PLAYERS_ONLINE;
public static final PlaceholderKey<Integer> PLAYERS_TOTAL = CommonPlaceholderKeys.PLAYERS_TOTAL;
public static final PlaceholderKey<String> PLAYERS_GRAPH_COLOR = CommonPlaceholderKeys.PLAYERS_GRAPH_COLOR;
public static final PlaceholderKey<String> WORLD_MAP_HIGH_COLOR = CommonPlaceholderKeys.WORLD_MAP_HIGH_COLOR;
public static final PlaceholderKey<String> WORLD_MAP_LOW_COLOR = CommonPlaceholderKeys.WORLD_MAP_LOW_COLOR;
public static final PlaceholderKey<String> REFRESH_TIME_F = CommonPlaceholderKeys.REFRESH_TIME_F;
public static final PlaceholderKey<String> RECENT_PEAK_TIME_F = CommonPlaceholderKeys.LAST_PEAK_TIME_F;
public static final PlaceholderKey<String> ALL_TIME_PEAK_TIME_F = CommonPlaceholderKeys.ALL_TIME_PEAK_TIME_F;
public static final PlaceholderKey<String> PLAYERS_RECENT_PEAK = CommonPlaceholderKeys.PLAYERS_LAST_PEAK;
public static final PlaceholderKey<String> PLAYERS_ALL_TIME_PEAK = CommonPlaceholderKeys.PLAYERS_ALL_TIME_PEAK;
public static final PlaceholderKey<Integer> PLAYERS_DAY = CommonPlaceholderKeys.PLAYERS_DAY;
public static final PlaceholderKey<Integer> PLAYERS_WEEK = CommonPlaceholderKeys.PLAYERS_WEEK;
public static final PlaceholderKey<Integer> PLAYERS_MONTH = CommonPlaceholderKeys.PLAYERS_MONTH;
public static final PlaceholderKey<Integer> PLAYERS_NEW_DAY = CommonPlaceholderKeys.PLAYERS_NEW_DAY;
public static final PlaceholderKey<Integer> PLAYERS_NEW_WEEK = CommonPlaceholderKeys.PLAYERS_NEW_WEEK;
public static final PlaceholderKey<Integer> PLAYERS_NEW_MONTH = CommonPlaceholderKeys.PLAYERS_NEW_MONTH;
public static final PlaceholderKey<String> WORLD_MAP_SERIES = CommonPlaceholderKeys.WORLD_MAP_SERIES;
public static final PlaceholderKey<String> PLAYERS_ONLINE_SERIES = CommonPlaceholderKeys.PLAYERS_ONLINE_SERIES;
public static final PlaceholderKey<String> ACTIVITY_STACK_SERIES = CommonPlaceholderKeys.ACTIVITY_STACK_SERIES;
public static final PlaceholderKey<String> ACTIVITY_STACK_CATEGORIES = CommonPlaceholderKeys.ACTIVITY_STACK_CATEGORIES;
public static final PlaceholderKey<String> ACTIVITY_PIE_SERIES = CommonPlaceholderKeys.ACTIVITY_PIE_SERIES;
public static final PlaceholderKey<String> COUNTRY_CATEGORIES = CommonPlaceholderKeys.COUNTRY_CATEGORIES;
public static final PlaceholderKey<String> COUNTRY_SERIES = CommonPlaceholderKeys.COUNTRY_SERIES;
public static final PlaceholderKey<Double> HEALTH_INDEX = CommonPlaceholderKeys.HEALTH_INDEX;
public static final PlaceholderKey<String> HEALTH_NOTES = CommonPlaceholderKeys.HEALTH_NOTES;
public static final Key<Long> REFRESH_TIME = new Key<>(Long.class, "REFRESH_TIME");
public static final Key<Long> REFRESH_TIME_DAY_AGO = new Key<>(Long.class, "REFRESH_TIME_DAY_AGO");
public static final Key<Long> REFRESH_TIME_WEEK_AGO = new Key<>(Long.class, "REFRESH_TIME_WEEK_AGO");
public static final Key<Long> REFRESH_TIME_MONTH_AGO = new Key<>(Long.class, "REFRESH_TIME_MONTH_AGO");
public static final Key<PlayersMutator> PLAYERS_MUTATOR = CommonKeys.PLAYERS_MUTATOR;
public static final Key<Collection<Server>> BUKKIT_SERVERS = new Key<>(new Type<Collection<Server>>() {}, "BUKKIT_SERVERS");
public static final Key<TreeMap<Long, Map<String, Set<UUID>>>> ACTIVITY_DATA = CommonKeys.ACTIVITY_DATA;
private NetworkKeys() {
/* static variable class */
}
}

View File

@ -0,0 +1,42 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.container.Ping;
import com.djrapitops.plan.data.container.PlayerDeath;
import com.djrapitops.plan.data.container.PlayerKill;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.containers.PerServerContainer;
import com.djrapitops.plan.data.time.WorldTimes;
import java.util.List;
/**
* Key objects for PerServerContainer container.
*
* @author Rsl1122
* @see com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps For Suppliers for each key
* @see PerServerContainer For the DataContainer.
*/
public class PerServerKeys {
private PerServerKeys() {
/* Static variable class */
}
public static final Key<Long> REGISTERED = CommonKeys.REGISTERED;
public static final Key<List<Ping>> PING = CommonKeys.PING;
public static final Key<List<Session>> SESSIONS = CommonKeys.SESSIONS;
public static final Key<WorldTimes> WORLD_TIMES = CommonKeys.WORLD_TIMES;
public static final Key<List<PlayerKill>> PLAYER_KILLS = CommonKeys.PLAYER_KILLS;
public static final Key<List<PlayerDeath>> PLAYER_DEATHS = CommonKeys.PLAYER_DEATHS;
public static final Key<Integer> PLAYER_KILL_COUNT = CommonKeys.PLAYER_KILL_COUNT;
public static final Key<Integer> MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT;
public static final Key<Integer> DEATH_COUNT = CommonKeys.DEATH_COUNT;
public static final Key<Long> LAST_SEEN = CommonKeys.LAST_SEEN;
public static final Key<Boolean> BANNED = CommonKeys.BANNED;
public static final Key<Boolean> OPERATOR = CommonKeys.OPERATOR;
}

View File

@ -0,0 +1,52 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.container.*;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.PlaceholderKey;
import com.djrapitops.plan.data.store.Type;
import com.djrapitops.plan.data.store.containers.PerServerContainer;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.data.time.WorldTimes;
import java.util.List;
import java.util.UUID;
/**
* Class that holds Key objects for PlayerContainer.
*
* @author Rsl1122
* @see com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps For Suppliers for each key
* @see com.djrapitops.plan.data.store.containers.PlayerContainer For DataContainer.
*/
public class PlayerKeys {
private PlayerKeys() {
/* Static variable class */
}
public static final Key<UUID> UUID = CommonKeys.UUID;
public static final Key<String> NAME = CommonKeys.NAME;
public static final Key<List<Nickname>> NICKNAMES = new Key<>(new Type<List<Nickname>>() {}, "nicknames");
public static final PlaceholderKey<Long> REGISTERED = CommonKeys.REGISTERED;
public static final Key<Integer> KICK_COUNT = new Key<>(Integer.class, "kick_count");
public static final Key<List<GeoInfo>> GEO_INFO = new Key<>(new Type<List<GeoInfo>>() {}, "geo_info");
public static final Key<List<Ping>> PING = CommonKeys.PING;
public static final Key<Session> ACTIVE_SESSION = new Key<>(Session.class, "active_session");
public static final Key<List<Session>> SESSIONS = CommonKeys.SESSIONS;
public static final Key<WorldTimes> WORLD_TIMES = CommonKeys.WORLD_TIMES;
public static final Key<List<PlayerKill>> PLAYER_KILLS = CommonKeys.PLAYER_KILLS;
public static final Key<List<PlayerDeath>> PLAYER_DEATHS = CommonKeys.PLAYER_DEATHS;
public static final Key<Integer> PLAYER_KILL_COUNT = CommonKeys.PLAYER_KILL_COUNT;
public static final Key<Integer> MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT;
public static final Key<Integer> DEATH_COUNT = CommonKeys.DEATH_COUNT;
public static final Key<PerServerContainer> PER_SERVER = new Key<>(PerServerContainer.class, "per_server_data");
public static final PlaceholderKey<Long> LAST_SEEN = CommonKeys.LAST_SEEN;
public static final Key<Boolean> BANNED = CommonKeys.BANNED;
public static final Key<Boolean> OPERATOR = CommonKeys.OPERATOR;
}

View File

@ -0,0 +1,50 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.container.Ping;
import com.djrapitops.plan.data.container.PlayerKill;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.Type;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.data.time.WorldTimes;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Keys for the ServerContainer.
*
* @author Rsl1122
* @see com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps For Suppliers for each key
* @see com.djrapitops.plan.data.store.containers.ServerContainer For DataContainer.
*/
public class ServerKeys {
private ServerKeys() {
/* Static variable class */
}
public static final Key<UUID> SERVER_UUID = CommonKeys.SERVER_UUID;
public static final Key<String> NAME = CommonKeys.NAME;
public static final Key<List<PlayerContainer>> PLAYERS = new Key<>(new Type<List<PlayerContainer>>() {}, "players");
public static final Key<List<PlayerContainer>> OPERATORS = new Key<>(new Type<List<PlayerContainer>>() {}, "operators");
public static final Key<Integer> PLAYER_COUNT = new Key<>(Integer.class, "player_count");
public static final Key<List<Session>> SESSIONS = CommonKeys.SESSIONS;
public static final Key<List<Ping>> PING = CommonKeys.PING;
public static final Key<WorldTimes> WORLD_TIMES = CommonKeys.WORLD_TIMES;
public static final Key<List<PlayerKill>> PLAYER_KILLS = CommonKeys.PLAYER_KILLS;
public static final Key<Integer> PLAYER_KILL_COUNT = CommonKeys.PLAYER_KILL_COUNT;
public static final Key<Integer> MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT;
public static final Key<Integer> DEATH_COUNT = CommonKeys.DEATH_COUNT;
public static final Key<List<TPS>> TPS = new Key<>(new Type<List<TPS>>() {}, "tps");
public static final Key<DateObj<Integer>> ALL_TIME_PEAK_PLAYERS = new Key<>(new Type<DateObj<Integer>>() {}, "all_time_peak_players");
public static final Key<DateObj<Integer>> RECENT_PEAK_PLAYERS = new Key<>(new Type<DateObj<Integer>>() {}, "recent_peak_players");
public static final Key<Map<String, Integer>> COMMAND_USAGE = new Key<>(new Type<Map<String, Integer>>() {}, "command_usage");
}

View File

@ -0,0 +1,39 @@
package com.djrapitops.plan.data.store.keys;
import com.djrapitops.plan.data.container.PlayerDeath;
import com.djrapitops.plan.data.container.PlayerKill;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.time.WorldTimes;
import java.util.List;
import java.util.UUID;
/**
* Class holding Key objects for Session (DataContainer).
*
* @author Rsl1122
* @see com.djrapitops.plan.data.container.Session for DataContainer.
*/
public class SessionKeys {
public static final Key<Integer> DB_ID = new Key<>(Integer.class, "db_id");
public static final Key<UUID> UUID = CommonKeys.UUID;
public static final Key<UUID> SERVER_UUID = CommonKeys.SERVER_UUID;
public static final Key<Long> START = new Key<>(Long.class, "start");
public static final Key<Long> END = new Key<>(Long.class, "end");
public static final Key<Long> LENGTH = new Key<>(Long.class, "length");
public static final Key<Long> AFK_TIME = new Key<>(Long.class, "afk_time");
public static final Key<Long> ACTIVE_TIME = new Key<>(Long.class, "active_time");
public static final Key<WorldTimes> WORLD_TIMES = CommonKeys.WORLD_TIMES;
public static final Key<List<PlayerKill>> PLAYER_KILLS = CommonKeys.PLAYER_KILLS;
public static final Key<Integer> PLAYER_KILL_COUNT = CommonKeys.PLAYER_KILL_COUNT;
public static final Key<Integer> MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT;
public static final Key<Integer> DEATH_COUNT = CommonKeys.DEATH_COUNT;
public static final Key<List<PlayerDeath>> PLAYER_DEATHS = CommonKeys.PLAYER_DEATHS;
private SessionKeys() {
/* Static variable class */
}
}

View File

@ -1,20 +1,21 @@
package com.djrapitops.plan.data.calculation;
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.PlayerProfile;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Optional;
public class ActivityIndex {
private final double value;
public ActivityIndex(PlayerProfile player, long date) {
value = calculate(player, date);
public ActivityIndex(DataContainer container, long date) {
value = calculate(container, date);
}
public static String[] getGroups() {
@ -29,7 +30,7 @@ public class ActivityIndex {
return value <= 0 ? 1 : value;
}
private double calculate(PlayerProfile player, long date) {
private double calculate(DataContainer container, long date) {
long week = TimeAmount.WEEK.ms();
long weekAgo = date - week;
long twoWeeksAgo = date - 2L * week;
@ -38,24 +39,33 @@ public class ActivityIndex {
long activePlayThreshold = loadSetting(Settings.ACTIVE_PLAY_THRESHOLD.getNumber() * TimeAmount.MINUTE.ms());
int activeLoginThreshold = loadSetting(Settings.ACTIVE_LOGIN_THRESHOLD.getNumber());
List<Session> sessionsWeek = player.getSessions(weekAgo, date).collect(Collectors.toList());
List<Session> sessionsWeek2 = player.getSessions(twoWeeksAgo, weekAgo).collect(Collectors.toList());
List<Session> sessionsWeek3 = player.getSessions(threeWeeksAgo, twoWeeksAgo).collect(Collectors.toList());
Optional<List<Session>> sessionsValue = container.getValue(PlayerKeys.SESSIONS);
if (!sessionsValue.isPresent()) {
return 0.0;
}
SessionsMutator sessionsMutator = new SessionsMutator(sessionsValue.get());
if (sessionsMutator.all().isEmpty()) {
return 0.0;
}
SessionsMutator weekOne = sessionsMutator.filterSessionsBetween(weekAgo, date);
SessionsMutator weekTwo = sessionsMutator.filterSessionsBetween(twoWeeksAgo, weekAgo);
SessionsMutator weekThree = sessionsMutator.filterSessionsBetween(threeWeeksAgo, twoWeeksAgo);
// Playtime per week multipliers, max out to avoid too high values.
double max = 4.0;
long playtimeWeek = PlayerProfile.getActivePlaytime(sessionsWeek.stream());
long playtimeWeek = weekOne.toActivePlaytime();
double weekPlay = (playtimeWeek * 1.0 / activePlayThreshold);
if (weekPlay > max) {
weekPlay = max;
}
long playtimeWeek2 = PlayerProfile.getActivePlaytime(sessionsWeek2.stream());
long playtimeWeek2 = weekTwo.toActivePlaytime();
double week2Play = (playtimeWeek2 * 1.0 / activePlayThreshold);
if (week2Play > max) {
week2Play = max;
}
long playtimeWeek3 = PlayerProfile.getActivePlaytime(sessionsWeek3.stream());
long playtimeWeek3 = weekThree.toActivePlaytime();
double week3Play = (playtimeWeek3 * 1.0 / activePlayThreshold);
if (week3Play > max) {
week3Play = max;
@ -79,9 +89,9 @@ public class ActivityIndex {
double playAvg = (weekPlay + week2Play + week3Play) / 3.0;
double weekLogin = sessionsWeek.size() >= activeLoginThreshold ? 1.0 : 0.5;
double week2Login = sessionsWeek2.size() >= activeLoginThreshold ? 1.0 : 0.5;
double week3Login = sessionsWeek3.size() >= activeLoginThreshold ? 1.0 : 0.5;
double weekLogin = weekOne.count() >= activeLoginThreshold ? 1.0 : 0.5;
double week2Login = weekTwo.count() >= activeLoginThreshold ? 1.0 : 0.5;
double week3Login = weekThree.count() >= activeLoginThreshold ? 1.0 : 0.5;
double loginMultiplier = 1.0;
double loginTotal = weekLogin + week2Login + week3Login;

View File

@ -0,0 +1,35 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.keys.ServerKeys;
import java.util.HashMap;
import java.util.Map;
/**
* Mutator for Command Usage Map objects.
* <p>
* Can be used to easily get different values about the map.
*
* @author Rsl1122
*/
public class CommandUseMutator {
private Map<String, Integer> commandUsage;
public CommandUseMutator(Map<String, Integer> commandUsage) {
this.commandUsage = commandUsage;
}
public static CommandUseMutator forContainer(DataContainer container) {
return new CommandUseMutator(container.getValue(ServerKeys.COMMAND_USAGE).orElse(new HashMap<>()));
}
public int commandUsageCount() {
int total = 0;
for (Integer value : commandUsage.values()) {
total += value;
}
return total;
}
}

View File

@ -0,0 +1,57 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
public class DateHoldersMutator<T extends DateHolder> {
private final List<T> dateHolders;
public DateHoldersMutator(List<T> dateHolders) {
this.dateHolders = dateHolders;
}
public TreeMap<Long, List<T>> groupByStartOfMinute() {
return groupByStartOfSections(TimeAmount.MINUTE.ms());
}
private TreeMap<Long, List<T>> groupByStartOfSections(long sectionLength) {
TreeMap<Long, List<T>> map = new TreeMap<>();
if (dateHolders.isEmpty()) {
return map;
}
for (T holder : dateHolders) {
long date = holder.getDate();
long startOfMinute = date - (date % sectionLength);
List<T> list = map.getOrDefault(startOfMinute, new ArrayList<>());
list.add(holder);
map.put(startOfMinute, list);
}
return map;
}
public TreeMap<Long, List<T>> groupByStartOfDay() {
long twentyFourHours = 24L * TimeAmount.HOUR.ms();
TreeMap<Long, List<T>> map = groupByStartOfSections(twentyFourHours);
// Empty map firstKey attempt causes NPE if not checked.
if (!map.isEmpty()) {
// Add missing in-between dates
long start = map.firstKey();
long now = System.currentTimeMillis();
long end = now - (now % twentyFourHours);
for (long date = map.firstKey(); date < end; date += twentyFourHours) {
map.putIfAbsent(date, new ArrayList<>());
}
}
return map;
}
}

View File

@ -0,0 +1,42 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.utilities.comparators.GeoInfoComparator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
/**
* Mutator for lists of GeoInfo objects.
*
* @author Rsl1122
* @see GeoInfo for the object.
*/
public class GeoInfoMutator {
private final List<GeoInfo> geoInfo;
public static GeoInfoMutator forContainer(DataContainer container) {
return new GeoInfoMutator(container.getValue(PlayerKeys.GEO_INFO).orElse(new ArrayList<>()));
}
public GeoInfoMutator(List<GeoInfo> geoInfo) {
this.geoInfo = geoInfo;
}
public GeoInfoMutator forCollection(Collection<GeoInfo> collection) {
return new GeoInfoMutator(new ArrayList<>(collection));
}
public Optional<GeoInfo> mostRecent() {
if (geoInfo.isEmpty()) {
return Optional.empty();
}
geoInfo.sort(new GeoInfoComparator());
return Optional.of(geoInfo.get(0));
}
}

View File

@ -0,0 +1,24 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.utilities.html.graphs.line.Point;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.stream.Collectors;
public class MutatorFunctions {
public static List<Point> toPoints(NavigableMap<Long, Integer> map) {
return map.entrySet().stream()
.map(entry -> new Point(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
public static int average(Map<Long, Integer> map) {
return (int) map.values().stream()
.mapToInt(i -> i)
.average().orElse(0);
}
}

View File

@ -0,0 +1,48 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.containers.PerServerContainer;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.keys.CommonKeys;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import java.util.*;
public class NetworkPerServerMutator {
private final Map<UUID, List<DataContainer>> perServerContainers;
public NetworkPerServerMutator(PlayersMutator playersMutator) {
this.perServerContainers = perServerContainers(playersMutator);
}
public static NetworkPerServerMutator forContainer(DataContainer container) {
return new NetworkPerServerMutator(
container.getValue(CommonKeys.PLAYERS_MUTATOR)
.orElse(PlayersMutator.forContainer(container))
);
}
public Map<UUID, List<DataContainer>> getPerServerContainers() {
return perServerContainers;
}
private Map<UUID, List<DataContainer>> perServerContainers(PlayersMutator playersMutator) {
Map<UUID, List<DataContainer>> dataContainerMap = new HashMap<>();
for (PlayerContainer playerContainer : playersMutator.all()) {
UUID uuid = playerContainer.getUnsafe(PlayerKeys.UUID);
PerServerContainer perServerContainer = playerContainer.getValue(PlayerKeys.PER_SERVER).orElse(new PerServerContainer());
for (Map.Entry<UUID, DataContainer> entry : perServerContainer.entrySet()) {
UUID serverUUID = entry.getKey();
DataContainer container = entry.getValue();
container.putRawData(PlayerKeys.UUID, uuid);
List<DataContainer> dataContainers = dataContainerMap.getOrDefault(serverUUID, new ArrayList<>());
dataContainers.add(container);
dataContainerMap.put(serverUUID, dataContainers);
}
}
return dataContainerMap;
}
}

View File

@ -0,0 +1,100 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.containers.PerServerContainer;
import com.djrapitops.plan.data.store.keys.PerServerKeys;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.data.time.WorldTimes;
import java.util.*;
import java.util.stream.Collectors;
/**
* Mutator for PerServerContainer object.
*
* @author Rsl1122
*/
public class PerServerMutator {
private final PerServerContainer data;
public PerServerMutator(PerServerContainer data) {
this.data = data;
}
public static PerServerMutator forContainer(DataContainer container) {
return new PerServerMutator(container.getValue(PlayerKeys.PER_SERVER).orElse(new PerServerContainer()));
}
public List<Session> flatMapSessions() {
return data.values().stream()
.filter(container -> container.supports(PerServerKeys.SESSIONS))
.map(container -> container.getUnsafe(PerServerKeys.SESSIONS))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
public WorldTimes flatMapWorldTimes() {
WorldTimes total = new WorldTimes(new HashMap<>());
for (DataContainer container : data.values()) {
if (container.supports(PerServerKeys.WORLD_TIMES)) {
WorldTimes worldTimes = container.getUnsafe(PerServerKeys.WORLD_TIMES);
total.add(worldTimes);
}
}
return total;
}
public Map<UUID, WorldTimes> worldTimesPerServer() {
Map<UUID, WorldTimes> timesMap = new HashMap<>();
for (Map.Entry<UUID, DataContainer> entry : data.entrySet()) {
DataContainer container = entry.getValue();
timesMap.put(entry.getKey(), container.getValue(PerServerKeys.WORLD_TIMES).orElse(new WorldTimes(new HashMap<>())));
}
return timesMap;
}
public UUID favoriteServer() {
long max = 0;
UUID maxServer = null;
for (Map.Entry<UUID, DataContainer> entry : data.entrySet()) {
long total = SessionsMutator.forContainer(entry.getValue()).toPlaytime();
if (total > max) {
max = total;
maxServer = entry.getKey();
}
}
return maxServer;
}
public Map<UUID, List<Session>> sessionsPerServer() {
Map<UUID, List<Session>> sessionMap = new HashMap<>();
for (Map.Entry<UUID, DataContainer> entry : data.entrySet()) {
sessionMap.put(entry.getKey(), entry.getValue().getValue(PerServerKeys.SESSIONS).orElse(new ArrayList<>()));
}
return sessionMap;
}
public boolean isBanned() {
for (DataContainer container : data.values()) {
if (container.getValue(PlayerKeys.BANNED).orElse(false)) {
return true;
}
}
return false;
}
public boolean isOperator() {
for (DataContainer container : data.values()) {
if (container.getValue(PlayerKeys.OPERATOR).orElse(false)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,86 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.container.Ping;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.keys.CommonKeys;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PingMutator {
private final List<Ping> pings;
public PingMutator(List<Ping> pings) {
this.pings = pings;
}
public static PingMutator forContainer(DataContainer container) {
return new PingMutator(container.getValue(CommonKeys.PING).orElse(new ArrayList<>()));
}
public PingMutator filterBy(Predicate<Ping> predicate) {
return new PingMutator(pings.stream().filter(predicate).collect(Collectors.toList()));
}
public PingMutator filterByServer(UUID serverUUID) {
return filterBy(ping -> serverUUID.equals(ping.getServerUUID()));
}
public PingMutator mutateToByMinutePings() {
DateHoldersMutator<Ping> dateMutator = new DateHoldersMutator<>(pings);
TreeMap<Long, List<Ping>> byStartOfMinute = dateMutator.groupByStartOfMinute();
return new PingMutator(byStartOfMinute.entrySet().stream()
.map(entry -> {
PingMutator mutator = new PingMutator(entry.getValue());
return new Ping(entry.getKey(), null,
mutator.min(), mutator.max(), mutator.average());
}).collect(Collectors.toList()));
}
public List<Ping> all() {
return pings;
}
public int max() {
int max = -1;
for (Ping ping : pings) {
Integer value = ping.getMax();
if (value < 0) {
continue;
}
if (value > max) {
max = value;
}
}
return max;
}
public int min() {
int min = -1;
for (Ping ping : pings) {
Integer value = ping.getMin();
if (value < 0) {
continue;
}
if (value < min || min == -1) {
min = value;
}
}
return min;
}
public double average() {
return pings.stream().mapToDouble(Ping::getAverage)
.filter(value -> value >= 0)
.average().orElse(-1);
}
}

View File

@ -0,0 +1,251 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.container.Ping;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.data.store.keys.ServerKeys;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.utilities.analysis.AnalysisUtils;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Mutator for a bunch of {@link com.djrapitops.plan.data.store.containers.PlayerContainer}s.
*
* @author Rsl1122
*/
public class PlayersMutator {
private List<PlayerContainer> players;
public PlayersMutator(List<PlayerContainer> players) {
this.players = players;
}
public static PlayersMutator copyOf(PlayersMutator mutator) {
return new PlayersMutator(new ArrayList<>(mutator.players));
}
public static PlayersMutator forContainer(DataContainer container) {
return new PlayersMutator(container.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>()));
}
public <T extends Predicate<PlayerContainer>> PlayersMutator filterBy(T by) {
return new PlayersMutator(players.stream().filter(by).collect(Collectors.toList()));
}
public PlayersMutator filterPlayedBetween(long after, long before) {
return filterBy(
player -> player.getValue(PlayerKeys.SESSIONS)
.map(sessions -> sessions.stream().anyMatch(session -> {
long start = session.getValue(SessionKeys.START).orElse(-1L);
long end = session.getValue(SessionKeys.END).orElse(-1L);
return (after <= start && start <= before) || (after <= end && end <= before);
})).orElse(false)
);
}
public PlayersMutator filterRegisteredBetween(long after, long before) {
return filterBy(
player -> player.getValue(PlayerKeys.REGISTERED)
.map(date -> after <= date && date <= before).orElse(false)
);
}
public PlayersMutator filterRetained(long after, long before) {
return filterBy(
player -> {
long backLimit = Math.max(after, player.getValue(PlayerKeys.REGISTERED).orElse(0L));
long half = backLimit + ((before - backLimit) / 2L);
SessionsMutator sessionsMutator = SessionsMutator.forContainer(player);
return sessionsMutator.playedBetween(backLimit, half) &&
sessionsMutator.playedBetween(half, before);
}
);
}
public PlayersMutator filterActive(long date, double limit) {
return filterBy(player -> player.getActivityIndex(date).getValue() >= limit);
}
public PlayersMutator filterPlayedOnServer(UUID serverUUID) {
return filterBy(player -> !SessionsMutator.forContainer(player)
.filterPlayedOnServer(serverUUID)
.all().isEmpty()
);
}
public List<PlayerContainer> all() {
return players;
}
public List<Long> registerDates() {
List<Long> registerDates = new ArrayList<>();
for (PlayerContainer player : players) {
registerDates.add(player.getValue(PlayerKeys.REGISTERED).orElse(-1L));
}
return registerDates;
}
public List<String> getGeolocations() {
List<String> geolocations = new ArrayList<>();
for (PlayerContainer player : players) {
Optional<GeoInfo> mostRecent = GeoInfoMutator.forContainer(player).mostRecent();
geolocations.add(mostRecent.map(GeoInfo::getGeolocation).orElse("Unknown"));
}
return geolocations;
}
public Map<String, List<Ping>> getPingPerCountry(UUID serverUUID) {
Map<String, List<Ping>> pingPerCountry = new HashMap<>();
for (PlayerContainer player : players) {
Optional<GeoInfo> mostRecent = GeoInfoMutator.forContainer(player).mostRecent();
if (!mostRecent.isPresent()) {
continue;
}
List<Ping> pings = player.getValue(PlayerKeys.PING).orElse(new ArrayList<>());
String country = mostRecent.get().getGeolocation();
List<Ping> countryPings = pingPerCountry.getOrDefault(country, new ArrayList<>());
pings.stream()
.filter(ping -> ping.getServerUUID().equals(serverUUID))
.forEach(countryPings::add);
pingPerCountry.put(country, countryPings);
}
return pingPerCountry;
}
public TreeMap<Long, Map<String, Set<UUID>>> toActivityDataMap(long date) {
TreeMap<Long, Map<String, Set<UUID>>> activityData = new TreeMap<>();
for (long time = date; time >= date - TimeAmount.MONTH.ms() * 2L; time -= TimeAmount.WEEK.ms()) {
Map<String, Set<UUID>> map = activityData.getOrDefault(time, new HashMap<>());
if (!players.isEmpty()) {
for (PlayerContainer player : players) {
if (player.getValue(PlayerKeys.REGISTERED).orElse(0L) > time) {
continue;
}
ActivityIndex activityIndex = player.getActivityIndex(time);
String activityGroup = activityIndex.getGroup();
Set<UUID> uuids = map.getOrDefault(activityGroup, new HashSet<>());
uuids.add(player.getUnsafe(PlayerKeys.UUID));
map.put(activityGroup, uuids);
}
}
activityData.put(time, map);
}
return activityData;
}
public int count() {
return players.size();
}
public int averageNewPerDay() {
return MutatorFunctions.average(newPerDay());
}
public TreeMap<Long, Integer> newPerDay() {
List<DateObj> registerDates = registerDates().stream()
.map(value -> new DateObj<>(value, value))
.collect(Collectors.toList());
TreeMap<Long, List<DateObj>> byDay = new DateHoldersMutator<>(registerDates).groupByStartOfDay();
TreeMap<Long, Integer> byDayCounts = new TreeMap<>();
for (Map.Entry<Long, List<DateObj>> entry : byDay.entrySet()) {
byDayCounts.put(entry.getKey(), entry.getValue().size());
}
return byDayCounts;
}
/**
* Compares players in the mutator to other players in terms of player retention.
*
* @param compareTo Players to compare to.
* @param dateLimit Epoch ms back limit, if the player registered after this their value is not used.
* @return Mutator containing the players that are considered to be retained.
* @throws IllegalStateException If all players are rejected due to dateLimit.
*/
public PlayersMutator compareAndFindThoseLikelyToBeRetained(Iterable<PlayerContainer> compareTo,
long dateLimit,
PlayersOnlineResolver onlineResolver) {
Collection<PlayerContainer> retainedAfterMonth = new ArrayList<>();
Collection<PlayerContainer> notRetainedAfterMonth = new ArrayList<>();
for (PlayerContainer player : players) {
long registered = player.getValue(PlayerKeys.REGISTERED).orElse(System.currentTimeMillis());
// Discard uncertain data
if (registered > dateLimit) {
continue;
}
long monthAfterRegister = registered + TimeAmount.MONTH.ms();
long half = registered + (TimeAmount.MONTH.ms() / 2L);
if (player.playedBetween(registered, half) && player.playedBetween(half, monthAfterRegister)) {
retainedAfterMonth.add(player);
} else {
notRetainedAfterMonth.add(player);
}
}
if (retainedAfterMonth.isEmpty() || notRetainedAfterMonth.isEmpty()) {
throw new IllegalStateException("No players to compare to after rejecting with dateLimit");
}
List<RetentionData> retained = retainedAfterMonth.stream()
.map(player -> new RetentionData(player, onlineResolver))
.collect(Collectors.toList());
List<RetentionData> notRetained = notRetainedAfterMonth.stream()
.map(player -> new RetentionData(player, onlineResolver))
.collect(Collectors.toList());
RetentionData avgRetained = AnalysisUtils.average(retained);
RetentionData avgNotRetained = AnalysisUtils.average(notRetained);
List<PlayerContainer> toBeRetained = new ArrayList<>();
for (PlayerContainer player : compareTo) {
RetentionData retentionData = new RetentionData(player, onlineResolver);
if (retentionData.distance(avgRetained) < retentionData.distance(avgNotRetained)) {
toBeRetained.add(player);
}
}
return new PlayersMutator(toBeRetained);
}
public List<Session> getSessions() {
return players.stream()
.map(player -> player.getValue(PlayerKeys.SESSIONS).orElse(new ArrayList<>()))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
public List<UUID> uuids() {
return players.stream()
.map(player -> player.getValue(PlayerKeys.UUID).orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
public List<PlayerContainer> operators() {
return players.stream()
.filter(player -> player.getValue(PlayerKeys.OPERATOR).orElse(false)).collect(Collectors.toList());
}
public List<Ping> pings() {
return players.stream()
.map(player -> player.getValue(PlayerKeys.PING).orElse(new ArrayList<>()))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,40 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.utilities.html.graphs.line.Point;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
/**
* Resolves dates into players online numbers with a help of a NavigableMap.
* <p>
* Time Complexity of O(n / 2) with the use of TreeMap.
*
* @author Rsl1122
*/
public class PlayersOnlineResolver extends TreeMap<Long, Integer> {
public PlayersOnlineResolver(TPSMutator mutator) {
List<Point> points = mutator.playersOnlinePoints();
for (Point point : points) {
double date = point.getX();
double value = point.getY();
put((long) date, (int) value);
}
}
public Optional<Integer> getOnlineOn(long date) {
Map.Entry<Long, Integer> entry = floorEntry(date);
if (entry == null) {
return Optional.empty();
}
return Optional.of(entry.getValue());
}
public boolean isServerOnline(long date, long timeLimit) {
Long lastEntry = floorKey(date);
return date - lastEntry < timeLimit;
}
}

View File

@ -0,0 +1,57 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.containers.DataContainer;
import java.util.List;
public class PvpInfoMutator {
private final SessionsMutator sessionsMutator;
private PvpInfoMutator(SessionsMutator sessionsMutator) {
this.sessionsMutator = sessionsMutator;
}
public PvpInfoMutator(List<Session> sessions) {
this(new SessionsMutator(sessions));
}
public static PvpInfoMutator forContainer(DataContainer container) {
return new PvpInfoMutator(SessionsMutator.forContainer(container));
}
public static PvpInfoMutator forMutator(SessionsMutator sessionsMutator) {
return new PvpInfoMutator(sessionsMutator);
}
public double killDeathRatio() {
int deathCount = sessionsMutator.toPlayerDeathCount();
return sessionsMutator.toPlayerKillCount() * 1.0 / (deathCount != 0 ? deathCount : 1);
}
public int mobCausedDeaths() {
return sessionsMutator.toDeathCount() - sessionsMutator.toPlayerDeathCount();
}
public double mobKillDeathRatio() {
int deathCount = mobCausedDeaths();
return sessionsMutator.toMobKillCount() * 1.0 / (deathCount != 0 ? deathCount : 1);
}
public int mobKills() {
return sessionsMutator.toMobKillCount();
}
public int playerKills() {
return sessionsMutator.toPlayerKillCount();
}
public int deaths() {
return sessionsMutator.toDeathCount();
}
public int playerCausedDeaths() {
return sessionsMutator.toPlayerDeathCount();
}
}

View File

@ -0,0 +1,71 @@
/*
* Licence is provided in the jar as license.yml also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
*/
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plugin.api.TimeAmount;
import com.google.common.base.Objects;
import java.util.Optional;
/**
* Utility class for player retention calculations.
* <p>
* Previously known as StickyData.
*
* @author Rsl1122
*/
public class RetentionData {
private final double activityIndex;
private double onlineOnJoin;
public RetentionData(double activityIndex, double onlineOnJoin) {
this.activityIndex = activityIndex;
this.onlineOnJoin = onlineOnJoin;
}
public RetentionData(PlayerContainer player, PlayersOnlineResolver onlineOnJoin) {
Optional<Long> registeredValue = player.getValue(PlayerKeys.REGISTERED);
activityIndex = registeredValue
.map(registered -> new ActivityIndex(player, registered + TimeAmount.DAY.ms()).getValue())
.orElse(0.0);
this.onlineOnJoin = registeredValue
.map(registered -> onlineOnJoin.getOnlineOn(registered).orElse(-1))
.orElse(0);
}
public double distance(RetentionData data) {
double num = 0;
num += Math.abs(data.activityIndex - activityIndex) * 2.0;
num += data.onlineOnJoin != -1 && onlineOnJoin != -1
? Math.abs(data.onlineOnJoin - onlineOnJoin) / 10.0
: 0;
return num;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RetentionData that = (RetentionData) o;
return Double.compare(that.activityIndex, activityIndex) == 0 &&
Objects.equal(onlineOnJoin, that.onlineOnJoin);
}
@Override
public int hashCode() {
return Objects.hashCode(activityIndex, onlineOnJoin);
}
public double getOnlineOnJoin() {
return onlineOnJoin;
}
public double getActivityIndex() {
return activityIndex;
}
}

View File

@ -0,0 +1,204 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.container.PlayerDeath;
import com.djrapitops.plan.data.container.PlayerKill;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.keys.CommonKeys;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.data.time.WorldTimes;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Mutator for a list of Sessions.
* <p>
* Can be used to get properties of a large number of sessions easily.
*
* @author Rsl1122
*/
public class SessionsMutator {
private List<Session> sessions;
public static SessionsMutator forContainer(DataContainer container) {
return new SessionsMutator(container.getValue(CommonKeys.SESSIONS).orElse(new ArrayList<>()));
}
public static SessionsMutator copyOf(SessionsMutator mutator) {
return new SessionsMutator(new ArrayList<>(mutator.sessions));
}
public SessionsMutator(List<Session> sessions) {
this.sessions = sessions;
}
public List<Session> all() {
return sessions;
}
public SessionsMutator filterBy(Predicate<Session> predicate) {
return new SessionsMutator(sessions.stream()
.filter(predicate)
.collect(Collectors.toList()));
}
public SessionsMutator filterSessionsBetween(long after, long before) {
return filterBy(getBetweenPredicate(after, before));
}
public SessionsMutator filterPlayedOnServer(UUID serverUUID) {
return filterBy(session ->
session.getValue(SessionKeys.SERVER_UUID)
.map(uuid -> uuid.equals(serverUUID))
.orElse(false)
);
}
public DateHoldersMutator<Session> toDateHoldersMutator() {
return new DateHoldersMutator<>(sessions);
}
public WorldTimes toTotalWorldTimes() {
WorldTimes total = new WorldTimes(new HashMap<>());
for (Session session : sessions) {
session.getValue(SessionKeys.WORLD_TIMES).ifPresent(total::add);
}
return total;
}
public List<PlayerKill> toPlayerKillList() {
return sessions.stream()
.map(session -> session.getValue(SessionKeys.PLAYER_KILLS).orElse(new ArrayList<>()))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
public List<PlayerDeath> toPlayerDeathList() {
return sessions.stream()
.map(session -> session.getValue(SessionKeys.PLAYER_DEATHS).orElse(new ArrayList<>()))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
public int toMobKillCount() {
return sessions.stream()
.mapToInt(session -> session.getValue(SessionKeys.MOB_KILL_COUNT).orElse(0))
.sum();
}
public int toDeathCount() {
return sessions.stream()
.mapToInt(session -> session.getValue(SessionKeys.DEATH_COUNT).orElse(0))
.sum();
}
public long toPlaytime() {
return sessions.stream()
.mapToLong(Session::getLength)
.sum();
}
public long toAfkTime() {
return sessions.stream()
.mapToLong(session -> session.getValue(SessionKeys.AFK_TIME).orElse(0L))
.sum();
}
public long toActivePlaytime() {
return sessions.stream()
.mapToLong(session -> session.getValue(SessionKeys.ACTIVE_TIME).orElse(0L))
.sum();
}
public long toLastSeen() {
return sessions.stream()
.mapToLong(session -> Math.max(session.getUnsafe(
SessionKeys.START),
session.getValue(SessionKeys.END).orElse(System.currentTimeMillis()))
).max().orElse(-1);
}
public long toLongestSessionLength() {
OptionalLong longestSession = sessions.stream().mapToLong(Session::getLength).max();
if (longestSession.isPresent()) {
return longestSession.getAsLong();
}
return -1;
}
public long toAverageSessionLength() {
OptionalDouble average = sessions.stream().map(Session::getLength)
.mapToLong(i -> i)
.average();
if (average.isPresent()) {
return (long) average.getAsDouble();
}
return 0L;
}
public long toMedianSessionLength() {
List<Long> sessionLengths = sessions.stream().map(Session::getLength)
.sorted()
.collect(Collectors.toList());
if (sessionLengths.isEmpty()) {
return 0;
}
return sessionLengths.get(sessionLengths.size() / 2);
}
public int toAverageUniqueJoinsPerDay() {
return MutatorFunctions.average(uniqueJoinsPerDay());
}
public TreeMap<Long, Integer> uniqueJoinsPerDay() {
TreeMap<Long, List<Session>> byStartOfDay = toDateHoldersMutator().groupByStartOfDay();
TreeMap<Long, Integer> uniqueJoins = new TreeMap<>();
for (Map.Entry<Long, List<Session>> entry : byStartOfDay.entrySet()) {
uniqueJoins.put(
entry.getKey(),
new SessionsMutator(entry.getValue()).toUniquePlayers()
);
}
return uniqueJoins;
}
public int toUniquePlayers() {
return (int) sessions.stream()
.map(session -> session.getUnsafe(SessionKeys.UUID))
.distinct()
.count();
}
public int count() {
return sessions.size();
}
public int toPlayerKillCount() {
return toPlayerKillList().size();
}
public boolean playedBetween(long after, long before) {
return sessions.stream().anyMatch(getBetweenPredicate(after, before));
}
private Predicate<Session> getBetweenPredicate(long after, long before) {
return session -> {
Long start = session.getUnsafe(SessionKeys.START);
Long end = session.getValue(SessionKeys.END).orElse(System.currentTimeMillis());
return (after <= start && start <= before) || (after <= end && end <= before);
};
}
public int toPlayerDeathCount() {
return toPlayerDeathList().size();
}
}

View File

@ -0,0 +1,221 @@
package com.djrapitops.plan.data.store.mutators;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.keys.ServerKeys;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.utilities.html.graphs.line.Point;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalDouble;
import java.util.stream.Collectors;
/**
* Mutator for a list of TPS data.
* <p>
* Can be used to get properties of a large number of TPS entries easily.
*
* @author Rsl1122
*/
public class TPSMutator {
private List<TPS> tpsData;
public TPSMutator(List<TPS> tpsData) {
this.tpsData = tpsData;
}
public static TPSMutator forContainer(DataContainer container) {
return new TPSMutator(container.getValue(ServerKeys.TPS).orElse(new ArrayList<>()));
}
public static TPSMutator copyOf(TPSMutator mutator) {
return new TPSMutator(new ArrayList<>(mutator.tpsData));
}
public TPSMutator filterDataBetween(long after, long before) {
return new TPSMutator(tpsData.stream()
.filter(tps -> tps.getDate() >= after && tps.getDate() <= before)
.collect(Collectors.toList()));
}
public List<TPS> all() {
return tpsData;
}
public List<Point> playersOnlinePoints() {
return tpsData.stream()
.map(tps -> new Point(tps.getDate(), tps.getPlayers()))
.collect(Collectors.toList());
}
public List<Point> tpsPoints() {
return tpsData.stream()
.map(tps -> new Point(tps.getDate(), tps.getTicksPerSecond()))
.collect(Collectors.toList());
}
public List<Point> cpuPoints() {
return tpsData.stream()
.map(tps -> new Point(tps.getDate(), tps.getCPUUsage()))
.collect(Collectors.toList());
}
public List<Point> ramUsagePoints() {
return tpsData.stream()
.map(tps -> new Point(tps.getDate(), tps.getUsedMemory()))
.collect(Collectors.toList());
}
public List<Point> entityPoints() {
return tpsData.stream()
.map(tps -> new Point(tps.getDate(), tps.getEntityCount()))
.collect(Collectors.toList());
}
public List<Point> chunkPoints() {
return tpsData.stream()
.map(tps -> new Point(tps.getDate(), tps.getChunksLoaded()))
.collect(Collectors.toList());
}
public long serverDownTime() {
long lastDate = -1;
long downTime = 0;
for (TPS tps : tpsData) {
long date = tps.getDate();
if (lastDate == -1) {
lastDate = date;
continue;
}
long diff = date - lastDate;
if (diff > TimeAmount.MINUTE.ms() * 3L) {
downTime += diff;
}
lastDate = date;
}
return downTime;
}
public long serverIdleTime() {
long lastDate = -1;
int lastPlayers = 0;
long idleTime = 0;
for (TPS tps : tpsData) {
long date = tps.getDate();
int players = tps.getPlayers();
if (lastDate == -1) {
lastDate = date;
lastPlayers = players;
continue;
}
long diff = date - lastDate;
if (lastPlayers == 0 && players == 0) {
idleTime += diff;
}
lastDate = date;
lastPlayers = players;
}
return idleTime;
}
public double percentageTPSAboveLowThreshold() {
if (tpsData.isEmpty()) {
return 1;
}
int threshold = Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber();
long count = 0;
for (TPS tps : tpsData) {
if (tps.getTicksPerSecond() >= threshold) {
count++;
}
}
return count * 1.0 / tpsData.size();
}
public int lowTpsSpikeCount() {
int mediumThreshold = Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber();
boolean wasLow = false;
int spikeCount = 0;
for (TPS tpsObj : tpsData) {
double tps = tpsObj.getTicksPerSecond();
if (tps < mediumThreshold) {
if (!wasLow) {
spikeCount++;
wasLow = true;
}
} else {
wasLow = false;
}
}
return spikeCount;
}
public double averageTPS() {
OptionalDouble average = tpsData.stream()
.mapToDouble(TPS::getTicksPerSecond)
.filter(num -> num >= 0)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double averageCPU() {
OptionalDouble average = tpsData.stream()
.mapToDouble(TPS::getCPUUsage)
.filter(num -> num >= 0)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double averageRAM() {
OptionalDouble average = tpsData.stream()
.mapToDouble(TPS::getUsedMemory)
.filter(num -> num >= 0)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double averageEntities() {
OptionalDouble average = tpsData.stream()
.mapToDouble(TPS::getEntityCount)
.filter(num -> num >= 0)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
public double averageChunks() {
OptionalDouble average = tpsData.stream()
.mapToDouble(TPS::getChunksLoaded)
.filter(num -> num >= 0)
.average();
if (average.isPresent()) {
return average.getAsDouble();
}
return -1;
}
}

View File

@ -0,0 +1,49 @@
package com.djrapitops.plan.data.store.mutators.combiners;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.containers.PerServerContainer;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.keys.PerServerKeys;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.data.store.keys.ServerKeys;
import com.djrapitops.plan.system.info.server.ServerInfo;
import java.util.*;
public class MultiBanCombiner {
private final DataContainer container;
/**
* Constructor.
*
* @param container DataContainer that supports {@link com.djrapitops.plan.data.store.keys.ServerKeys}.PLAYERS
*/
public MultiBanCombiner(DataContainer container) {
this.container = container;
}
public void combine(Set<UUID> banned) {
combine(Collections.singletonMap(ServerInfo.getServerUUID(), banned));
}
public void combine(Map<UUID, Set<UUID>> perServerBanned) {
List<PlayerContainer> playerContainers = container.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>());
for (Map.Entry<UUID, Set<UUID>> entry : perServerBanned.entrySet()) {
UUID serverUUID = entry.getKey();
Set<UUID> banned = entry.getValue();
for (PlayerContainer player : playerContainers) {
if (player.getValue(PlayerKeys.UUID).map(banned::contains).orElse(false)) {
PerServerContainer perServer = player.getValue(PlayerKeys.PER_SERVER)
.orElse(new PerServerContainer());
DataContainer perServerContainer = perServer.getOrDefault(serverUUID, new DataContainer());
perServerContainer.putRawData(PerServerKeys.BANNED, true);
perServer.put(serverUUID, perServerContainer);
}
}
}
}
}

View File

@ -0,0 +1,12 @@
package com.djrapitops.plan.data.store.mutators.formatting;
import java.util.function.Function;
/**
* Interface for formatting a value into a String.
*
* @author Rsl1122
*/
public interface Formatter<T> extends Function<T, String> {
}

View File

@ -0,0 +1,82 @@
package com.djrapitops.plan.data.store.mutators.formatting;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.Calendar;
import java.util.function.Function;
/**
* Class that holds static methods for getting new instances of different {@link Formatter}s.
*
* @author Rsl1122
*/
public class Formatters {
private Formatters() {
/* Static method class */
}
public static Formatter<DateHolder> year() {
return dateHolder -> {
long date = dateHolder.getDate();
return date > 0 ? FormatUtils.formatTimeStampYear(date) : "-";
};
}
public static Formatter<Long> yearLongValue() {
return date -> date > 0 ? FormatUtils.formatTimeStampYear(date) : "-";
}
public static Formatter<DateHolder> day() {
return dateHolder -> {
long date = dateHolder.getDate();
return date > 0 ? FormatUtils.formatTimeStampDay(date) : "-";
};
}
public static Formatter<DateHolder> second() {
return dateHolder -> {
long date = dateHolder.getDate();
return date > 0 ? FormatUtils.formatTimeStampSecond(date) : "-";
};
}
public static Formatter<DateHolder> clock() {
return dateHolder -> {
long date = dateHolder.getDate();
return date > 0 ? FormatUtils.formatTimeStampClock(date) : "-";
};
}
public static Formatter<DateHolder> iso8601NoClock() {
return dateHolder -> FormatUtils.formatTimeStampISO8601NoClock(dateHolder.getDate());
}
public static Formatter<Long> timeAmount() {
return new TimeAmountFormatter();
}
public static Function<Long, Integer> dayOfYear() {
return date -> {
Calendar day = Calendar.getInstance();
day.setTimeInMillis(date);
return day.get(Calendar.DAY_OF_YEAR);
};
}
public static Formatter<Double> percentage() {
return value -> value >= 0 ? FormatUtils.cutDecimals(value * 100.0) + "%" : "-";
}
public static Formatter<Long> benchmark() {
return ns -> {
if (ns > TimeAmount.MILLISECOND.ns() * 5L) {
return (ns / TimeAmount.MILLISECOND.ns()) + "ms";
} else {
return 1.0 * ns / TimeAmount.MILLISECOND.ns() + "ms";
}
};
}
}

View File

@ -0,0 +1,49 @@
package com.djrapitops.plan.data.store.mutators.formatting;
import com.djrapitops.plan.data.store.PlaceholderKey;
import com.djrapitops.plan.data.store.containers.DataContainer;
import org.apache.commons.text.StringSubstitutor;
import java.io.Serializable;
import java.util.HashMap;
/**
* Formatter for replacing ${placeholder} values inside strings.
*
* @author Rsl1122
*/
public class PlaceholderReplacer extends HashMap<String, Serializable> implements Formatter<String> {
public <T> void addPlaceholderFrom(DataContainer container, PlaceholderKey<T> key) {
if (!container.supports(key)) {
return;
}
put(key.getPlaceholder(), container.getSupplier(key).get().toString());
}
public void addAllPlaceholdersFrom(DataContainer container, PlaceholderKey... keys) {
for (PlaceholderKey key : keys) {
addPlaceholderFrom(container, key);
}
}
public <T> void addPlaceholderFrom(DataContainer container, Formatter<T> formatter, PlaceholderKey<T> key) {
if (!container.supports(key)) {
return;
}
put(key.getPlaceholder(), formatter.apply(container.getSupplier(key).get()));
}
public <T> void addAllPlaceholdersFrom(DataContainer container, Formatter<T> formatter, PlaceholderKey<T>... keys) {
for (PlaceholderKey<T> key : keys) {
addPlaceholderFrom(container, formatter, key);
}
}
@Override
public String apply(String string) {
StringSubstitutor sub = new StringSubstitutor(this);
sub.setEnableSubstitutionInVariables(true);
return sub.replace(string);
}
}

View File

@ -0,0 +1,131 @@
package com.djrapitops.plan.data.store.mutators.formatting;
import com.djrapitops.plan.system.settings.Settings;
import org.apache.commons.lang3.StringUtils;
/**
* Formatter for time amount in milliseconds.
*
* @author Rsl1122
*/
public class TimeAmountFormatter implements Formatter<Long> {
// Placeholders for the config settings
private final String zeroPH = "%zero%";
private final String secondsPH = "%seconds%";
private final String minutesPH = "%minutes%";
private final String hoursPH = "%hours%";
private final String daysPH = "%days%";
private final String monthsPH = "%months%";
private final String yearsPH = "%years%";
@Override
public String apply(Long ms) {
if (ms <= 0) {
return "-";
}
StringBuilder builder = new StringBuilder();
long x = ms / 1000;
long seconds = x % 60;
x /= 60;
long minutes = x % 60;
x /= 60;
long hours = x % 24;
x /= 24;
long days = x % 365;
long months = (days - days % 30) / 30;
days -= months * 30;
x /= 365;
long years = x;
appendYears(builder, years);
appendMonths(builder, months);
appendDays(builder, days);
String hourFormat = Settings.FORMAT_HOURS.toString();
String minuteFormat = Settings.FORMAT_MINUTES.toString();
String secondFormat = Settings.FORMAT_SECONDS.toString();
appendHours(builder, hours, hourFormat);
appendMinutes(builder, minutes, hours, hourFormat, minuteFormat);
appendSeconds(builder, seconds, minutes, hours, hourFormat, minuteFormat, secondFormat);
String formattedTime = StringUtils.remove(builder.toString(), zeroPH);
if (formattedTime.isEmpty()) {
return Settings.FORMAT_ZERO_SECONDS.toString();
}
return formattedTime;
}
private void appendSeconds(StringBuilder builder, long seconds, long minutes, long hours, String fHours, String fMinutes, String fSeconds) {
if (seconds != 0) {
String s = fSeconds.replace(secondsPH, String.valueOf(seconds));
if (minutes == 0 && s.contains(minutesPH)) {
if (hours == 0 && fMinutes.contains(hoursPH)) {
builder.append(fHours.replace(zeroPH, "0").replace(hoursPH, "0"));
}
builder.append(fMinutes.replace(hoursPH, "").replace(zeroPH, "0").replace(minutesPH, "0"));
}
s = s.replace(minutesPH, "");
if (s.contains(zeroPH) && String.valueOf(seconds).length() == 1) {
builder.append('0');
}
builder.append(s);
}
}
private void appendMinutes(StringBuilder builder, long minutes, long hours, String fHours, String fMinutes) {
if (minutes != 0) {
String m = fMinutes.replace(minutesPH, String.valueOf(minutes));
if (hours == 0 && m.contains(hoursPH)) {
builder.append(fHours.replace(zeroPH, "0").replace(hoursPH, "0"));
m = m.replace(hoursPH, "");
}
m = m.replace(hoursPH, "");
if (m.contains(zeroPH) && String.valueOf(minutes).length() == 1) {
builder.append('0');
}
builder.append(m);
}
}
private void appendHours(StringBuilder builder, long hours, String fHours) {
if (hours != 0) {
String h = fHours.replace(hoursPH, String.valueOf(hours));
if (h.contains(zeroPH) && String.valueOf(hours).length() == 1) {
builder.append('0');
}
builder.append(h);
}
}
private void appendDays(StringBuilder builder, long days) {
String singular = Settings.FORMAT_DAY.toString();
String plural = Settings.FORMAT_DAYS.toString();
appendValue(builder, days, singular, plural, daysPH);
}
private void appendMonths(StringBuilder builder, long months) {
String singular = Settings.FORMAT_MONTH.toString();
String plural = Settings.FORMAT_MONTHS.toString();
appendValue(builder, months, singular, plural, monthsPH);
}
private void appendYears(StringBuilder builder, long years) {
String singular = Settings.FORMAT_YEAR.toString();
String plural = Settings.FORMAT_YEARS.toString();
appendValue(builder, years, singular, plural, yearsPH);
}
private void appendValue(StringBuilder builder, long amount, String singular, String plural, String replace) {
if (amount != 0) {
if (amount == 1) {
builder.append(singular);
} else {
builder.append(plural.replace(replace, String.valueOf(amount)));
}
}
}
}

View File

@ -0,0 +1,148 @@
package com.djrapitops.plan.data.store.mutators.health;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.html.icon.Icons;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.*;
public abstract class AbstractHealthInfo {
protected final String subNote = "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
protected final List<String> notes;
protected final long now;
protected final long monthAgo;
protected double serverHealth;
public AbstractHealthInfo(long now, long monthAgo) {
this.now = now;
this.monthAgo = monthAgo;
serverHealth = 100.0;
this.notes = new ArrayList<>();
}
protected abstract void calculate();
public double getServerHealth() {
return serverHealth;
}
public String toHtml() {
StringBuilder healthNoteBuilder = new StringBuilder();
for (String healthNote : notes) {
healthNoteBuilder.append(healthNote);
}
return healthNoteBuilder.toString();
}
protected void activityChangeNote(TreeMap<Long, Map<String, Set<UUID>>> activityData) {
Map<String, Set<UUID>> activityNow = activityData.getOrDefault(now, new HashMap<>());
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
Map<String, Set<UUID>> activityFourWAgo = activityData.getOrDefault(monthAgo, new HashMap<>());
Set<UUID> veryActiveFWAG = activityFourWAgo.getOrDefault("Very Active", new HashSet<>());
Set<UUID> activeFWAG = activityFourWAgo.getOrDefault("Active", new HashSet<>());
Set<UUID> regularFWAG = activityFourWAgo.getOrDefault("Regular", new HashSet<>());
Set<UUID> regularRemainCompareSet = new HashSet<>(regularFWAG);
regularRemainCompareSet.addAll(activeFWAG);
regularRemainCompareSet.addAll(veryActiveFWAG);
int activeFWAGNum = regularRemainCompareSet.size();
regularRemainCompareSet.removeAll(regularNow);
regularRemainCompareSet.removeAll(activeNow);
regularRemainCompareSet.removeAll(veryActiveNow);
int notRegularAnymore = regularRemainCompareSet.size();
int remain = activeFWAGNum - notRegularAnymore;
double percRemain = remain * 100.0 / activeFWAGNum;
int newActive = getNewActive(veryActiveNow, activeNow, regularNow, veryActiveFWAG, activeFWAG, regularFWAG);
int change = newActive - notRegularAnymore;
String remainNote = "";
if (activeFWAGNum != 0) {
remainNote = "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
if (percRemain > 50) {
remainNote += Icons.GREEN_THUMB;
} else if (percRemain > 20) {
remainNote += Icons.YELLOW_FLAG;
} else {
remainNote += Icons.RED_WARN;
serverHealth -= 2.5;
}
remainNote += " " + FormatUtils.cutDecimals(percRemain) + "% of regular players have remained active ("
+ remain + "/" + activeFWAGNum + ")";
}
if (change > 0) {
addNote(Icons.GREEN_THUMB + " Number of regular players has increased (+" + change + ")" + remainNote);
} else if (change == 0) {
addNote(Icons.GREEN_THUMB + " Number of regular players has stayed the same (+" + change + ")" + remainNote);
} else if (change > -20) {
addNote(Icons.YELLOW_FLAG + " Number of regular players has decreased (" + change + ")" + remainNote);
serverHealth -= 5;
} else {
addNote(Icons.RED_WARN + " Number of regular players has decreased (" + change + ")" + remainNote);
serverHealth -= 10;
}
}
protected void activePlayerPlaytimeChange(PlayersMutator playersMutator) {
PlayersMutator currentlyActive = playersMutator.filterActive(now, 1.75);
long twoWeeksAgo = (now - (now - monthAgo)) / 2L;
long totalFourToTwoWeeks = 0;
long totalLastTwoWeeks = 0;
for (PlayerContainer activePlayer : currentlyActive.all()) {
totalFourToTwoWeeks += SessionsMutator.forContainer(activePlayer)
.filterSessionsBetween(monthAgo, twoWeeksAgo).toActivePlaytime();
totalLastTwoWeeks += SessionsMutator.forContainer(activePlayer)
.filterSessionsBetween(twoWeeksAgo, now).toActivePlaytime();
}
int activeCount = currentlyActive.count();
if (activeCount != 0) {
long avgFourToTwoWeeks = totalFourToTwoWeeks / (long) activeCount;
long avgLastTwoWeeks = totalLastTwoWeeks / (long) activeCount;
String avgLastTwoWeeksString = Formatters.timeAmount().apply(avgLastTwoWeeks);
String avgFourToTwoWeeksString = Formatters.timeAmount().apply(avgFourToTwoWeeks);
if (avgFourToTwoWeeks >= avgLastTwoWeeks) {
addNote(Icons.GREEN_THUMB + " Active players seem to have things to do (Played "
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
+ ", last two weeks vs weeks 2-4)");
} else if (avgFourToTwoWeeks - avgLastTwoWeeks > TimeAmount.HOUR.ms() * 2L) {
addNote(Icons.RED_WARN + " Active players might be running out of things to do (Played "
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
+ ", last two weeks vs weeks 2-4)");
serverHealth -= 5;
} else {
addNote(Icons.YELLOW_FLAG + " Active players might be running out of things to do (Played "
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
+ ", last two weeks vs weeks 2-4)");
}
}
}
private int getNewActive(Set<UUID> veryActiveNow, Set<UUID> activeNow, Set<UUID> regularNow, Set<UUID> veryActiveFWAG, Set<UUID> activeFWAG, Set<UUID> regularFWAG) {
Set<UUID> regularNewCompareSet = new HashSet<>(regularNow);
regularNewCompareSet.addAll(activeNow);
regularNewCompareSet.addAll(veryActiveNow);
regularNewCompareSet.removeAll(regularFWAG);
regularNewCompareSet.removeAll(activeFWAG);
regularNewCompareSet.removeAll(veryActiveFWAG);
return regularNewCompareSet.size();
}
protected void addNote(String note) {
notes.add("<p>" + note + "</p>");
}
}

View File

@ -0,0 +1,140 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package com.djrapitops.plan.data.store.mutators.health;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.containers.AnalysisContainer;
import com.djrapitops.plan.data.store.keys.AnalysisKeys;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.data.store.mutators.PlayersOnlineResolver;
import com.djrapitops.plan.data.store.mutators.TPSMutator;
import com.djrapitops.plan.data.store.mutators.formatting.Formatter;
import com.djrapitops.plan.data.store.mutators.formatting.Formatters;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.html.icon.Icons;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.ArrayList;
import java.util.Optional;
/**
* Server Health analysis mutator.
*
* @author Rsl1122
*/
public class HealthInformation extends AbstractHealthInfo {
private final AnalysisContainer analysisContainer;
public HealthInformation(AnalysisContainer analysisContainer) {
super(
analysisContainer.getUnsafe(AnalysisKeys.ANALYSIS_TIME),
analysisContainer.getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO)
);
this.analysisContainer = analysisContainer;
calculate();
}
public String toHtml() {
StringBuilder healthNoteBuilder = new StringBuilder();
for (String healthNote : notes) {
healthNoteBuilder.append(healthNote);
}
return healthNoteBuilder.toString();
}
@Override
protected void calculate() {
activityChangeNote(analysisContainer.getUnsafe(AnalysisKeys.ACTIVITY_DATA));
newPlayerNote();
activePlayerPlaytimeChange(analysisContainer.getUnsafe(AnalysisKeys.PLAYERS_MUTATOR));
lowPerformance();
}
private void newPlayerNote() {
Key<PlayersMutator> newMonth = new Key<>(PlayersMutator.class, "NEW_MONTH");
PlayersMutator newPlayersMonth = analysisContainer.getValue(newMonth).orElse(new PlayersMutator(new ArrayList<>()));
PlayersOnlineResolver onlineResolver = analysisContainer.getUnsafe(AnalysisKeys.PLAYERS_ONLINE_RESOLVER);
double avgOnlineOnRegister = newPlayersMonth.registerDates().stream()
.map(onlineResolver::getOnlineOn)
.filter(Optional::isPresent)
.mapToInt(Optional::get)
.average().orElse(0);
if (avgOnlineOnRegister >= 1) {
addNote(Icons.GREEN_THUMB + " New Players have players to play with when they join ("
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)");
} else {
addNote(Icons.YELLOW_FLAG + " New Players may not have players to play with when they join ("
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)");
serverHealth -= 5;
}
long playersNewMonth = analysisContainer.getValue(AnalysisKeys.PLAYERS_NEW_MONTH).orElse(0);
long playersRetainedMonth = analysisContainer.getValue(AnalysisKeys.PLAYERS_RETAINED_MONTH).orElse(0);
if (playersNewMonth != 0) {
double retainPercentage = playersRetainedMonth / playersNewMonth;
if (retainPercentage >= 0.25) {
addNote(Icons.GREEN_THUMB + " " + Formatters.percentage().apply(retainPercentage)
+ " of new players have stuck around (" + playersRetainedMonth + "/" + playersNewMonth + ")");
} else {
addNote(Icons.YELLOW_FLAG + " " + Formatters.percentage().apply(retainPercentage)
+ "% of new players have stuck around (" + playersRetainedMonth + "/" + playersNewMonth + ")");
}
}
}
private void lowPerformance() {
Key<TPSMutator> tpsMonth = new Key<>(TPSMutator.class, "TPS_MONTH");
TPSMutator tpsMutator = analysisContainer.getUnsafe(tpsMonth);
long serverDownTime = tpsMutator.serverDownTime();
double aboveThreshold = tpsMutator.percentageTPSAboveLowThreshold();
long tpsSpikeMonth = analysisContainer.getValue(AnalysisKeys.TPS_SPIKE_MONTH).orElse(0);
String avgLowThresholdString = "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
if (aboveThreshold >= 0.96) {
avgLowThresholdString += Icons.GREEN_THUMB;
} else if (aboveThreshold >= 0.9) {
avgLowThresholdString += Icons.YELLOW_FLAG;
serverHealth *= 0.9;
} else {
avgLowThresholdString += Icons.RED_WARN;
serverHealth *= 0.6;
}
avgLowThresholdString += " Average TPS was above Low Threshold "
+ FormatUtils.cutDecimals(aboveThreshold * 100.0) + "% of the time";
int threshold = Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber();
if (tpsSpikeMonth <= 5) {
addNote(Icons.GREEN_THUMB + " Average TPS dropped below Low Threshold (" + threshold + ")" +
" " + tpsSpikeMonth + " times" +
avgLowThresholdString);
} else if (tpsSpikeMonth <= 25) {
addNote(Icons.YELLOW_FLAG + " Average TPS dropped below Low Threshold (" + threshold + ")" +
" " + tpsSpikeMonth + " times" +
avgLowThresholdString);
serverHealth *= 0.95;
} else {
addNote(Icons.RED_WARN + " Average TPS dropped below Low Threshold (" + threshold + ")" +
" " + tpsSpikeMonth + " times" +
avgLowThresholdString);
serverHealth *= 0.8;
}
Formatter<Long> formatter = Formatters.timeAmount();
if (serverDownTime <= TimeAmount.DAY.ms()) {
addNote(Icons.GREEN_THUMB + " Total Server downtime (No Data) was " + formatter.apply(serverDownTime));
} else if (serverDownTime <= TimeAmount.WEEK.ms()) {
addNote(Icons.YELLOW_FLAG + " Total Server downtime (No Data) was " + formatter.apply(serverDownTime));
serverHealth *= (TimeAmount.WEEK.ms() - serverDownTime) * 1.0 / TimeAmount.WEEK.ms();
} else {
addNote(Icons.RED_WARN + " Total Server downtime (No Data) was " + formatter.apply(serverDownTime));
serverHealth *= (TimeAmount.MONTH.ms() - serverDownTime) * 1.0 / TimeAmount.MONTH.ms();
}
}
}

View File

@ -0,0 +1,154 @@
package com.djrapitops.plan.data.store.mutators.health;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.data.store.keys.AnalysisKeys;
import com.djrapitops.plan.data.store.keys.NetworkKeys;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.utilities.FormatUtils;
import com.djrapitops.plan.utilities.html.icon.Icon;
import com.djrapitops.plan.utilities.html.icon.Icons;
import java.util.*;
public class NetworkHealthInformation extends AbstractHealthInfo {
private final NetworkContainer container;
public NetworkHealthInformation(NetworkContainer container) {
super(
container.getUnsafe(NetworkKeys.REFRESH_TIME),
container.getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO)
);
this.container = container;
calculate();
}
@Override
protected void calculate() {
perServerComparisonNotes(container.getUnsafe(NetworkKeys.PLAYERS_MUTATOR));
activityChangeNote(container.getUnsafe(NetworkKeys.ACTIVITY_DATA));
activePlayerPlaytimeChange(container.getUnsafe(NetworkKeys.PLAYERS_MUTATOR));
}
private void perServerComparisonNotes(PlayersMutator playersMutator) {
Collection<Server> servers = container.getValue(NetworkKeys.BUKKIT_SERVERS)
.orElse(Collections.emptyList());
if (servers.isEmpty()) {
addNote(Icons.HELP_RING + " No Bukkit/Sponge servers to gather session data - These measures are inaccurate.");
return;
}
int serverCount = servers.size();
if (serverCount == 1) {
addNote(Icons.HELP_RING + " Single Bukkit/Sponge server to gather session data.");
return;
}
Key<Server> serverKey = new Key<>(Server.class, "SERVER");
List<DataContainer> perServerContainers = getPerServerContainers(playersMutator, servers, serverKey);
uniquePlayersNote(serverCount, serverKey, perServerContainers);
newPlayersNote(serverCount, serverKey, perServerContainers);
playersNote(serverCount, serverKey, perServerContainers);
}
private void uniquePlayersNote(int serverCount, Key<Server> serverKey, List<DataContainer> perServerContainers) {
Icon icon;
String uniquePlayersNote = " players visit on servers per day/server on average.";
double average = perServerContainers.stream()
.mapToInt(c -> c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH))
.average().orElse(0.0);
if (average < 1) {
icon = Icons.RED_WARN;
serverHealth -= 10.0;
} else if (average < serverCount) {
icon = Icons.YELLOW_FLAG;
serverHealth -= 5.0;
} else {
icon = Icons.GREEN_THUMB;
}
StringBuilder subNotes = new StringBuilder();
perServerContainers.stream()
.sorted(Comparator.comparingInt(c -> 0 - c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH)))
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH);
Server server = c.getUnsafe(serverKey);
return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon + " " + FormatUtils.cutDecimals(average) + uniquePlayersNote + subNotes.toString());
}
private void newPlayersNote(int serverCount, Key<Server> serverKey, List<DataContainer> perServerContainers) {
Icon icon;
String newPlayersNote = " players register on servers per day/server on average.";
double average = perServerContainers.stream()
.mapToInt(c -> c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH))
.average().orElse(0.0);
if (average < 1) {
icon = Icons.RED_WARN;
serverHealth -= 10.0;
} else if (average < serverCount) {
icon = Icons.YELLOW_FLAG;
serverHealth -= 5.0;
} else {
icon = Icons.GREEN_THUMB;
}
StringBuilder subNotes = new StringBuilder();
perServerContainers.stream()
.sorted(Comparator.comparingInt(c -> 0 - c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH)))
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH);
Server server = c.getUnsafe(serverKey);
return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon + " " + FormatUtils.cutDecimals(average) + newPlayersNote + subNotes.toString());
}
private List<DataContainer> getPerServerContainers(PlayersMutator playersMutator, Collection<Server> servers, Key<Server> serverKey) {
List<DataContainer> perServerContainers = new ArrayList<>();
for (Server server : servers) {
UUID serverUUID = server.getUuid();
DataContainer serverContainer = new DataContainer();
serverContainer.putRawData(serverKey, server);
PlayersMutator serverPlayers = playersMutator.filterPlayedOnServer(serverUUID);
PlayersMutator serverRegistered = serverPlayers.filterRegisteredBetween(monthAgo, now);
int averageNewPerDay = serverRegistered.averageNewPerDay();
serverContainer.putRawData(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, averageNewPerDay);
SessionsMutator serverSessions = new SessionsMutator(serverPlayers.getSessions())
.filterSessionsBetween(monthAgo, now)
.filterPlayedOnServer(serverUUID);
int averageUniquePerDay = serverSessions.toAverageUniqueJoinsPerDay();
int uniquePlayers = serverSessions.toUniquePlayers();
serverContainer.putRawData(AnalysisKeys.AVG_PLAYERS_MONTH, averageUniquePerDay);
serverContainer.putRawData(AnalysisKeys.PLAYERS_MONTH, uniquePlayers);
perServerContainers.add(serverContainer);
}
return perServerContainers;
}
private void playersNote(int serverCount, Key<Server> serverKey, List<DataContainer> perServerContainers) {
Icon icon = Icons.HELP_RING;
String uniquePlayersNote = "${playersMonth} players played on the network:";
StringBuilder subNotes = new StringBuilder();
perServerContainers.stream()
.sorted(Comparator.comparingInt(c -> 0 - c.getUnsafe(AnalysisKeys.PLAYERS_MONTH)))
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.PLAYERS_MONTH);
Server server = c.getUnsafe(serverKey);
return subNote + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon.toHtml() + " " + uniquePlayersNote + subNotes.toString());
}
}

View File

@ -0,0 +1,17 @@
package com.djrapitops.plan.data.store.objects;
/**
* Interface for objects that have a epoch ms date.
*
* @author Rsl1122
*/
public interface DateHolder {
/**
* Get the date the object holds.
*
* @return Epoch ms - milliseconds passed since January 1st 1970.
*/
long getDate();
}

View File

@ -0,0 +1,23 @@
package com.djrapitops.plan.data.store.objects;
import java.util.TreeMap;
/**
* Basic TreeMap that uses Epoch MS as keys.
*
* @author Rsl1122
*/
public class DateMap<T> extends TreeMap<Long, T> {
public DateMap() {
super(Long::compareTo);
}
public boolean hasValuesBetween(long after, long before) {
return countBetween(after, before) > 0;
}
public int countBetween(long after, long before) {
return subMap(after, before).size();
}
}

View File

@ -0,0 +1,26 @@
package com.djrapitops.plan.data.store.objects;
/**
* Object that has a value tied to a date.
*
* @author Rsl1122
*/
public class DateObj<T> implements DateHolder {
private final long date;
private final T value;
public DateObj(long date, T value) {
this.date = date;
this.value = value;
}
@Override
public long getDate() {
return date;
}
public T getValue() {
return value;
}
}

View File

@ -0,0 +1,20 @@
package com.djrapitops.plan.data.store.objects;
import java.util.TreeSet;
/**
* Basic TreeSet with Epoch ms as values.
*
* @author Rsl1122
*/
public class DateSet extends TreeSet<Long> {
public boolean hasValuesBetween(long after, long before) {
return countBetween(after, before) > 0;
}
public int countBetween(long after, long before) {
return subSet(after, before).size();
}
}

View File

@ -0,0 +1,59 @@
package com.djrapitops.plan.data.store.objects;
import java.util.Objects;
import java.util.UUID;
/**
* Object storing nickname information.
*
* @author Rsl1122
*/
public class Nickname implements DateHolder {
private final String name;
private final long date;
private final UUID serverUUID;
public Nickname(String name, long date, UUID serverUUID) {
this.name = name;
this.date = date;
this.serverUUID = serverUUID;
}
public String getName() {
return name;
}
@Override
public long getDate() {
return date;
}
public UUID getServerUUID() {
return serverUUID;
}
@Override
public String toString() {
return "Nickname{" +
"name='" + name + '\'' +
", date=" + date +
", serverUUID=" + serverUUID +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Nickname)) return false;
Nickname nickname = (Nickname) o;
return Objects.equals(name, nickname.name) &&
Objects.equals(serverUUID, nickname.serverUUID);
}
@Override
public int hashCode() {
return Objects.hash(name, date, serverUUID);
}
}

View File

@ -8,6 +8,7 @@ import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.api.BungeeAPI;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.data.plugin.HookHandler;
import com.djrapitops.plan.system.cache.BungeeCacheSystem;
import com.djrapitops.plan.system.database.BungeeDBSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.file.FileSystem;
@ -37,6 +38,7 @@ public class BungeeSystem extends PlanSystem {
fileSystem = new FileSystem(plugin);
configSystem = new BungeeConfigSystem();
databaseSystem = new BungeeDBSystem();
cacheSystem = new BungeeCacheSystem(this);
listenerSystem = new BungeeListenerSystem(plugin);
taskSystem = new BungeeTaskSystem(plugin);

View File

@ -36,7 +36,7 @@ public abstract class PlanSystem implements SubSystem {
// Initialized in this class
private Processing processing;
protected final WebServerSystem webServerSystem;
protected final CacheSystem cacheSystem;
protected CacheSystem cacheSystem;
// These need to be initialized in the sub class.
protected VersionCheckSystem versionCheckSystem;

View File

@ -29,8 +29,6 @@ import com.djrapitops.plugin.api.utility.log.Log;
*/
public class SpongeSystem extends PlanSystem implements ServerSystem {
private boolean firstInstall = false;
public SpongeSystem(PlanSponge plugin) {
setTestSystem(this);

View File

@ -5,10 +5,7 @@ import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plugin.api.TimeAmount;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
/**
* Keeps track how long player has been afk during a session
@ -17,35 +14,55 @@ import java.util.UUID;
*/
public class AFKTracker {
private final Set<UUID> usedAFKCommand;
private final Map<UUID, Long> lastMovement;
private final long afkThresholdMs;
public AFKTracker() {
usedAFKCommand = new HashSet<>();
lastMovement = new HashMap<>();
afkThresholdMs = Settings.AFK_THRESHOLD_MINUTES.getNumber() * TimeAmount.MINUTE.ms();
}
public void hasIgnorePermission(UUID uuid) {
lastMovement.put(uuid, -1L);
}
public void usedAfkCommand(UUID uuid, long time) {
usedAFKCommand.add(uuid);
lastMovement.put(uuid, time - afkThresholdMs);
}
public void performedAction(UUID uuid, long time) {
Long lastMoved = lastMovement.getOrDefault(uuid, time);
if (lastMoved == -1) {
return;
}
lastMovement.put(uuid, time);
if (time - lastMoved < afkThresholdMs) {
// Threshold not crossed, no action required.
return;
}
try {
if (time - lastMoved < afkThresholdMs) {
// Threshold not crossed, no action required.
return;
}
long timeAFK = time - lastMoved;
long removeAfkCommandEffect = usedAFKCommand.contains(uuid) ? afkThresholdMs : 0;
long timeAFK = time - lastMoved - removeAfkCommandEffect;
Optional<Session> cachedSession = SessionCache.getCachedSession(uuid);
if (!cachedSession.isPresent()) {
return;
Optional<Session> cachedSession = SessionCache.getCachedSession(uuid);
if (!cachedSession.isPresent()) {
return;
}
Session session = cachedSession.get();
session.addAFKTime(timeAFK);
} finally {
usedAFKCommand.remove(uuid);
}
Session session = cachedSession.get();
session.addAFKTime(timeAFK);
}
public void loggedOut(UUID uuid, long time) {
performedAction(uuid, time);
lastMovement.remove(uuid);
usedAFKCommand.remove(uuid);
}
}

View File

@ -0,0 +1,18 @@
package com.djrapitops.plan.system.cache;
import com.djrapitops.plan.system.PlanSystem;
/**
* CacheSystem for Bungee.
* <p>
* Used for overriding {@link DataCache} with {@link BungeeDataCache}
*
* @author Rsl1122
*/
public class BungeeCacheSystem extends CacheSystem {
public BungeeCacheSystem(PlanSystem system) {
super(new BungeeDataCache(system));
}
}

View File

@ -0,0 +1,25 @@
package com.djrapitops.plan.system.cache;
import com.djrapitops.plan.system.PlanSystem;
import java.util.UUID;
/**
* Bungee specific DataCache.
* <p>
* Used for overriding {@link SessionCache#endSession(UUID, long)}.
*
* @author Rsl1122
*/
public class BungeeDataCache extends DataCache {
public BungeeDataCache(PlanSystem system) {
super(system);
}
@Override
public void endSession(UUID uuid, long time) {
removeSessionFromCache(uuid);
/* Bungee should not save sessions so session is not removed.. */
}
}

View File

@ -20,7 +20,11 @@ public class CacheSystem implements SubSystem {
private final GeolocationCache geolocationCache;
public CacheSystem(PlanSystem system) {
dataCache = new DataCache(system);
this(new DataCache(system));
}
protected CacheSystem(DataCache dataCache) {
this.dataCache = dataCache;
geolocationCache = new GeolocationCache();
}

View File

@ -1,6 +1,6 @@
package com.djrapitops.plan.system.cache;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.SubSystem;
import com.djrapitops.plan.system.database.databases.Database;
@ -69,21 +69,14 @@ public class DataCache extends SessionCache implements SubSystem {
}
}
public void cacheSavedNames() {
try {
Map<UUID, String> playerNames = db.fetch().getPlayerNames();
this.playerNames.putAll(playerNames);
for (Map.Entry<UUID, String> entry : playerNames.entrySet()) {
uuids.put(entry.getValue(), entry.getKey());
}
} catch (DBException e) {
Log.toLog(this.getClass(), e);
}
}
/**
* Used to get the player name in the cache.
*
* It is recommended to use
* {@link com.djrapitops.plan.data.store.keys.AnalysisKeys#PLAYER_NAMES} and
* {@link com.djrapitops.plan.data.store.keys.PlayerKeys#NAME} when possible
* because this method will call database if a name is not found.
*
* @param uuid UUID of the player.
* @return name or null if not cached.
*/
@ -93,7 +86,7 @@ public class DataCache extends SessionCache implements SubSystem {
try {
name = db.fetch().getPlayerName(uuid);
playerNames.put(uuid, name);
} catch (DBException e) {
} catch (DBOpException e) {
Log.toLog(this.getClass(), e);
name = "Error occurred";
}
@ -118,7 +111,7 @@ public class DataCache extends SessionCache implements SubSystem {
if (!nicknames.isEmpty()) {
return nicknames.get(nicknames.size() - 1);
}
} catch (DBException e) {
} catch (DBOpException e) {
Log.toLog(this.getClass(), e);
}
}

View File

@ -1,7 +1,8 @@
package com.djrapitops.plan.system.cache;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plugin.api.utility.log.Log;
@ -20,7 +21,6 @@ import java.util.UUID;
*/
public class SessionCache {
private static final Map<UUID, Integer> firstSessionInformation = new HashMap<>();
private static final Map<UUID, Session> activeSessions = new HashMap<>();
protected final PlanSystem system;
@ -34,13 +34,6 @@ public class SessionCache {
return dataCache;
}
/**
* Used to get the Map of active sessions.
* <p>
* Used for testing.
*
* @return key:value UUID:Session
*/
public static Map<UUID, Session> getActiveSessions() {
return activeSessions;
}
@ -49,28 +42,9 @@ public class SessionCache {
activeSessions.clear();
}
public void cacheSession(UUID uuid, Session session) {
activeSessions.put(uuid, session);
}
public void endSession(UUID uuid, long time) {
try {
Session session = activeSessions.get(uuid);
if (session == null) {
return;
}
session.endSession(time);
Database.getActive().save().session(uuid, session);
} catch (DBException e) {
Log.toLog(this.getClass(), e);
} finally {
activeSessions.remove(uuid);
}
}
public static void refreshActiveSessionsState() {
for (Session session : activeSessions.values()) {
session.getWorldTimes().updateState(System.currentTimeMillis());
session.getUnsafe(SessionKeys.WORLD_TIMES).updateState(System.currentTimeMillis());
}
}
@ -78,50 +52,42 @@ public class SessionCache {
* Used to get the Session of the player in the sessionCache.
*
* @param uuid UUID of the player.
* @return Session or null if not cached.
* @return Optional with the session inside it if found.
*/
public static Optional<Session> getCachedSession(UUID uuid) {
Session session = activeSessions.get(uuid);
if (session != null) {
return Optional.of(session);
return Optional.ofNullable(activeSessions.get(uuid));
}
public static boolean isOnline(UUID uuid) {
return getCachedSession(uuid).isPresent();
}
public void cacheSession(UUID uuid, Session session) {
if (getCachedSession(uuid).isPresent()) {
endSession(uuid, System.currentTimeMillis());
}
return Optional.empty();
activeSessions.put(uuid, session);
}
/**
* Used for marking first Session Actions to be saved.
*
* @param uuid UUID of the new player.
*/
public void markFirstSession(UUID uuid) {
firstSessionInformation.put(uuid, 0);
public void endSession(UUID uuid, long time) {
Session session = activeSessions.get(uuid);
if (session == null) {
return;
}
if (session.getUnsafe(SessionKeys.START) > time) {
return;
}
try {
session.endSession(time);
Database.getActive().save().session(uuid, session);
} catch (DBOpException e) {
Log.toLog(this.getClass(), e);
} finally {
removeSessionFromCache(uuid);
}
}
/**
* Check if a session is player's first session on the server.
*
* @param uuid UUID of the player
* @return true / false
*/
public boolean isFirstSession(UUID uuid) {
return firstSessionInformation.containsKey(uuid);
}
public void endFirstSessionActionTracking(UUID uuid) {
firstSessionInformation.remove(uuid);
}
public void firstSessionMessageSent(UUID uuid) {
Integer msgCount = firstSessionInformation.getOrDefault(uuid, 0);
msgCount++;
firstSessionInformation.put(uuid, msgCount);
}
public int getFirstSessionMsgCount(UUID uuid) {
return firstSessionInformation.getOrDefault(uuid, 0);
}
public Map<UUID, Integer> getFirstSessionMsgCounts() {
return firstSessionInformation;
protected void removeSessionFromCache(UUID uuid) {
activeSessions.remove(uuid);
}
}

View File

@ -1,14 +1,11 @@
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.system.database.databases.Database;
import java.sql.SQLException;
public interface BackupOperations {
void backup(Database toDatabase) throws SQLException;
void backup(Database toDatabase);
void restore(Database fromDatabase) throws DBException, SQLException;
void restore(Database fromDatabase);
}

View File

@ -1,21 +1,20 @@
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.system.info.server.ServerInfo;
import java.util.UUID;
public interface CheckOperations {
boolean isPlayerRegistered(UUID player) throws DBException;
boolean isPlayerRegistered(UUID player);
boolean isPlayerRegistered(UUID player, UUID server) throws DBException;
boolean isPlayerRegistered(UUID player, UUID server);
boolean doesWebUserExists(String username) throws DBException;
boolean doesWebUserExists(String username);
default boolean isPlayerRegisteredOnThisServer(UUID player) throws DBException {
default boolean isPlayerRegisteredOnThisServer(UUID player) {
return isPlayerRegistered(player, ServerInfo.getServerUUID());
}
boolean isServerInDatabase(UUID serverUUID) throws DBException;
boolean isServerInDatabase(UUID serverUUID);
}

View File

@ -1,12 +1,10 @@
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import java.util.UUID;
public interface CountOperations {
int getServerPlayerCount(UUID server) throws DBException;
int getServerPlayerCount(UUID server);
int getNetworkPlayerCount() throws DBException;
int getNetworkPlayerCount();
}

View File

@ -1,93 +1,119 @@
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.data.PlayerProfile;
import com.djrapitops.plan.data.ServerProfile;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.data.container.*;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.container.UserInfo;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.containers.ServerContainer;
import com.djrapitops.plan.system.info.server.Server;
import java.util.*;
public interface FetchOperations {
// Profiles
/**
* Used to get a NetworkContainer, some limitations apply to values returned by DataContainer keys.
* <p>
* Limitations:
* - Bungee ServerContainer does not support: ServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT
* - Bungee ServerContainer ServerKeys.TPS only contains playersOnline values
* - NetworkKeys.PLAYERS PlayerContainers:
* - do not support: PlayerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT
* - PlayerKeys.PER_SERVER does not support: PerServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT
* <p>
* Blocking methods are not called until DataContainer getter methods are called.
*
* @return a new NetworkContainer.
*/
NetworkContainer getNetworkContainer();
ServerProfile getServerProfile(UUID serverUUID) throws DBException;
/**
* Used to get a ServerContainer, some limitations apply to values returned by DataContainer keys.
* <p>
* Limitations:
* - ServerKeys.PLAYERS PlayerContainers PlayerKeys.PER_SERVER only contains information about the queried server.
* <p>
* Blocking methods are not called until DataContainer getter methods are called.
*
* @param serverUUID UUID of the Server.
* @return a new ServerContainer.
*/
ServerContainer getServerContainer(UUID serverUUID);
List<PlayerProfile> getPlayers(UUID serverUUID) throws DBException;
/**
* Used to get PlayerContainers of all players on the network, some limitations apply to DataContainer keys.
* <p>
* Limitations:
* - PlayerContainers do not support: PlayerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT
* - PlayerContainers PlayerKeys.PER_SERVER does not support: PerServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT
* <p>
* Blocking methods are not called until DataContainer getter methods are called.
*
* @return a list of PlayerContainers in Plan database.
*/
List<PlayerContainer> getAllPlayerContainers();
PlayerProfile getPlayerProfile(UUID uuid) throws DBException;
/**
* Used to get a PlayerContainer of a specific player.
* <p>
* Blocking methods are not called until DataContainer getter methods are called.
*
* @param uuid UUID of the player.
* @return a new PlayerContainer.
*/
PlayerContainer getPlayerContainer(UUID uuid);
// UUIDs
Set<UUID> getSavedUUIDs() throws DBException;
Set<UUID> getSavedUUIDs();
Set<UUID> getSavedUUIDs(UUID server) throws DBException;
Set<UUID> getSavedUUIDs(UUID server);
Map<UUID, String> getServerNames() throws DBException;
Map<UUID, String> getServerNames();
Optional<UUID> getServerUUID(String serverName) throws DBException;
Optional<UUID> getServerUUID(String serverName);
UUID getUuidOf(String playerName) throws DBException;
UUID getUuidOf(String playerName);
// WebUsers
WebUser getWebUser(String username) throws DBException;
WebUser getWebUser(String username);
// Servers
Optional<String> getServerName(UUID serverUUID) throws DBException;
Optional<String> getServerName(UUID serverUUID);
Optional<Server> getBungeeInformation() throws DBException;
Optional<Server> getBungeeInformation();
Optional<Integer> getServerID(UUID serverUUID) throws DBException;
Optional<Integer> getServerID(UUID serverUUID);
// Raw Data
List<TPS> getTPSData(UUID serverUUID) throws DBException;
List<TPS> getTPSData(UUID serverUUID);
List<TPS> getNetworkOnlineData() throws DBException;
Map<UUID, Map<UUID, List<Session>>> getSessionsWithNoExtras();
List<Long> getRegisterDates() throws DBException;
Map<UUID, UserInfo> getUsers();
Optional<TPS> getAllTimePeak(UUID serverUUID) throws DBException;
Map<UUID, Long> getLastSeenForAllPlayers();
Optional<TPS> getPeakPlayerCount(UUID serverUUID, long afterDate) throws DBException;
Map<UUID, List<GeoInfo>> getAllGeoInfo();
Map<UUID, Map<UUID, List<Session>>> getSessionsWithNoExtras() throws DBException;
Map<UUID, String> getPlayerNames();
Map<UUID, Map<UUID, List<Session>>> getSessionsAndExtras() throws DBException;
String getPlayerName(UUID playerUUID);
Set<String> getWorldNames(UUID serverUuid) throws DBException;
List<String> getNicknames(UUID uuid);
List<String> getNicknamesOfPlayerOnServer(UUID uuid, UUID serverUUID) throws DBException;
Map<UUID, Server> getBukkitServers();
List<Action> getActions(UUID uuid) throws DBException;
List<WebUser> getWebUsers();
Map<UUID, UserInfo> getUsers() throws DBException;
List<Server> getServers();
Map<UUID, Long> getLastSeenForAllPlayers() throws DBException;
List<UUID> getServerUUIDs();
Map<UUID, List<GeoInfo>> getAllGeoInfo() throws DBException;
Map<UUID, String> getPlayerNames() throws DBException;
String getPlayerName(UUID playerUUID) throws DBException;
List<String> getNicknames(UUID uuid) throws DBException;
Map<UUID, Server> getBukkitServers() throws DBException;
List<WebUser> getWebUsers() throws DBException;
Map<Integer, String> getServerNamesByID() throws DBException;
Map<UUID, Map<UUID, List<Session>>> getSessionsInLastMonth() throws DBException;
List<Server> getServers() throws DBException;
List<UUID> getServerUUIDs() throws DBException;
List<String> getNetworkGeolocations() throws DBException;
}

View File

@ -1,18 +1,12 @@
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import java.util.UUID;
public interface RemoveOperations {
void player(UUID uuid) throws DBException;
void player(UUID uuid);
void player(UUID player, UUID server) throws DBException;
void everything();
void server(UUID serverUUID) throws DBException;
void everything() throws DBException;
void webUser(String name) throws DBException;
void webUser(String name);
}

View File

@ -4,9 +4,9 @@
*/
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.data.container.*;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.system.info.server.Server;
import java.util.List;
@ -15,7 +15,7 @@ import java.util.UUID;
/**
* Operation methods for saving data.
*
* <p>
* Note: Method names subject to change (TODO remove insert update and such)
*
* @author Rsl1122
@ -24,50 +24,49 @@ public interface SaveOperations {
// Bulk save
void insertTPS(Map<UUID, List<TPS>> ofServers) throws DBException;
void insertTPS(Map<UUID, List<TPS>> ofServers);
void insertCommandUsage(Map<UUID, Map<String, Integer>> ofServers) throws DBException;
void insertCommandUsage(Map<UUID, Map<String, Integer>> ofServers);
void insertUsers(Map<UUID, UserInfo> ofServers) throws DBException;
void insertUsers(Map<UUID, UserInfo> ofServers);
void insertSessions(Map<UUID, Map<UUID, List<Session>>> ofServers, boolean containsExtraData)
throws DBException;
void insertSessions(Map<UUID, Map<UUID, List<Session>>> ofServers, boolean containsExtraData);
void kickAmount(Map<UUID, Integer> ofUsers) throws DBException;
void kickAmount(Map<UUID, Integer> ofUsers);
void insertUserInfo(Map<UUID, List<UserInfo>> ofServers) throws DBException;
void insertUserInfo(Map<UUID, List<UserInfo>> ofServers);
void insertNicknames(Map<UUID, Map<UUID, List<String>>> ofServers) throws DBException;
void insertNicknames(Map<UUID, Map<UUID, List<Nickname>>> ofServers);
void insertAllGeoInfo(Map<UUID, List<GeoInfo>> ofUsers) throws DBException;
void insertAllGeoInfo(Map<UUID, List<GeoInfo>> ofUsers);
// Single data point
void banStatus(UUID uuid, boolean banned) throws DBException;
void banStatus(UUID uuid, boolean banned);
void opStatus(UUID uuid, boolean op) throws DBException;
void opStatus(UUID uuid, boolean op);
void registerNewUser(UUID uuid, long registered, String name) throws DBException;
void registerNewUser(UUID uuid, long registered, String name);
void action(UUID uuid, Action action) throws DBException;
void geoInfo(UUID uuid, GeoInfo geoInfo);
void geoInfo(UUID uuid, GeoInfo geoInfo) throws DBException;
void playerWasKicked(UUID uuid);
void playerWasKicked(UUID uuid) throws DBException;
void playerName(UUID uuid, String playerName);
void playerName(UUID uuid, String playerName) throws DBException;
void playerDisplayName(UUID uuid, Nickname nickname);
void playerDisplayName(UUID uuid, String displayName) throws DBException;
void registerNewUserOnThisServer(UUID uuid, long registered);
void registerNewUserOnThisServer(UUID uuid, long registered) throws DBException;
void commandUsed(String commandName);
void commandUsed(String commandName) throws DBException;
void insertTPSforThisServer(TPS tps);
void insertTPSforThisServer(TPS tps) throws DBException;
void session(UUID uuid, Session session);
void session(UUID uuid, Session session) throws DBException;
void serverInfoForThisServer(Server server);
void serverInfoForThisServer(Server server) throws DBException;
void webUser(WebUser webUser);
void webUser(WebUser webUser) throws DBException;
}
void ping(UUID uuid, Ping ping);
}

View File

@ -1,11 +1,9 @@
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import java.util.List;
public interface SearchOperations {
List<String> matchingPlayers(String search) throws DBException;
List<String> matchingPlayers(String search);
}

View File

@ -4,10 +4,7 @@
*/
package com.djrapitops.plan.system.database.databases.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import java.util.Optional;
import java.util.UUID;
/**
* Operations for transferring data via Database to another server.
@ -20,12 +17,9 @@ public interface TransferOperations {
// Save
void storeConfigSettings(String encodedSettingString) throws DBException;
void storeConfigSettings(String encodedSettingString);
// Get
@Deprecated
Optional<UUID> getServerPlayerIsOnlineOn(UUID playerUUID) throws DBException;
Optional<String> getEncodedConfigSettings() throws DBException;
}
Optional<String> getEncodedConfigSettings();
}

Some files were not shown because too many files have changed in this diff Show More