[Merge] 4.7.0 (#941)

This commit is contained in:
Risto Lahtela 2019-03-03 19:41:32 +02:00 committed by GitHub
commit 45b33e49e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
410 changed files with 13324 additions and 12126 deletions

View File

@ -12,9 +12,10 @@ allprojects {
wrapper.gradleVersion = "5.0"
group "com.djrapitops"
version "4.6.2-SNAPSHOT"
version "4.7.0"
test {
// useJUnitPlatform()
testLogging {
events "passed", "failed"
exceptionFormat "full"
@ -30,7 +31,7 @@ allprojects {
subprojects {
// Build plugins
apply plugin: "java"
apply plugin: "maven"
apply plugin: "maven-publish"
apply plugin: "net.ltgt.apt" // Annotation processing plugin
apply plugin: "net.ltgt.apt-idea" // Annotation processing IntelliJ IDEA configuration plugin
apply plugin: "com.github.johnrengelman.shadow"
@ -46,7 +47,7 @@ subprojects {
ext.daggerCompilerVersion = "2.21"
ext.abstractPluginFrameworkVersion = "3.4.1"
ext.planPluginBridgeVersion = "4.6.2"
ext.planPluginBridgeVersion = "4.7.0"
ext.bukkitVersion = "1.12.2-R0.1-SNAPSHOT"
ext.spigotVersion = "1.12.2-R0.1-SNAPSHOT"
@ -59,10 +60,10 @@ subprojects {
ext.httpClientVersion = "4.5.7"
ext.commonsTextVersion = "1.6"
ext.htmlCompressorVersion = "1.5.2"
ext.caffeineVersion = "2.6.2"
ext.caffeineVersion = "2.7.0"
ext.h2Version = "1.4.196"
ext.hikariVersion = "3.3.0"
ext.slf4jVersion = "1.7.25"
ext.hikariVersion = "3.3.1"
ext.slf4jVersion = "1.7.26"
ext.geoIpVersion = "2.12.0"
ext.guavaVersion = "26.0-jre"
ext.bstatsVersion = "1.2"
@ -102,13 +103,12 @@ subprojects {
testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerCompilerVersion"
// Test Tooling Dependencies
testCompile "org.junit.jupiter:junit-jupiter-engine:5.3.2" // JUnit 5
testCompile "org.junit.platform:junit-platform-runner:1.3.2" // JUnit 4 runner for JUnit 5 tests
testCompile "org.junit.vintage:junit-vintage-engine:5.3.2" // JUnit 4 compatibility for JUnit 5
testCompile "org.junit.jupiter:junit-jupiter-params:5.3.2" // JUnit 5, parameterized tests
testCompile "org.junit-pioneer:junit-pioneer:0.3.0" // TempDirectory TODO REMOVE W/ JUNIT 5.4
testCompile "org.mockito:mockito-core:2.24.0" // Mockito Core
testCompile "org.mockito:mockito-junit-jupiter:2.24.0" // Mockito JUnit 5 Extension
testCompile "org.junit.jupiter:junit-jupiter-engine:5.4.0" // JUnit 5
testCompile "org.junit.platform:junit-platform-runner:1.4.0" // JUnit 4 runner for JUnit 5 tests
testCompile "org.junit.vintage:junit-vintage-engine:5.4.0" // JUnit 4 compatibility for JUnit 5
testCompile "org.junit.jupiter:junit-jupiter-params:5.4.0" // JUnit 5, parameterized tests
testCompile "org.mockito:mockito-core:2.24.5" // Mockito Core
testCompile "org.mockito:mockito-junit-jupiter:2.24.5" // Mockito JUnit 5 Extension
testCompile "org.seleniumhq.selenium:selenium-java:3.141.59" // Selenium (Browser tests)
testCompile "com.jayway.awaitility:awaitility:1.7.0" // Awaitility (Concurrent wait conditions)

View File

@ -0,0 +1,64 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.utilities.java.Reflection;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* ServerShutdownSave implementation for Bukkit based servers.
*
* @author Rsl1122
*/
@Singleton
public class BukkitServerShutdownSave extends ServerShutdownSave {
@Inject
public BukkitServerShutdownSave(
Locale locale,
DBSystem dbSystem,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(locale, dbSystem, logger, errorHandler);
}
@Override
protected boolean checkServerShuttingDownStatus() {
try {
return performCheck();
} catch (Exception | NoClassDefFoundError | NoSuchFieldError e) {
logger.debug("Server shutdown check failed, using JVM ShutdownHook instead. Error: " + e.toString());
return false; // ShutdownHook handles save in case this fails upon plugin disable.
}
}
private boolean performCheck() {
// Special thanks to Fuzzlemann for figuring out the methods required for this check.
// https://github.com/Rsl1122/Plan-PlayerAnalytics/issues/769#issuecomment-433898242
Class<?> minecraftServerClass = Reflection.getMinecraftClass("MinecraftServer");
Object minecraftServer = Reflection.getField(minecraftServerClass, "SERVER", minecraftServerClass).get(null);
return Reflection.getField(minecraftServerClass, "isStopped", boolean.class).get(minecraftServer);
}
}

View File

@ -40,6 +40,7 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
private PlanSystem system;
private Locale locale;
private ServerShutdownSave serverShutdownSave;
@Override
public void onEnable() {
@ -47,6 +48,7 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
try {
timings.start("Enable");
system = component.system();
serverShutdownSave = component.serverShutdownSave();
locale = system.getLocaleSystem().getLocale();
system.enable();
@ -88,6 +90,9 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
*/
@Override
public void onDisable() {
if (serverShutdownSave != null) {
serverShutdownSave.performSave();
}
if (system != null) {
system.disable();
}

View File

@ -17,7 +17,10 @@
package com.djrapitops.plan;
import com.djrapitops.plan.command.PlanCommand;
import com.djrapitops.plan.modules.*;
import com.djrapitops.plan.modules.APFModule;
import com.djrapitops.plan.modules.FilesModule;
import com.djrapitops.plan.modules.ServerSuperClassBindingModule;
import com.djrapitops.plan.modules.SystemObjectProvidingModule;
import com.djrapitops.plan.modules.bukkit.BukkitPlanModule;
import com.djrapitops.plan.modules.bukkit.BukkitServerPropertiesModule;
import com.djrapitops.plan.modules.bukkit.BukkitSuperClassBindingModule;
@ -36,7 +39,6 @@ import javax.inject.Singleton;
@Singleton
@Component(modules = {
BukkitPlanModule.class,
SuperClassBindingModule.class,
SystemObjectProvidingModule.class,
APFModule.class,
FilesModule.class,
@ -51,6 +53,8 @@ public interface PlanBukkitComponent {
PlanSystem system();
ServerShutdownSave serverShutdownSave();
@Component.Builder
interface Builder {

View File

@ -16,6 +16,8 @@
*/
package com.djrapitops.plan.modules.bukkit;
import com.djrapitops.plan.BukkitServerShutdownSave;
import com.djrapitops.plan.ServerShutdownSave;
import com.djrapitops.plan.system.database.BukkitDBSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.importing.BukkitImportSystem;
@ -55,6 +57,9 @@ public interface BukkitSuperClassBindingModule {
ListenerSystem bindBukkitListenerSystem(BukkitListenerSystem bukkitListenerSystem);
@Binds
ImportSystem bindImportSsytem(BukkitImportSystem bukkitImportSystem);
ImportSystem bindImportSystem(BukkitImportSystem bukkitImportSystem);
@Binds
ServerShutdownSave bindBukkitServerShutdownSave(BukkitServerShutdownSave bukkitServerShutdownSave);
}

View File

@ -17,9 +17,9 @@
package com.djrapitops.plan.system.database;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.MySQLDB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.db.H2DB;
import com.djrapitops.plan.db.MySQLDB;
import com.djrapitops.plan.db.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;

View File

@ -21,19 +21,20 @@ import com.djrapitops.plan.system.DebugChannels;
import com.djrapitops.plugin.api.utility.UUIDFetcher;
import com.djrapitops.plugin.benchmarking.Timings;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import java.util.*;
import java.util.stream.Collectors;
/**
* A class for refining the {@link UserImportData}s
* so no {@code null} is left in any field.
* It also removes invalid data.
* UserImportRefiner attempts to find any crucial information that is missing.
*
* - Finds UUIDs if only name is present.
* - Finds Names if only UUID is present.
* - Removes any importers that do not have any identifiers.
*
* @author Fuzzlemann
*/
public class UserImportRefiner {
public class BukkitUserImportRefiner {
private final Plan plugin;
private final Timings timings;
@ -41,15 +42,13 @@ public class UserImportRefiner {
private final List<UserImportData> importers = new ArrayList<>();
private final Map<String, Boolean> worlds = new HashMap<>();
private final Map<UserImportData, String> uuidsMissing = new HashMap<>();
private final Map<UserImportData, String> namesMissing = new HashMap<>();
private final Map<UserImportData, String> missingUUIDs = new HashMap<>();
private final Map<UserImportData, String> missingNames = new HashMap<>();
private final Map<UserImportData, String> foundUUIDs = new HashMap<>();
private final Map<UserImportData, String> foundNames = new HashMap<>();
public UserImportRefiner(Plan plugin, List<UserImportData> importers) {
public BukkitUserImportRefiner(Plan plugin, List<UserImportData> importers) {
this.plugin = plugin;
this.timings = plugin.getTimings();
this.importers.addAll(importers);
@ -62,44 +61,11 @@ public class UserImportRefiner {
timings.start(benchmarkName);
processMissingIdentifiers();
processOldWorlds();
timings.end(DebugChannels.IMPORTING, benchmarkName);
return importers;
}
private void processOldWorlds() {
String benchmarkName = "Processing old worlds";
timings.start(benchmarkName);
importers.parallelStream()
.flatMap(importer -> importer.getWorldTimes().keySet().stream())
.forEach(this::checkOldWorld);
if (!worlds.containsValue(true)) {
return;
}
worlds.values().removeIf(old -> false);
importers.parallelStream()
.forEach(importer -> importer.getWorldTimes().keySet().removeAll(worlds.keySet()));
timings.end(DebugChannels.IMPORTING, benchmarkName);
}
private void checkOldWorld(String worldName) {
if (worlds.containsKey(worldName)) {
return;
}
World world = plugin.getServer().getWorld(worldName);
boolean old = world == null;
worlds.put(worldName, old);
}
private void processMissingIdentifiers() {
String benchmarkName = "Processing missing identifiers";
@ -117,9 +83,9 @@ public class UserImportRefiner {
if (nameNull && uuidNull) {
invalidData.add(importer);
} else if (nameNull) {
namesMissing.put(importer, uuid.toString());
missingNames.put(importer, uuid.toString());
} else if (uuidNull) {
uuidsMissing.put(importer, name);
missingUUIDs.put(importer, name);
}
});
@ -152,13 +118,13 @@ public class UserImportRefiner {
userImportData.setUuid(uuid);
});
importers.removeAll(uuidsMissing.keySet());
importers.removeAll(missingUUIDs.keySet());
timings.end(DebugChannels.IMPORTING, benchmarkName);
}
private void addMissingUUIDsOverFetcher() {
UUIDFetcher uuidFetcher = new UUIDFetcher(new ArrayList<>(uuidsMissing.values()));
UUIDFetcher uuidFetcher = new UUIDFetcher(new ArrayList<>(missingUUIDs.values()));
Map<String, String> result;
@ -175,7 +141,7 @@ public class UserImportRefiner {
private void addMissingUUIDsOverOfflinePlayer() {
Map<String, String> result = new HashMap<>();
for (String name : uuidsMissing.values()) {
for (String name : missingUUIDs.values()) {
String uuid = getUuidByOfflinePlayer(name);
if (uuid == null) {
@ -191,7 +157,7 @@ public class UserImportRefiner {
private void addFoundUUIDs(Map<String, String> foundUUIDs) {
List<UserImportData> found = new ArrayList<>();
uuidsMissing.entrySet().parallelStream().forEach((entry) -> {
missingUUIDs.entrySet().parallelStream().forEach(entry -> {
UserImportData importer = entry.getKey();
String name = entry.getValue();
@ -201,7 +167,7 @@ public class UserImportRefiner {
found.add(importer);
});
uuidsMissing.keySet().removeAll(found);
missingUUIDs.keySet().removeAll(found);
}
@SuppressWarnings("deprecation")
@ -220,19 +186,19 @@ public class UserImportRefiner {
timings.start(benchmarkNames);
addMissingNames();
findMissingNames();
foundNames.entrySet().parallelStream().forEach(entry -> entry.getKey().setName(entry.getValue()));
importers.removeAll(namesMissing.keySet());
importers.removeAll(missingNames.keySet());
timings.end(DebugChannels.IMPORTING, benchmarkNames);
}
private void addMissingNames() {
private void findMissingNames() {
Map<String, String> result = new HashMap<>();
namesMissing.values().parallelStream().forEach(uuid -> {
missingNames.values().parallelStream().forEach(uuid -> {
String name = getNameByOfflinePlayer(uuid);
result.put(uuid, name);
@ -244,7 +210,7 @@ public class UserImportRefiner {
private void addFoundNames(Map<String, String> foundNames) {
List<UserImportData> found = new ArrayList<>();
namesMissing.entrySet().parallelStream().forEach(entry -> {
missingNames.entrySet().parallelStream().forEach(entry -> {
UserImportData importer = entry.getKey();
String uuid = entry.getValue();
@ -254,7 +220,7 @@ public class UserImportRefiner {
found.add(importer);
});
namesMissing.keySet().removeAll(found);
missingNames.keySet().removeAll(found);
}
private String getNameByOfflinePlayer(String uuid) {

View File

@ -17,24 +17,24 @@
package com.djrapitops.plan.system.importing.importers;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.container.BaseUser;
import com.djrapitops.plan.data.container.GeoInfo;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.container.UserInfo;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.LargeStoreQueries;
import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.system.cache.GeolocationCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
import com.djrapitops.plan.system.database.databases.operation.SaveOperations;
import com.djrapitops.plan.system.importing.data.BukkitUserImportRefiner;
import com.djrapitops.plan.system.importing.data.ServerImportData;
import com.djrapitops.plan.system.importing.data.UserImportData;
import com.djrapitops.plan.system.importing.data.UserImportRefiner;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.utilities.SHA256Hash;
import com.djrapitops.plugin.utilities.Verify;
import com.google.common.collect.ImmutableMap;
import java.security.NoSuchAlgorithmException;
import java.util.*;
@ -45,6 +45,8 @@ import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Generic importer for user data into Plan on the Bukkit platform.
*
* @author Fuzzlemann
*/
public abstract class BukkitImporter implements Importer {
@ -88,17 +90,12 @@ public abstract class BukkitImporter implements Importer {
public final void processImport() {
ExecutorService service = Executors.newCachedThreadPool();
submitTo(service, this::processServerData);
submitTo(service, this::processUserData);
service.shutdown();
try {
service.awaitTermination(20, TimeUnit.MINUTES);
} catch (InterruptedException e) {
service.shutdownNow();
Thread.currentThread().interrupt();
service.submit(this::processServerData);
service.submit(this::processUserData);
} finally {
shutdownService(service);
}
}
private void processServerData() {
@ -108,19 +105,13 @@ public abstract class BukkitImporter implements Importer {
return;
}
ExecutorService service = Executors.newCachedThreadPool();
SaveOperations save = dbSystem.getDatabase().save();
submitTo(service, () -> save.insertTPS(ImmutableMap.of(serverUUID.get(), serverImportData.getTpsData())));
submitTo(service, () -> save.insertCommandUsage(ImmutableMap.of(serverUUID.get(), serverImportData.getCommandUsages())));
service.shutdown();
try {
service.awaitTermination(20, TimeUnit.MINUTES);
} catch (InterruptedException e) {
service.shutdownNow();
Thread.currentThread().interrupt();
}
dbSystem.getDatabase().executeTransaction(new Transaction() {
@Override
protected void performOperations() {
execute(LargeStoreQueries.storeAllTPSData(Collections.singletonMap(serverUUID.get(), serverImportData.getTpsData())));
execute(LargeStoreQueries.storeAllCommandUsageData(Collections.singletonMap(serverUUID.get(), serverImportData.getCommandUsages())));
}
});
}
private void processUserData() {
@ -130,70 +121,76 @@ public abstract class BukkitImporter implements Importer {
return;
}
UserImportRefiner userImportRefiner = new UserImportRefiner(plugin, userImportData);
BukkitUserImportRefiner userImportRefiner = new BukkitUserImportRefiner(plugin, userImportData);
userImportData = userImportRefiner.refineData();
FetchOperations fetch = dbSystem.getDatabase().fetch();
Set<UUID> existingUUIDs = fetch.getSavedUUIDs();
Set<UUID> existingUserInfoTableUUIDs = fetch.getSavedUUIDs(serverUUID.get());
Database db = dbSystem.getDatabase();
Map<UUID, UserInfo> users = new HashMap<>();
Set<UUID> existingUUIDs = db.query(UserIdentifierQueries.fetchAllPlayerUUIDs());
Set<UUID> existingUserInfoTableUUIDs = db.query(UserIdentifierQueries.fetchPlayerUUIDsOfServer(serverUUID.get()));
Map<UUID, BaseUser> users = new HashMap<>();
List<UserInfo> userInfo = new ArrayList<>();
Map<UUID, List<Nickname>> nickNames = new HashMap<>();
Map<UUID, List<Session>> sessions = new HashMap<>();
List<Session> sessions = new ArrayList<>();
Map<UUID, List<GeoInfo>> geoInfo = new HashMap<>();
Map<UUID, Integer> timesKicked = new HashMap<>();
userImportData.parallelStream().forEach(data -> {
UUID uuid = data.getUuid();
UserInfo info = toUserInfo(data);
if (!existingUUIDs.contains(uuid)) {
users.put(uuid, info);
users.put(uuid, toBaseUser(data));
}
if (!existingUserInfoTableUUIDs.contains(uuid)) {
userInfo.add(info);
userInfo.add(toUserInfo(data));
}
nickNames.put(uuid, data.getNicknames());
geoInfo.put(uuid, convertGeoInfo(data));
timesKicked.put(uuid, data.getTimesKicked());
sessions.put(uuid, Collections.singletonList(toSession(data)));
sessions.add(toSession(data));
});
ExecutorService service = Executors.newCachedThreadPool();
SaveOperations save = dbSystem.getDatabase().save();
save.insertUsers(users);
submitTo(service, () -> save.insertSessions(ImmutableMap.of(serverUUID.get(), sessions), true));
submitTo(service, () -> save.kickAmount(timesKicked));
submitTo(service, () -> save.insertUserInfo(ImmutableMap.of(serverUUID.get(), userInfo)));
submitTo(service, () -> save.insertNicknames(ImmutableMap.of(serverUUID.get(), nickNames)));
submitTo(service, () -> save.insertAllGeoInfo(geoInfo));
db.executeTransaction(new Transaction() {
@Override
protected void performOperations() {
execute(LargeStoreQueries.storeAllCommonUserInformation(users.values()));
execute(LargeStoreQueries.storeAllSessionsWithKillAndWorldData(sessions));
Map<UUID, List<UserInfo>> userInformation = Collections.singletonMap(serverUUID.get(), userInfo);
execute(LargeStoreQueries.storePerServerUserInformation(userInformation));
execute(LargeStoreQueries.storeAllNicknameData(Collections.singletonMap(serverUUID.get(), nickNames)));
execute(LargeStoreQueries.storeAllGeoInformation(geoInfo));
}
});
}
private void shutdownService(ExecutorService service) {
service.shutdown();
try {
service.awaitTermination(20, TimeUnit.MINUTES);
if (!service.awaitTermination(20, TimeUnit.MINUTES)) {
service.shutdownNow();
}
} catch (InterruptedException e) {
service.shutdownNow();
Thread.currentThread().interrupt();
}
}
private void submitTo(ExecutorService service, ImportExecutorHelper helper) {
helper.submit(service);
private BaseUser toBaseUser(UserImportData userImportData) {
UUID uuid = userImportData.getUuid();
String name = userImportData.getName();
long registered = userImportData.getRegistered();
int timesKicked = userImportData.getTimesKicked();
return new BaseUser(uuid, name, registered, timesKicked);
}
private UserInfo toUserInfo(UserImportData userImportData) {
UUID uuid = userImportData.getUuid();
String name = userImportData.getName();
long registered = userImportData.getRegistered();
boolean op = userImportData.isOp();
boolean banned = userImportData.isBanned();
return new UserInfo(uuid, name, registered, op, banned);
return new UserInfo(uuid, serverUUID.get(), registered, op, banned);
}
private Session toSession(UserImportData userImportData) {
@ -221,18 +218,4 @@ public abstract class BukkitImporter implements Importer {
}
}).collect(Collectors.toList());
}
private interface ImportExecutorHelper {
void execute() throws DBException;
default void submit(ExecutorService service) {
service.submit(() -> {
try {
execute();
} catch (DBException e) {
throw new DBOpException("Import Execution failed", e);
}
});
}
}
}

View File

@ -16,8 +16,11 @@
*/
package com.djrapitops.plan.system.listeners.bukkit;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.player.PlayerProcessors;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.db.access.transactions.events.NicknameStoreTransaction;
import com.djrapitops.plan.system.cache.NicknameCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import org.bukkit.entity.Player;
@ -36,18 +39,21 @@ import java.util.UUID;
*/
public class ChatListener implements Listener {
private final PlayerProcessors processorFactory;
private final Processing processing;
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
private final NicknameCache nicknameCache;
private final ErrorHandler errorHandler;
@Inject
public ChatListener(
PlayerProcessors processorFactory,
Processing processing,
ServerInfo serverInfo,
DBSystem dbSystem,
NicknameCache nicknameCache,
ErrorHandler errorHandler
) {
this.processorFactory = processorFactory;
this.processing = processing;
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.nicknameCache = nicknameCache;
this.errorHandler = errorHandler;
}
@ -65,10 +71,14 @@ public class ChatListener implements Listener {
}
private void actOnChatEvent(AsyncPlayerChatEvent event) {
Player p = event.getPlayer();
UUID uuid = p.getUniqueId();
String name = p.getName();
String displayName = p.getDisplayName();
processing.submit(processorFactory.nameProcessor(uuid, name, displayName));
long time = System.currentTimeMillis();
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
String displayName = player.getDisplayName();
dbSystem.getDatabase().executeTransaction(new NicknameStoreTransaction(
uuid, new Nickname(displayName, time, serverInfo.getServerUUID()),
(playerUUID, name) -> name.equals(nicknameCache.getDisplayName(playerUUID))
));
}
}

View File

@ -17,8 +17,9 @@
package com.djrapitops.plan.system.listeners.bukkit;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
import com.djrapitops.plan.db.access.transactions.events.CommandStoreTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DataGatheringSettings;
@ -41,22 +42,22 @@ public class CommandListener implements Listener {
private final Plan plugin;
private final PlanConfig config;
private final Processors processors;
private final Processing processing;
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
private final ErrorHandler errorHandler;
@Inject
public CommandListener(
Plan plugin,
PlanConfig config,
Processors processors,
Processing processing,
ServerInfo serverInfo,
DBSystem dbSystem,
ErrorHandler errorHandler
) {
this.plugin = plugin;
this.config = config;
this.processors = processors;
this.processing = processing;
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.errorHandler = errorHandler;
}
@ -90,7 +91,7 @@ public class CommandListener implements Listener {
commandName = command.getName();
}
}
processing.submit(processors.commandProcessor(commandName));
dbSystem.getDatabase().executeTransaction(new CommandStoreTransaction(serverInfo.getServerUUID(), commandName));
}
private Command getBukkitCommand(String commandName) {

View File

@ -17,7 +17,10 @@
package com.djrapitops.plan.system.listeners.bukkit;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.db.access.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.WorldAliasSettings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@ -39,14 +42,20 @@ import java.util.UUID;
public class GameModeChangeListener implements Listener {
private final WorldAliasSettings worldAliasSettings;
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
private final ErrorHandler errorHandler;
@Inject
public GameModeChangeListener(
WorldAliasSettings worldAliasSettings,
ServerInfo serverInfo,
DBSystem dbSystem,
ErrorHandler errorHandler
) {
this.worldAliasSettings = worldAliasSettings;
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.errorHandler = errorHandler;
}
@ -69,6 +78,7 @@ public class GameModeChangeListener implements Listener {
String gameMode = event.getNewGameMode().name();
String worldName = player.getWorld().getName();
dbSystem.getDatabase().executeTransaction(new WorldNameStoreTransaction(serverInfo.getServerUUID(), worldName));
worldAliasSettings.addWorld(worldName);
Optional<Session> cachedSession = SessionCache.getCachedSession(uuid);

View File

@ -17,7 +17,13 @@
package com.djrapitops.plan.system.listeners.bukkit;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.events.*;
import com.djrapitops.plan.system.cache.GeolocationCache;
import com.djrapitops.plan.system.cache.NicknameCache;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
@ -26,7 +32,6 @@ import com.djrapitops.plan.system.settings.paths.DataGatheringSettings;
import com.djrapitops.plan.system.status.Status;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.RunnableFactory;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -51,10 +56,12 @@ public class PlayerOnlineListener implements Listener {
private final Processors processors;
private final Processing processing;
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
private final GeolocationCache geolocationCache;
private final NicknameCache nicknameCache;
private final SessionCache sessionCache;
private final ErrorHandler errorHandler;
private final Status status;
private final RunnableFactory runnableFactory;
@Inject
public PlayerOnlineListener(
@ -62,18 +69,22 @@ public class PlayerOnlineListener implements Listener {
Processors processors,
Processing processing,
ServerInfo serverInfo,
DBSystem dbSystem,
GeolocationCache geolocationCache,
NicknameCache nicknameCache,
SessionCache sessionCache,
Status status,
RunnableFactory runnableFactory,
ErrorHandler errorHandler
) {
this.config = config;
this.processors = processors;
this.processing = processing;
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.geolocationCache = geolocationCache;
this.nicknameCache = nicknameCache;
this.sessionCache = sessionCache;
this.status = status;
this.runnableFactory = runnableFactory;
this.errorHandler = errorHandler;
}
@ -81,10 +92,11 @@ public class PlayerOnlineListener implements Listener {
public void onPlayerLogin(PlayerLoginEvent event) {
try {
PlayerLoginEvent.Result result = event.getResult();
UUID uuid = event.getPlayer().getUniqueId();
boolean op = event.getPlayer().isOp();
UUID playerUUID = event.getPlayer().getUniqueId();
boolean operator = event.getPlayer().isOp();
boolean banned = result == PlayerLoginEvent.Result.KICK_BANNED;
processing.submit(processors.player().banAndOpProcessor(uuid, () -> banned, op));
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, () -> banned));
dbSystem.getDatabase().executeTransaction(new OperatorStatusTransaction(playerUUID, operator));
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
@ -109,7 +121,7 @@ public class PlayerOnlineListener implements Listener {
return;
}
processing.submit(processors.player().kickProcessor(uuid));
dbSystem.getDatabase().executeTransaction(new KickStoreTransaction(uuid));
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
@ -126,9 +138,9 @@ public class PlayerOnlineListener implements Listener {
private void actOnJoinEvent(PlayerJoinEvent event) {
Player player = event.getPlayer();
// TODO Move update notification to the website.
UUID uuid = player.getUniqueId();
UUID serverUUID = serverInfo.getServerUUID();
long time = System.currentTimeMillis();
AFKListener.AFK_TRACKER.performedAction(uuid, time);
@ -136,21 +148,32 @@ public class PlayerOnlineListener implements Listener {
String world = player.getWorld().getName();
String gm = player.getGameMode().name();
Database database = dbSystem.getDatabase();
database.executeTransaction(new WorldNameStoreTransaction(serverUUID, world));
InetAddress address = player.getAddress().getAddress();
String playerName = player.getName();
String displayName = player.getDisplayName();
boolean gatheringGeolocations = config.isTrue(DataGatheringSettings.GEOLOCATIONS);
if (gatheringGeolocations) {
database.executeTransaction(
new GeoInfoStoreTransaction(uuid, address, time, geolocationCache::getCountry)
);
}
database.executeTransaction(new PlayerServerRegisterTransaction(uuid, player::getFirstPlayed, playerName, serverUUID));
sessionCache.cacheSession(uuid, new Session(uuid, serverUUID, time, world, gm))
.ifPresent(previousSession -> database.executeTransaction(new SessionEndTransaction(previousSession)));
database.executeTransaction(new NicknameStoreTransaction(
uuid, new Nickname(displayName, time, serverUUID),
(playerUUID, name) -> name.equals(nicknameCache.getDisplayName(playerUUID))
));
processing.submitNonCritical(processors.info().playerPageUpdateProcessor(uuid));
processing.submitCritical(() -> sessionCache.cacheSession(uuid, new Session(uuid, serverInfo.getServerUUID(), time, world, gm)));
runnableFactory.create("Player Register: " + uuid,
processors.player().registerProcessor(uuid, player::getFirstPlayed, playerName,
gatheringGeolocations ? processors.player().ipUpdateProcessor(uuid, address, time) : null,
processors.player().nameProcessor(uuid, playerName, displayName),
processors.info().playerPageUpdateProcessor(uuid)
)
).runTaskAsynchronously();
}
@EventHandler(priority = EventPriority.MONITOR)
@ -165,12 +188,17 @@ public class PlayerOnlineListener implements Listener {
private void actOnQuitEvent(PlayerQuitEvent event) {
long time = System.currentTimeMillis();
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
UUID playerUUID = player.getUniqueId();
AFKListener.AFK_TRACKER.loggedOut(uuid, time);
AFKListener.AFK_TRACKER.loggedOut(playerUUID, time);
processing.submit(processors.player().banAndOpProcessor(uuid, player::isBanned, player.isOp()));
processing.submit(processors.player().endSessionProcessor(uuid, time));
processing.submit(processors.info().playerPageUpdateProcessor(uuid));
nicknameCache.removeDisplayName(playerUUID);
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, player::isBanned));
sessionCache.endSession(playerUUID, time)
.ifPresent(endedSession -> dbSystem.getDatabase().executeTransaction(new SessionEndTransaction(endedSession)));
processing.submit(processors.info().playerPageUpdateProcessor(playerUUID));
}
}

View File

@ -17,7 +17,10 @@
package com.djrapitops.plan.system.listeners.bukkit;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.db.access.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.WorldAliasSettings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@ -34,14 +37,20 @@ import java.util.UUID;
public class WorldChangeListener implements Listener {
private final WorldAliasSettings worldAliasSettings;
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
private final ErrorHandler errorHandler;
@Inject
public WorldChangeListener(
WorldAliasSettings worldAliasSettings,
ServerInfo serverInfo,
DBSystem dbSystem,
ErrorHandler errorHandler
) {
this.worldAliasSettings = worldAliasSettings;
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.errorHandler = errorHandler;
}
@ -63,6 +72,7 @@ public class WorldChangeListener implements Listener {
String worldName = player.getWorld().getName();
String gameMode = player.getGameMode().name();
dbSystem.getDatabase().executeTransaction(new WorldNameStoreTransaction(serverInfo.getServerUUID(), worldName));
worldAliasSettings.addWorld(worldName);
Optional<Session> cachedSession = SessionCache.getCachedSession(uuid);

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.system.tasks;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.ShutdownHook;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.system.tasks.bukkit.BukkitTPSCountTimer;
@ -32,6 +33,7 @@ import com.djrapitops.plugin.task.RunnableFactory;
import org.bukkit.Bukkit;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@ -40,12 +42,14 @@ import java.util.concurrent.TimeUnit;
*
* @author Rsl1122
*/
@Singleton
public class BukkitTaskSystem extends ServerTaskSystem {
private final Plan plugin;
private final ShutdownHook shutdownHook;
private final PingCountTimerBukkit pingCountTimer;
private final ConfigStoreTask configStoreTask;
private final DBCleanTask dbCleanTask;
@Inject
public BukkitTaskSystem(
@ -60,7 +64,8 @@ public class BukkitTaskSystem extends ServerTaskSystem {
PingCountTimerBukkit pingCountTimer,
LogsFolderCleanTask logsFolderCleanTask,
PlayersPageRefreshTask playersPageRefreshTask,
ConfigStoreTask configStoreTask
ConfigStoreTask configStoreTask,
DBCleanTask dbCleanTask
) {
super(
runnableFactory,
@ -74,6 +79,7 @@ public class BukkitTaskSystem extends ServerTaskSystem {
this.shutdownHook = shutdownHook;
this.pingCountTimer = pingCountTimer;
this.configStoreTask = configStoreTask;
this.dbCleanTask = dbCleanTask;
}
@Override
@ -88,6 +94,11 @@ public class BukkitTaskSystem extends ServerTaskSystem {
// +40 ticks / 2 seconds so that update check task runs first.
long storeDelay = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MILLISECONDS) + 40;
registerTask("Config Store Task", configStoreTask).runTaskLaterAsynchronously(storeDelay);
registerTask("DB Clean Task", dbCleanTask).runTaskTimerAsynchronously(
TimeAmount.toTicks(20, TimeUnit.SECONDS),
TimeAmount.toTicks(config.get(TimeSettings.CLEAN_DATABASE_PERIOD), TimeUnit.MILLISECONDS)
);
} catch (ExceptionInInitializerError | NoClassDefFoundError ignore) {
// Running CraftBukkit
}

View File

@ -19,9 +19,9 @@ package com.djrapitops.plan.system.tasks.bukkit;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.container.builders.TPSBuilder;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.info.server.properties.ServerProperties;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
import com.djrapitops.plan.system.tasks.TPSCountTimer;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@ -41,13 +41,13 @@ public class BukkitTPSCountTimer extends TPSCountTimer {
@Inject
public BukkitTPSCountTimer(
Plan plugin,
Processors processors,
Processing processing,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerProperties serverProperties,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(processors, processing, logger, errorHandler);
super(dbSystem, serverInfo, logger, errorHandler);
this.plugin = plugin;
this.serverProperties = serverProperties;
lastCheckNano = -1;

View File

@ -19,9 +19,8 @@ package com.djrapitops.plan.system.tasks.bukkit;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.container.builders.TPSBuilder;
import com.djrapitops.plan.system.info.server.properties.ServerProperties;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import org.bukkit.World;
@ -33,13 +32,12 @@ public class PaperTPSCountTimer extends BukkitTPSCountTimer {
@Inject
public PaperTPSCountTimer(
Plan plugin,
Processors processors,
Processing processing,
ServerProperties serverProperties,
DBSystem dbSystem,
ServerInfo serverInfo,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(plugin, processors, processing, serverProperties, logger, errorHandler);
super(plugin, dbSystem, serverInfo, serverInfo.getServerProperties(), logger, errorHandler);
}
@Override

View File

@ -24,8 +24,9 @@
package com.djrapitops.plan.system.tasks.bukkit;
import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
import com.djrapitops.plan.db.access.transactions.events.PingStoreTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.utilities.java.Reflection;
@ -107,20 +108,20 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final PlanConfig config;
private final Processors processors;
private final Processing processing;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
@Inject
public PingCountTimerBukkit(
PlanConfig config,
Processors processors,
Processing processing,
DBSystem dbSystem,
ServerInfo serverInfo,
RunnableFactory runnableFactory
) {
this.config = config;
this.processors = processors;
this.processing = processing;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory;
playerHistory = new HashMap<>();
}
@ -153,7 +154,9 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
}
history.add(new DateObj<>(time, ping));
if (history.size() >= 30) {
processing.submit(processors.player().pingInsertProcessor(uuid, new ArrayList<>(history)));
dbSystem.getDatabase().executeTransaction(
new PingStoreTransaction(uuid, serverInfo.getServerUUID(), new ArrayList<>(history))
);
history.clear();
}
} else {

View File

@ -16,7 +16,7 @@
*/
package com.djrapitops.plan;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import org.bstats.bungeecord.Metrics;
@ -49,7 +49,7 @@ public class BStatsBungee {
addStringSettingPie("server_type", serverType);
addStringSettingPie("database_type", databaseType);
addStringSettingPie("network_servers", connectionSystem.getBukkitServers().size());
addStringSettingPie("network_servers", connectionSystem.getDataServers().size());
}
protected void addStringSettingPie(String id, Serializable setting) {

View File

@ -17,7 +17,10 @@
package com.djrapitops.plan;
import com.djrapitops.plan.command.PlanProxyCommand;
import com.djrapitops.plan.modules.*;
import com.djrapitops.plan.modules.APFModule;
import com.djrapitops.plan.modules.FilesModule;
import com.djrapitops.plan.modules.ProxySuperClassBindingModule;
import com.djrapitops.plan.modules.SystemObjectProvidingModule;
import com.djrapitops.plan.modules.bungee.BungeeCommandModule;
import com.djrapitops.plan.modules.bungee.BungeePlanModule;
import com.djrapitops.plan.modules.bungee.BungeeServerPropertiesModule;
@ -38,7 +41,6 @@ import javax.inject.Singleton;
@Component(modules = {
BungeePlanModule.class,
BungeeCommandModule.class,
SuperClassBindingModule.class,
SystemObjectProvidingModule.class,
APFModule.class,
FilesModule.class,

View File

@ -18,8 +18,10 @@ package com.djrapitops.plan.system.info.server;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.db.access.transactions.StoreServerInformationTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.server.properties.ServerProperties;
import com.djrapitops.plan.system.webserver.WebServer;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -29,6 +31,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
/**
* Manages Server information on the Bungee instance.
@ -61,15 +64,17 @@ public class BungeeServerInfo extends ServerInfo {
try {
Database database = dbSystem.getDatabase();
Optional<Server> bungeeInfo = database.fetch().getBungeeInformation();
if (bungeeInfo.isPresent()) {
server = bungeeInfo.get();
Optional<Server> proxyInfo = database.query(ServerQueries.fetchProxyServerInformation());
if (proxyInfo.isPresent()) {
server = proxyInfo.get();
updateServerInfo(database);
} else {
server = registerBungeeInfo(database);
}
} catch (DBOpException e) {
} catch (DBOpException | ExecutionException e) {
throw new EnableException("Failed to read Server information from Database.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return server;
}
@ -78,7 +83,7 @@ public class BungeeServerInfo extends ServerInfo {
String accessAddress = webServer.get().getAccessAddress();
if (!accessAddress.equals(server.getWebAddress())) {
server.setWebAddress(accessAddress);
db.save().serverInfoForThisServer(server);
db.executeTransaction(new StoreServerInformationTransaction(server));
}
}
@ -91,26 +96,18 @@ public class BungeeServerInfo extends ServerInfo {
}
}
private Server registerBungeeInfo(Database db) throws EnableException {
private Server registerBungeeInfo(Database db) throws EnableException, ExecutionException, InterruptedException {
UUID serverUUID = generateNewUUID();
String accessAddress = webServer.get().getAccessAddress();
Server bungeeCord = new Server(-1, serverUUID, "BungeeCord", accessAddress, serverProperties.getMaxPlayers());
db.save().serverInfoForThisServer(bungeeCord);
Server proxy = new Server(-1, serverUUID, "BungeeCord", accessAddress, serverProperties.getMaxPlayers());
db.executeTransaction(new StoreServerInformationTransaction(proxy))
.get();
Optional<Server> bungeeInfo = db.fetch().getBungeeInformation();
if (bungeeInfo.isPresent()) {
return bungeeInfo.get();
Optional<Server> proxyInfo = db.query(ServerQueries.fetchProxyServerInformation());
if (proxyInfo.isPresent()) {
return proxyInfo.get();
}
throw new EnableException("BungeeCord registration failed (DB)");
}
private UUID generateNewUUID() {
String seed = serverProperties.getName() +
serverProperties.getIp() +
serverProperties.getPort() +
serverProperties.getVersion() +
serverProperties.getImplVersion();
return UUID.nameUUIDFromBytes(seed.getBytes());
}
}

View File

@ -17,7 +17,12 @@
package com.djrapitops.plan.system.listeners.bungee;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.events.GeoInfoStoreTransaction;
import com.djrapitops.plan.db.access.transactions.events.PlayerRegisterTransaction;
import com.djrapitops.plan.system.cache.GeolocationCache;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
@ -48,6 +53,8 @@ public class PlayerOnlineListener implements Listener {
private final PlanConfig config;
private final Processors processors;
private final Processing processing;
private final DBSystem dbSystem;
private final GeolocationCache geolocationCache;
private final SessionCache sessionCache;
private final ServerInfo serverInfo;
private final ErrorHandler errorHandler;
@ -57,6 +64,8 @@ public class PlayerOnlineListener implements Listener {
PlanConfig config,
Processors processors,
Processing processing,
DBSystem dbSystem,
GeolocationCache geolocationCache,
SessionCache sessionCache,
ServerInfo serverInfo,
ErrorHandler errorHandler
@ -64,6 +73,8 @@ public class PlayerOnlineListener implements Listener {
this.config = config;
this.processors = processors;
this.processing = processing;
this.dbSystem = dbSystem;
this.geolocationCache = geolocationCache;
this.sessionCache = sessionCache;
this.serverInfo = serverInfo;
this.errorHandler = errorHandler;
@ -73,19 +84,23 @@ public class PlayerOnlineListener implements Listener {
public void onPostLogin(PostLoginEvent event) {
try {
ProxiedPlayer player = event.getPlayer();
UUID uuid = player.getUniqueId();
UUID playerUUID = player.getUniqueId();
String name = player.getName();
InetAddress address = player.getAddress().getAddress();
long time = System.currentTimeMillis();
sessionCache.cacheSession(uuid, new Session(uuid, serverInfo.getServerUUID(), time, null, null));
sessionCache.cacheSession(playerUUID, new Session(playerUUID, serverInfo.getServerUUID(), time, null, null));
Database database = dbSystem.getDatabase();
boolean gatheringGeolocations = config.isTrue(DataGatheringSettings.GEOLOCATIONS);
if (gatheringGeolocations) {
database.executeTransaction(
new GeoInfoStoreTransaction(playerUUID, address, time, geolocationCache::getCountry)
);
}
processing.submit(processors.player().proxyRegisterProcessor(uuid, name, time,
gatheringGeolocations ? processors.player().ipUpdateProcessor(uuid, address, time) : null
));
processing.submit(processors.info().playerPageUpdateProcessor(uuid));
database.executeTransaction(new PlayerRegisterTransaction(playerUUID, () -> time, name));
processing.submit(processors.info().playerPageUpdateProcessor(playerUUID));
ResponseCache.clearResponse(PageId.SERVER.of(serverInfo.getServerUUID()));
} catch (Exception e) {
errorHandler.log(L.WARN, this.getClass(), e);
@ -96,10 +111,10 @@ public class PlayerOnlineListener implements Listener {
public void onLogout(PlayerDisconnectEvent event) {
try {
ProxiedPlayer player = event.getPlayer();
UUID uuid = player.getUniqueId();
UUID playerUUID = player.getUniqueId();
sessionCache.endSession(uuid, System.currentTimeMillis());
processing.submit(processors.info().playerPageUpdateProcessor(uuid));
sessionCache.endSession(playerUUID, System.currentTimeMillis());
processing.submit(processors.info().playerPageUpdateProcessor(playerUUID));
ResponseCache.clearResponse(PageId.SERVER.of(serverInfo.getServerUUID()));
} catch (Exception e) {
errorHandler.log(L.WARN, this.getClass(), e);
@ -110,12 +125,12 @@ public class PlayerOnlineListener implements Listener {
public void onServerSwitch(ServerSwitchEvent event) {
try {
ProxiedPlayer player = event.getPlayer();
UUID uuid = player.getUniqueId();
UUID playerUUID = player.getUniqueId();
long time = System.currentTimeMillis();
// Replaces the current session in the cache.
sessionCache.cacheSession(uuid, new Session(uuid, serverInfo.getServerUUID(), time, null, null));
processing.submit(processors.info().playerPageUpdateProcessor(uuid));
sessionCache.cacheSession(playerUUID, new Session(playerUUID, serverInfo.getServerUUID(), time, null, null));
processing.submit(processors.info().playerPageUpdateProcessor(playerUUID));
} catch (Exception e) {
errorHandler.log(L.WARN, this.getClass(), e);
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.tasks;
import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.system.tasks.bungee.BungeeTPSCountTimer;
@ -27,6 +28,7 @@ import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
/**
@ -34,6 +36,7 @@ import java.util.concurrent.TimeUnit;
*
* @author Rsl1122
*/
@Singleton
public class BungeeTaskSystem extends TaskSystem {
private final PlanBungee plugin;
@ -43,6 +46,7 @@ public class BungeeTaskSystem extends TaskSystem {
private final LogsFolderCleanTask logsFolderCleanTask;
private final PlayersPageRefreshTask playersPageRefreshTask;
private final NetworkConfigStoreTask networkConfigStoreTask;
private final DBCleanTask dbCleanTask;
@Inject
public BungeeTaskSystem(
@ -54,7 +58,8 @@ public class BungeeTaskSystem extends TaskSystem {
PingCountTimerBungee pingCountTimer,
LogsFolderCleanTask logsFolderCleanTask,
PlayersPageRefreshTask playersPageRefreshTask,
NetworkConfigStoreTask networkConfigStoreTask
NetworkConfigStoreTask networkConfigStoreTask,
DBCleanTask dbCleanTask
) {
super(runnableFactory, bungeeTPSCountTimer);
this.plugin = plugin;
@ -65,6 +70,7 @@ public class BungeeTaskSystem extends TaskSystem {
this.logsFolderCleanTask = logsFolderCleanTask;
this.playersPageRefreshTask = playersPageRefreshTask;
this.networkConfigStoreTask = networkConfigStoreTask;
this.dbCleanTask = dbCleanTask;
}
@Override
@ -87,5 +93,10 @@ public class BungeeTaskSystem extends TaskSystem {
// +40 ticks / 2 seconds so that update check task runs first.
long storeDelay = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MILLISECONDS) + 40;
registerTask("Config Store Task", networkConfigStoreTask).runTaskLaterAsynchronously(storeDelay);
registerTask("DB Clean Task", dbCleanTask).runTaskTimerAsynchronously(
TimeAmount.toTicks(20, TimeUnit.SECONDS),
TimeAmount.toTicks(config.get(TimeSettings.CLEAN_DATABASE_PERIOD), TimeUnit.MILLISECONDS)
);
}
}

View File

@ -18,9 +18,9 @@ package com.djrapitops.plan.system.tasks.bungee;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.container.builders.TPSBuilder;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.info.server.properties.ServerProperties;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
import com.djrapitops.plan.system.tasks.TPSCountTimer;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@ -35,13 +35,13 @@ public class BungeeTPSCountTimer extends TPSCountTimer {
@Inject
public BungeeTPSCountTimer(
Processors processors,
Processing processing,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerProperties serverProperties,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(processors, processing, logger, errorHandler);
super(dbSystem, serverInfo, logger, errorHandler);
this.serverProperties = serverProperties;
}

View File

@ -24,8 +24,9 @@
package com.djrapitops.plan.system.tasks.bungee;
import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.processing.processors.Processors;
import com.djrapitops.plan.db.access.transactions.events.PingStoreTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plugin.api.TimeAmount;
@ -58,20 +59,20 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener {
private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final PlanConfig config;
private final Processors processors;
private final Processing processing;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
@Inject
public PingCountTimerBungee(
PlanConfig config,
Processors processors,
Processing processing,
DBSystem dbSystem,
ServerInfo serverInfo,
RunnableFactory runnableFactory
) {
this.config = config;
this.processors = processors;
this.processing = processing;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory;
playerHistory = new HashMap<>();
}
@ -94,7 +95,9 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener {
}
history.add(new DateObj<>(time, ping));
if (history.size() >= 30) {
processing.submit(processors.player().pingInsertProcessor(uuid, new ArrayList<>(history)));
dbSystem.getDatabase().executeTransaction(
new PingStoreTransaction(uuid, serverInfo.getServerUUID(), new ArrayList<>(history))
);
history.clear();
}
} else {

View File

@ -17,11 +17,13 @@
package com.djrapitops.plan;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.db.SQLiteDB;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.ProxySettings;
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
@ -62,7 +64,9 @@ public class BungeeSystemTest {
config.set(ProxySettings.IP, "8.8.8.8");
DBSystem dbSystem = bungeeSystem.getDatabaseSystem();
dbSystem.setActiveDatabase(dbSystem.getSqLiteFactory().usingDefaultFile());
SQLiteDB db = dbSystem.getSqLiteFactory().usingDefaultFile();
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
dbSystem.setActiveDatabase(db);
bungeeSystem.enable();
} finally {
@ -82,7 +86,9 @@ public class BungeeSystemTest {
config.set(ProxySettings.IP, "0.0.0.0");
DBSystem dbSystem = bungeeSystem.getDatabaseSystem();
dbSystem.setActiveDatabase(dbSystem.getSqLiteFactory().usingDefaultFile());
SQLiteDB db = dbSystem.getSqLiteFactory().usingDefaultFile();
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
dbSystem.setActiveDatabase(db);
bungeeSystem.enable();
assertTrue(bungeeSystem.isEnabled());

View File

@ -0,0 +1,135 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
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.db.Database;
import com.djrapitops.plan.db.access.transactions.events.ServerShutdownTransaction;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
/**
* Class in charge of performing save operations when the server shuts down.
*
* @author Rsl1122
*/
public abstract class ServerShutdownSave {
protected final PluginLogger logger;
private final DBSystem dbSystem;
private final Locale locale;
private final ErrorHandler errorHandler;
private boolean shuttingDown = false;
public ServerShutdownSave(
Locale locale,
DBSystem dbSystem,
PluginLogger logger,
ErrorHandler errorHandler
) {
this.locale = locale;
this.dbSystem = dbSystem;
this.logger = logger;
this.errorHandler = errorHandler;
}
protected abstract boolean checkServerShuttingDownStatus();
public void serverIsKnownToBeShuttingDown() {
shuttingDown = true;
}
public void performSave() {
if (!checkServerShuttingDownStatus() && !shuttingDown) {
return;
}
Map<UUID, Session> activeSessions = SessionCache.getActiveSessions();
if (activeSessions.isEmpty()) {
return;
}
// This check ensures that logging is not attempted on JVM shutdown.
// Underlying Logger might not be available leading to an exception.
if (!shuttingDown) {
logger.info(locale.getString(PluginLang.DISABLED_UNSAVED_SESSIONS));
}
attemptSave(activeSessions);
SessionCache.clear();
}
private void attemptSave(Map<UUID, Session> activeSessions) {
try {
prepareSessionsForStorage(activeSessions, System.currentTimeMillis());
saveActiveSessions(activeSessions);
} catch (DBInitException e) {
errorHandler.log(L.ERROR, this.getClass(), e);
} catch (IllegalStateException ignored) {
/* Database is not initialized */
} finally {
closeDatabase(dbSystem.getDatabase());
}
}
private void saveActiveSessions(Map<UUID, Session> activeSessions) {
Database database = dbSystem.getDatabase();
if (database.getState() == Database.State.CLOSED) {
// Ensure that database is not closed when performing the transaction.
database.init();
}
saveSessions(activeSessions, database);
}
private void prepareSessionsForStorage(Map<UUID, Session> activeSessions, long now) {
for (Session session : activeSessions.values()) {
Optional<Long> end = session.getValue(SessionKeys.END);
if (!end.isPresent()) {
session.endSession(now);
}
}
}
private void saveSessions(Map<UUID, Session> activeSessions, Database database) {
try {
database.executeTransaction(new ServerShutdownTransaction(activeSessions.values()))
.get(); // Ensure that the transaction is executed before shutdown.
} catch (ExecutionException | DBOpException e) {
errorHandler.log(L.ERROR, this.getClass(), e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void closeDatabase(Database database) {
database.close();
}
}

View File

@ -16,21 +16,8 @@
*/
package com.djrapitops.plan;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
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.cache.SessionCache;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import javax.inject.Singleton;
/**
* Thread that is run when JVM shuts down.
@ -39,16 +26,16 @@ import java.util.UUID;
*
* @author Rsl1122
*/
@Singleton
public class ShutdownHook extends Thread {
private static ShutdownHook activated;
private final DBSystem dbSystem;
private final ErrorHandler errorHandler;
private final ServerShutdownSave serverShutdownSave;
@Inject
public ShutdownHook(DBSystem dbSystem, ErrorHandler errorHandler) {
this.dbSystem = dbSystem;
this.errorHandler = errorHandler;
public ShutdownHook(ServerShutdownSave serverShutdownSave) {
this.serverShutdownSave = serverShutdownSave;
}
private static boolean isActivated() {
@ -74,41 +61,7 @@ public class ShutdownHook extends Thread {
@Override
public void run() {
try {
Map<UUID, Session> activeSessions = SessionCache.getActiveSessions();
long now = System.currentTimeMillis();
saveActiveSessions(activeSessions, now);
} catch (IllegalStateException ignored) {
/* Database is not initialized */
} catch (DBInitException e) {
errorHandler.log(L.ERROR, this.getClass(), e);
} finally {
try {
dbSystem.getDatabase().close();
} catch (DBException e) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
}
}
private void saveActiveSessions(Map<UUID, Session> activeSessions, long now) throws DBInitException {
for (Map.Entry<UUID, Session> entry : activeSessions.entrySet()) {
UUID uuid = entry.getKey();
Session session = entry.getValue();
Optional<Long> end = session.getValue(SessionKeys.END);
if (!end.isPresent()) {
session.endSession(now);
}
Database database = dbSystem.getDatabase();
if (!database.isOpen()) {
database.init();
}
try {
database.save().session(uuid, session);
} catch (DBOpException e) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
}
activeSessions.clear();
serverShutdownSave.serverIsKnownToBeShuttingDown();
serverShutdownSave.performSave();
}
}

View File

@ -16,11 +16,26 @@
*/
package com.djrapitops.plan.api;
import com.djrapitops.plan.api.data.PlayerContainer;
import com.djrapitops.plan.api.data.ServerContainer;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.plugin.HookHandler;
import com.djrapitops.plan.data.plugin.PluginData;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
import com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps;
import com.djrapitops.plan.utilities.uuid.UUIDUtility;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@ -30,17 +45,36 @@ import java.util.UUID;
*
* @author Rsl1122
*/
public abstract class CommonAPI implements PlanAPI {
@Singleton
public class CommonAPI implements PlanAPI {
private final DBSystem dbSystem;
private final UUIDUtility uuidUtility;
private final HookHandler hookHandler;
private final PluginLogger logger;
private final ErrorHandler errorHandler;
CommonAPI(UUIDUtility uuidUtility, ErrorHandler errorHandler) {
@Inject
public CommonAPI(
DBSystem dbSystem,
UUIDUtility uuidUtility,
HookHandler hookHandler,
PluginLogger logger,
ErrorHandler errorHandler
) {
this.dbSystem = dbSystem;
this.uuidUtility = uuidUtility;
this.hookHandler = hookHandler;
this.logger = logger;
this.errorHandler = errorHandler;
PlanAPIHolder.set(this);
}
@Override
public void addPluginDataSource(PluginData pluginData) {
hookHandler.addPluginDataSource(pluginData);
}
@Override
public String getPlayerInspectPageLink(UUID uuid) {
return getPlayerInspectPageLink(getPlayerName(uuid));
@ -59,11 +93,43 @@ public abstract class CommonAPI implements PlanAPI {
@Override
public Map<UUID, String> getKnownPlayerNames() {
try {
return fetchFromPlanDB().getPlayerNames();
return queryDB(UserIdentifierQueries.fetchAllPlayerNames());
} catch (DBOpException e) {
errorHandler.log(L.ERROR, this.getClass(), e);
return new HashMap<>();
}
}
@Override
public PlayerContainer fetchPlayerContainer(UUID uuid) {
return new PlayerContainer(queryDB(ContainerFetchQueries.fetchPlayerContainer(uuid)));
}
@Override
public ServerContainer fetchServerContainer(UUID serverUUID) {
return new ServerContainer(queryDB(ContainerFetchQueries.fetchServerContainer(serverUUID)));
}
@Override
public Collection<UUID> fetchServerUUIDs() {
return queryDB(ServerQueries.fetchPlanServerInformation()).keySet();
}
@Override
public String getPlayerName(UUID playerUUID) {
return queryDB(UserIdentifierQueries.fetchPlayerNameOf(playerUUID)).orElse(null);
}
@Override
public FetchOperations fetchFromPlanDB() {
logger.warn("PlanAPI#fetchFromPlanDB has been deprecated and will be removed in the future. Stack trace to follow");
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
logger.warn(element.toString());
}
return new SQLFetchOps(dbSystem.getDatabase());
}
private <T> T queryDB(Query<T> query) {
return dbSystem.getDatabase().query(query);
}
}

View File

@ -79,9 +79,7 @@ public interface PlanAPI {
* @param uuid UUID of the player.
* @return a {@link PlayerContainer}.
*/
default PlayerContainer fetchPlayerContainer(UUID uuid) {
return new PlayerContainer(fetchFromPlanDB().getPlayerContainer(uuid));
}
PlayerContainer fetchPlayerContainer(UUID uuid);
/**
* Fetch a ServerContainer from the database.
@ -91,16 +89,12 @@ public interface PlanAPI {
* @param serverUUID UUID of the server.
* @return a {@link ServerContainer}.
*/
default ServerContainer fetchServerContainer(UUID serverUUID) {
return new ServerContainer(fetchFromPlanDB().getServerContainer(serverUUID));
}
ServerContainer fetchServerContainer(UUID serverUUID);
/**
* Fetch server UUIDs.
*
* @return All Plan server UUIDs.
*/
default Collection<UUID> fetchServerUUIDs() {
return fetchFromPlanDB().getServerUUIDs();
}
Collection<UUID> fetchServerUUIDs();
}

View File

@ -1,68 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.api;
import com.djrapitops.plan.data.plugin.HookHandler;
import com.djrapitops.plan.data.plugin.PluginData;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
import com.djrapitops.plan.utilities.uuid.UUIDUtility;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.UUID;
/**
* PlanAPI extension for proxy servers.
*
* @author Rsl1122
*/
@Singleton
public class ProxyAPI extends CommonAPI {
private final HookHandler hookHandler;
private final DBSystem dbSystem;
@Inject
public ProxyAPI(
UUIDUtility uuidUtility,
DBSystem dbSystem,
HookHandler hookHandler,
ErrorHandler errorHandler
) {
super(uuidUtility, errorHandler);
this.dbSystem = dbSystem;
this.hookHandler = hookHandler;
}
@Override
public void addPluginDataSource(PluginData pluginData) {
hookHandler.addPluginDataSource(pluginData);
}
@Override
public String getPlayerName(UUID uuid) {
return dbSystem.getDatabase().fetch().getPlayerName(uuid);
}
@Override
public FetchOperations fetchFromPlanDB() {
return dbSystem.getDatabase().fetch();
}
}

View File

@ -1,67 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.api;
import com.djrapitops.plan.data.plugin.HookHandler;
import com.djrapitops.plan.data.plugin.PluginData;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
import com.djrapitops.plan.utilities.uuid.UUIDUtility;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.UUID;
/**
* PlanAPI extension for Bukkit
*
* @author Rsl1122
*/
@Singleton
public class ServerAPI extends CommonAPI {
private final HookHandler hookHandler;
private final DBSystem dbSystem;
@Inject
public ServerAPI(
UUIDUtility uuidUtility,
HookHandler hookHandler,
DBSystem dbSystem,
ErrorHandler errorHandler
) {
super(uuidUtility, errorHandler);
this.hookHandler = hookHandler;
this.dbSystem = dbSystem;
}
@Override
public void addPluginDataSource(PluginData pluginData) {
hookHandler.addPluginDataSource(pluginData);
}
@Override
public String getPlayerName(UUID uuid) {
return dbSystem.getDatabase().fetch().getPlayerName(uuid);
}
@Override
public FetchOperations fetchFromPlanDB() {
return dbSystem.getDatabase().fetch();
}
}

View File

@ -17,7 +17,7 @@
package com.djrapitops.plan.api.exceptions.connection;
/**
* Thrown when DBException occurs during InfoRequest#placeIntoDatabase.
* Thrown when {@link com.djrapitops.plan.api.exceptions.database.DBOpException} occurs during {@link com.djrapitops.plan.system.info.request.InfoRequest#placeIntoDatabase}.
*
* @author Rsl1122
*/

View File

@ -1,37 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.api.exceptions.database;
/**
* Thrown when something goes wrong with the Database, generic exception.
*
* @author Rsl1122
*/
public class DBException extends Exception {
public DBException(String message, Throwable cause) {
super(message, cause);
}
public DBException(Throwable cause) {
super(cause);
}
public DBException(String message) {
super(message);
}
}

View File

@ -21,16 +21,12 @@ package com.djrapitops.plan.api.exceptions.database;
*
* @author Rsl1122
*/
public class DBInitException extends FatalDBException {
public class DBInitException extends DBOpException {
public DBInitException(String message, Throwable cause) {
super(message, cause);
}
public DBInitException(Throwable cause) {
super(cause);
}
public DBInitException(String message) {
super(message);
}

View File

@ -23,7 +23,7 @@ import java.sql.SQLException;
*
* @author Rsl1122
*/
public class DBOpException extends RuntimeException {
public class DBOpException extends IllegalStateException {
public DBOpException(String message) {
super(message);

View File

@ -16,15 +16,7 @@
*/
package com.djrapitops.plan.api.exceptions.database;
public class FatalDBException extends DBException {
public FatalDBException(String message, Throwable cause) {
super(message, cause);
}
public FatalDBException(Throwable cause) {
super(cause);
}
public class FatalDBException extends DBOpException {
public FatalDBException(String message) {
super(message);

View File

@ -18,6 +18,9 @@ package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.db.access.queries.objects.WebUserQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.InfoSystem;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
@ -40,7 +43,6 @@ import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@ -89,6 +91,12 @@ public class AnalyzeCommand extends CommandNode {
@Override
public void onCommand(Sender sender, String commandLabel, String[] args) {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
processing.submitNonCritical(() -> {
@ -125,23 +133,16 @@ public class AnalyzeCommand extends CommandNode {
private void sendWebUserNotificationIfNecessary(Sender sender) {
if (webServer.isAuthRequired() &&
CommandUtils.isPlayer(sender) &&
!dbSystem.getDatabase().check().doesWebUserExists(sender.getName())) {
!dbSystem.getDatabase().query(WebUserQueries.fetchWebUser(sender.getName())).isPresent()) {
sender.sendMessage("§e" + locale.getString(CommandLang.NO_WEB_USER_NOTIFY));
}
}
private Optional<Server> getServer(String[] args) {
if (args.length >= 1 && connectionSystem.isServerAvailable()) {
Map<UUID, Server> bukkitServers = dbSystem.getDatabase().fetch().getBukkitServers();
String serverIdentifier = getGivenIdentifier(args);
for (Map.Entry<UUID, Server> entry : bukkitServers.entrySet()) {
Server server = entry.getValue();
if (Integer.toString(server.getId()).equals(serverIdentifier)
|| server.getName().equalsIgnoreCase(serverIdentifier)) {
return Optional.of(server);
}
}
return dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverIdentifier))
.filter(server -> !server.isProxy());
}
return Optional.empty();
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.locale.Locale;
@ -70,12 +71,15 @@ public class InfoCommand extends CommandNode {
String updateAvailable = versionCheckSystem.isNewVersionAvailable() ? yes : no;
String connectedToBungee = connectionSystem.isServerAvailable() ? yes : no;
Database database = dbSystem.getDatabase();
String[] messages = {
locale.getString(CommandLang.HEADER_INFO),
"",
locale.getString(CommandLang.INFO_VERSION, plugin.getVersion()),
locale.getString(CommandLang.INFO_UPDATE, updateAvailable),
locale.getString(CommandLang.INFO_DATABASE, dbSystem.getDatabase().getType().getName()),
locale.getString(CommandLang.INFO_DATABASE, database.getType().getName() + " (" + database.getState().name() + ")"),
locale.getString(CommandLang.INFO_BUNGEE_CONNECTION, connectedToBungee),
"",
">"

View File

@ -17,6 +17,9 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.PlayerFetchQueries;
import com.djrapitops.plan.db.access.queries.objects.WebUserQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.locale.Locale;
@ -90,25 +93,31 @@ public class InspectCommand extends CommandNode {
sender.sendMessage(locale.getString(CommandLang.FAIL_NO_PERMISSION));
}
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
runInspectTask(playerName, sender);
}
private void runInspectTask(String playerName, Sender sender) {
processing.submitNonCritical(() -> {
try {
UUID uuid = uuidUtility.getUUIDOf(playerName);
if (uuid == null) {
UUID playerUUID = uuidUtility.getUUIDOf(playerName);
if (playerUUID == null) {
sender.sendMessage(locale.getString(CommandLang.FAIL_USERNAME_NOT_VALID));
return;
}
if (!dbSystem.getDatabase().check().isPlayerRegistered(uuid)) {
if (!dbSystem.getDatabase().query(PlayerFetchQueries.isPlayerRegistered(playerUUID))) {
sender.sendMessage(locale.getString(CommandLang.FAIL_USERNAME_NOT_KNOWN));
return;
}
checkWebUserAndNotify(sender);
processing.submit(processorFactory.inspectCacheRequestProcessor(uuid, sender, playerName, this::sendInspectMsg));
processing.submit(processorFactory.inspectCacheRequestProcessor(playerUUID, sender, playerName, this::sendInspectMsg));
} catch (DBOpException e) {
sender.sendMessage("§eDatabase exception occurred: " + e.getMessage());
errorHandler.log(L.ERROR, this.getClass(), e);
@ -118,7 +127,7 @@ public class InspectCommand extends CommandNode {
private void checkWebUserAndNotify(Sender sender) {
if (CommandUtils.isPlayer(sender) && webServer.isAuthRequired()) {
boolean senderHasWebUser = dbSystem.getDatabase().check().doesWebUserExists(sender.getName());
boolean senderHasWebUser = dbSystem.getDatabase().query(WebUserQueries.fetchWebUser(sender.getName())).isPresent();
if (!senderHasWebUser) {
sender.sendMessage("§e" + locale.getString(CommandLang.NO_WEB_USER_NOTIFY));

View File

@ -17,6 +17,8 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.locale.Locale;
@ -33,6 +35,8 @@ import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@ -67,6 +71,12 @@ public class ListServersCommand extends CommandNode {
@Override
public void onCommand(Sender sender, String commandLabel, String[] args) {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
String sCol = colorScheme.getSecondaryColor();
String tCol = colorScheme.getTertiaryColor();
Formatter<Server> serverFormatter = serverLister(sCol, tCol);
@ -81,7 +91,8 @@ public class ListServersCommand extends CommandNode {
}
private void sendServers(Sender sender, Formatter<Server> serverFormatter) {
List<Server> servers = dbSystem.getDatabase().fetch().getServers();
List<Server> servers = new ArrayList<>(dbSystem.getDatabase().query(ServerQueries.fetchPlanServerInformation()).values());
Collections.sort(servers);
for (Server server : servers) {
sender.sendMessage(serverFormatter.apply(server));
}

View File

@ -24,6 +24,8 @@ 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.objects.DateHolder;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -101,6 +103,12 @@ public class QInspectCommand extends CommandNode {
return;
}
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
runInspectTask(playerName, sender);
}
@ -113,7 +121,7 @@ public class QInspectCommand extends CommandNode {
return;
}
PlayerContainer container = dbSystem.getDatabase().fetch().getPlayerContainer(uuid);
PlayerContainer container = dbSystem.getDatabase().query(ContainerFetchQueries.fetchPlayerContainer(uuid));
if (!container.getValue(PlayerKeys.REGISTERED).isPresent()) {
sender.sendMessage(locale.getString(CommandLang.FAIL_USERNAME_NOT_KNOWN));
return;

View File

@ -17,8 +17,10 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.WebUserQueries;
import com.djrapitops.plan.db.access.transactions.commands.RegisterWebUserTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -86,6 +88,12 @@ public class RegisterCommand extends CommandNode {
@Override
public void onCommand(Sender sender, String commandLabel, String[] args) {
try {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
if (CommandUtils.isPlayer(sender)) {
playerRegister(args, sender);
} else {
@ -147,12 +155,12 @@ public class RegisterCommand extends CommandNode {
String userName = webUser.getName();
try {
Database database = dbSystem.getDatabase();
boolean userExists = database.check().doesWebUserExists(userName);
boolean userExists = database.query(WebUserQueries.fetchWebUser(userName)).isPresent();
if (userExists) {
sender.sendMessage(locale.getString(CommandLang.FAIL_WEB_USER_EXISTS));
return;
}
database.save().webUser(webUser);
database.executeTransaction(new RegisterWebUserTransaction(webUser));
sender.sendMessage(locale.getString(CommandLang.WEB_USER_REGISTER_SUCCESS, userName));
logger.info(locale.getString(CommandLang.WEB_USER_REGISTER_NOTIFY, userName, webUser.getPermLevel()));
} catch (Exception e) {

View File

@ -17,6 +17,8 @@
package com.djrapitops.plan.command.commands;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -71,6 +73,12 @@ public class SearchCommand extends CommandNode {
@Override
public void onCommand(Sender sender, String commandLabel, String[] args) {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
Verify.isTrue(args.length >= 1,
() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ONE_ARG, Arrays.toString(this.getArguments()))));
@ -82,12 +90,12 @@ public class SearchCommand extends CommandNode {
private void runSearchTask(String[] args, Sender sender) {
processing.submitNonCritical(() -> {
try {
String searchTerm = args[0];
List<String> names = dbSystem.getDatabase().search().matchingPlayers(searchTerm);
String searchFor = args[0];
List<String> names = dbSystem.getDatabase().query(UserIdentifierQueries.fetchMatchingPlayerNames(searchFor));
Collections.sort(names);
boolean empty = Verify.isEmpty(names);
sender.sendMessage(locale.getString(CommandLang.HEADER_SEARCH, empty ? 0 : names.size(), searchTerm));
sender.sendMessage(locale.getString(CommandLang.HEADER_SEARCH, empty ? 0 : names.size(), searchFor));
// Results
if (!empty) {
String message = names.toString();

View File

@ -16,12 +16,14 @@
*/
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.DBOpException;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.SQLiteDB;
import com.djrapitops.plan.db.access.queries.ServerAggregateQueries;
import com.djrapitops.plan.db.access.transactions.BackupCopyTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -41,8 +43,7 @@ import com.djrapitops.plugin.utilities.Verify;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
/**
* This command is used to backup a database to a .db file.
@ -109,6 +110,10 @@ public class ManageBackupCommand extends CommandNode {
private void runBackupTask(Sender sender, String[] args, Database database) {
processing.submitCritical(() -> {
try {
Database.State dbState = database.getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.WARN_DATABASE_NOT_OPEN, dbState.name()));
}
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
createNewBackup(args[0], database);
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
@ -126,19 +131,22 @@ public class ManageBackupCommand extends CommandNode {
* @param copyFromDB Database you want to backup.
*/
private void createNewBackup(String dbName, Database copyFromDB) {
Integer userCount = copyFromDB.query(ServerAggregateQueries.baseUserCount());
if (userCount <= 0) {
return;
}
SQLiteDB backupDB = null;
try {
String timeStamp = iso8601LongFormatter.apply(System.currentTimeMillis());
String fileName = dbName + "-backup-" + timeStamp;
backupDB = sqliteFactory.usingFileCalled(fileName);
Collection<UUID> uuids = copyFromDB.fetch().getSavedUUIDs();
if (uuids.isEmpty()) {
return;
}
backupDB.init();
copyFromDB.backup().backup(backupDB);
} catch (DBException e) {
backupDB.executeTransaction(new BackupCopyTransaction(copyFromDB, backupDB)).get();
} catch (DBOpException | ExecutionException e) {
errorHandler.log(L.ERROR, this.getClass(), e);
} catch (InterruptedException e) {
backupDB.close();
Thread.currentThread().interrupt();
} finally {
if (backupDB != null) {
backupDB.close();

View File

@ -18,9 +18,10 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.commands.RemoveEverythingTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -101,7 +102,7 @@ public class ManageClearCommand extends CommandNode {
try {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
database.remove().everything();
database.executeTransaction(new RemoveEverythingTransaction());
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
} catch (DBOpException e) {

View File

@ -17,6 +17,8 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.connection.*;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.info.request.InfoRequestFactory;
@ -38,7 +40,7 @@ import com.djrapitops.plugin.command.Sender;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
@ -122,19 +124,24 @@ public class ManageConDebugCommand extends CommandNode {
return;
}
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
processing.submitNonCritical(() -> testServers(sender));
}
private void testServers(Sender sender) {
List<Server> servers = dbSystem.getDatabase().fetch().getServers();
Map<UUID, Server> servers = dbSystem.getDatabase().query(ServerQueries.fetchPlanServerInformation());
if (servers.isEmpty()) {
sender.sendMessage(locale.getString(ManageLang.CON_NO_SERVERS));
}
String accessAddress = webServer.getAccessAddress();
UUID thisServer = serverInfo.getServerUUID();
for (Server server : servers) {
for (Server server : servers.values()) {
if (thisServer.equals(server.getUuid())) {
continue;
}

View File

@ -17,9 +17,9 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -84,7 +84,7 @@ public class ManageHotSwapCommand extends CommandNode {
Database database = dbSystem.getActiveDatabaseByName(dbName);
database.init();
if (!database.isOpen()) {
if (database.getState() == Database.State.CLOSED) {
return;
}
} catch (Exception e) {

View File

@ -16,6 +16,8 @@
*/
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.importing.ImportSystem;
import com.djrapitops.plan.system.importing.importers.Importer;
import com.djrapitops.plan.system.locale.Locale;
@ -44,18 +46,21 @@ import java.util.Optional;
public class ManageImportCommand extends CommandNode {
private final Locale locale;
private final DBSystem dbSystem;
private final Processing processing;
private final ImportSystem importSystem;
@Inject
public ManageImportCommand(
Locale locale,
DBSystem dbSystem,
Processing processing,
ImportSystem importSystem
) {
super("import", Permissions.MANAGE.getPermission(), CommandType.CONSOLE);
this.locale = locale;
this.dbSystem = dbSystem;
this.processing = processing;
this.importSystem = importSystem;
@ -77,6 +82,12 @@ public class ManageImportCommand extends CommandNode {
return;
}
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
findAndProcessImporter(sender, importArg);
}

View File

@ -16,9 +16,10 @@
*/
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.BackupCopyTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -111,7 +112,7 @@ public class ManageMoveCommand extends CommandNode {
try {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
fromDatabase.backup().backup(toDatabase);
toDatabase.executeTransaction(new BackupCopyTransaction(fromDatabase, toDatabase)).get();
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
@ -119,6 +120,8 @@ public class ManageMoveCommand extends CommandNode {
if (movingToCurrentDB) {
sender.sendMessage(locale.getString(ManageLang.HOTSWAP_REMINDER, toDatabase.getType().getConfigName()));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);
sender.sendMessage(locale.getString(ManageLang.PROGRESS_FAIL, e.getMessage()));

View File

@ -17,8 +17,10 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.PlayerFetchQueries;
import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -94,15 +96,15 @@ public class ManageRemoveCommand extends CommandNode {
private void runRemoveTask(String playerName, Sender sender, String[] args) {
processing.submitCritical(() -> {
try {
UUID uuid = uuidUtility.getUUIDOf(playerName);
UUID playerUUID = uuidUtility.getUUIDOf(playerName);
if (uuid == null) {
if (playerUUID == null) {
sender.sendMessage(locale.getString(CommandLang.FAIL_USERNAME_NOT_VALID));
return;
}
Database db = dbSystem.getDatabase();
if (!db.check().isPlayerRegistered(uuid)) {
if (!db.query(PlayerFetchQueries.isPlayerRegistered(playerUUID))) {
sender.sendMessage(locale.getString(CommandLang.FAIL_USERNAME_NOT_KNOWN));
return;
}
@ -118,7 +120,7 @@ public class ManageRemoveCommand extends CommandNode {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
db.remove().player(uuid);
db.executeTransaction(new RemovePlayerTransaction(playerUUID));
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
} catch (DBOpException e) {

View File

@ -16,10 +16,11 @@
*/
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.SQLiteDB;
import com.djrapitops.plan.db.access.transactions.BackupCopyTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -121,11 +122,18 @@ public class ManageRestoreCommand extends CommandNode {
SQLiteDB backupDB = sqliteFactory.usingFile(backupDBFile);
backupDB.init();
Database.State dbState = database.getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.WARN_DATABASE_NOT_OPEN, dbState.name()));
}
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
database.backup().restore(backupDB);
database.executeTransaction(new BackupCopyTransaction(backupDB, database)).get();
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);
sender.sendMessage(locale.getString(ManageLang.PROGRESS_FAIL, e.getMessage()));

View File

@ -17,11 +17,15 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.db.access.transactions.commands.SetServerAsUninstalledTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
import com.djrapitops.plan.system.locale.lang.DeepHelpLang;
import com.djrapitops.plan.system.locale.lang.ManageLang;
import com.djrapitops.plan.system.processing.Processing;
@ -34,7 +38,6 @@ import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@ -77,6 +80,12 @@ public class ManageUninstalledCommand extends CommandNode {
public void onCommand(Sender sender, String commandLabel, String[] args) {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
processing.submitNonCritical(() -> {
try {
Optional<Server> serverOptional = getServer(args);
@ -91,7 +100,7 @@ public class ManageUninstalledCommand extends CommandNode {
return;
}
dbSystem.getDatabase().save().setAsUninstalled(serverUUID);
dbSystem.getDatabase().executeTransaction(new SetServerAsUninstalledTransaction(serverUUID));
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
} catch (DBOpException e) {
sender.sendMessage("§cError occurred: " + e.toString());
@ -102,16 +111,9 @@ public class ManageUninstalledCommand extends CommandNode {
private Optional<Server> getServer(String[] args) {
if (args.length >= 1) {
Map<UUID, Server> bukkitServers = dbSystem.getDatabase().fetch().getBukkitServers();
String serverIdentifier = getGivenIdentifier(args);
for (Map.Entry<UUID, Server> entry : bukkitServers.entrySet()) {
Server server = entry.getValue();
if (Integer.toString(server.getId()).equals(serverIdentifier)
|| server.getName().equalsIgnoreCase(serverIdentifier)) {
return Optional.of(server);
}
}
return dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverIdentifier))
.filter(Server::isNotProxy);
}
return Optional.empty();
}

View File

@ -17,8 +17,9 @@
package com.djrapitops.plan.command.commands.webuser;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.WebUserQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -35,6 +36,7 @@ import com.djrapitops.plugin.utilities.Verify;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.Optional;
/**
* Subcommand for checking WebUser permission level.
@ -69,6 +71,12 @@ public class WebCheckCommand extends CommandNode {
@Override
public void onCommand(Sender sender, String commandLabel, String[] args) {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
Verify.isTrue(args.length >= 1,
() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ONE_ARG, Arrays.toString(this.getArguments()))));
@ -77,11 +85,12 @@ public class WebCheckCommand extends CommandNode {
processing.submitNonCritical(() -> {
try {
Database db = dbSystem.getDatabase();
if (!db.check().doesWebUserExists(user)) {
Optional<WebUser> found = db.query(WebUserQueries.fetchWebUser(user));
if (!found.isPresent()) {
sender.sendMessage(locale.getString(CommandLang.FAIL_WEB_USER_NOT_EXISTS));
return;
}
WebUser info = db.fetch().getWebUser(user);
WebUser info = found.get();
sender.sendMessage(locale.getString(CommandLang.WEB_USER_LIST, info.getName(), info.getPermLevel()));
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);

View File

@ -16,8 +16,11 @@
*/
package com.djrapitops.plan.command.commands.webuser;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.WebUserQueries;
import com.djrapitops.plan.db.access.transactions.commands.RemoveWebUserTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
import com.djrapitops.plan.system.locale.lang.CommandLang;
@ -34,6 +37,7 @@ import com.djrapitops.plugin.utilities.Verify;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.Optional;
/**
* Subcommand for deleting a WebUser.
@ -68,6 +72,12 @@ public class WebDeleteCommand extends CommandNode {
@Override
public void onCommand(Sender sender, String commandLabel, String[] args) {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
Verify.isTrue(args.length >= 1,
() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ONE_ARG, Arrays.toString(this.getArguments()))));
@ -76,11 +86,12 @@ public class WebDeleteCommand extends CommandNode {
processing.submitNonCritical(() -> {
try {
Database db = dbSystem.getDatabase();
if (!db.check().doesWebUserExists(user)) {
Optional<WebUser> found = db.query(WebUserQueries.fetchWebUser(user));
if (!found.isPresent()) {
sender.sendMessage("§c[Plan] User Doesn't exist.");
return;
}
db.remove().webUser(user);
db.executeTransaction(new RemoveWebUserTransaction(user));
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);

View File

@ -17,6 +17,8 @@
package com.djrapitops.plan.command.commands.webuser;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.objects.WebUserQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -24,7 +26,6 @@ import com.djrapitops.plan.system.locale.lang.CommandLang;
import com.djrapitops.plan.system.locale.lang.ManageLang;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.utilities.comparators.WebUserComparator;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
import com.djrapitops.plugin.command.Sender;
@ -67,10 +68,15 @@ public class WebListUsersCommand extends CommandNode {
@Override
public void onCommand(Sender sender, String commandLabel, String[] args) {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
return;
}
processing.submitNonCritical(() -> {
try {
List<WebUser> users = dbSystem.getDatabase().fetch().getWebUsers();
users.sort(new WebUserComparator());
List<WebUser> users = dbSystem.getDatabase().query(WebUserQueries.fetchAllPlanWebUsers());
sender.sendMessage(locale.getString(CommandLang.HEADER_WEB_USERS, users.size()));
for (WebUser user : users) {
sender.sendMessage(locale.getString(CommandLang.WEB_USER_LIST, user.getName(), user.getPermLevel()));

View File

@ -0,0 +1,79 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.data.container;
import com.djrapitops.plugin.utilities.Verify;
import java.util.Objects;
import java.util.UUID;
/**
* Represents user information stored in plan_users.
* <p>
* Only one per player exists unlike {@link UserInfo} which is available per server.
*
* @author Rsl1122
*/
public class BaseUser {
private final UUID uuid;
private final String name;
private final long registered;
private final int timesKicked;
public BaseUser(UUID uuid, String name, long registered, int timesKicked) {
Verify.nullCheck(uuid, () -> new IllegalArgumentException("'uuid' can not be null"));
Verify.nullCheck(name, () -> new IllegalArgumentException("'name' can not be null"));
this.uuid = uuid;
this.name = name;
this.registered = registered;
this.timesKicked = timesKicked;
}
public UUID getUuid() {
return uuid;
}
public String getName() {
return name;
}
public long getRegistered() {
return registered;
}
public int getTimesKicked() {
return timesKicked;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseUser)) return false;
BaseUser baseUser = (BaseUser) o;
return registered == baseUser.registered &&
timesKicked == baseUser.timesKicked &&
uuid.equals(baseUser.uuid) &&
name.equals(baseUser.name);
}
@Override
public int hashCode() {
return Objects.hash(uuid, name, registered, timesKicked);
}
}

View File

@ -16,7 +16,7 @@
*/
package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.containers.DataContainer;
import com.djrapitops.plan.data.store.containers.DynamicDataContainer;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plan.data.time.WorldTimes;
@ -29,7 +29,7 @@ import java.util.*;
* @author Rsl1122
* @see SessionKeys for Key objects.
*/
public class Session extends DataContainer implements DateHolder {
public class Session extends DynamicDataContainer implements DateHolder {
private long sessionStart;
private WorldTimes worldTimes;
@ -228,4 +228,17 @@ public class Session extends DataContainer implements DateHolder {
private long getAfkTime() {
return afkTime;
}
@Override
public String toString() {
return "Session{" +
"sessionStart=" + getUnsafe(SessionKeys.START) +
", sessionEnd=" + getUnsafe(SessionKeys.END) +
", worldTimes=" + worldTimes +
", playerKills=" + playerKills +
", mobKills=" + mobKills +
", deaths=" + deaths +
", afkTime=" + afkTime +
'}';
}
}

View File

@ -20,48 +20,41 @@ import java.util.Objects;
import java.util.UUID;
/**
* Used for storing information of players after it has been fetched.
* Represents user information stored in plan_user_info.
* <p>
* Unlike {@link BaseUser} one instance is stored per server for a single player.
* Proxy servers are an exception, and UserInfo is not stored for them.
*
* @author Rsl1122
*/
public class UserInfo {
private final UUID uuid;
private String name;
private final UUID playerUUID;
private final UUID serverUUID;
private long registered;
private long lastSeen;
private boolean banned;
private boolean opped;
public UserInfo(UUID uuid, String name, long registered, boolean opped, boolean banned) {
this.uuid = uuid;
this.name = name;
public UserInfo(UUID playerUUID, UUID serverUUID, long registered, boolean opped, boolean banned) {
this.playerUUID = playerUUID;
this.serverUUID = serverUUID;
this.registered = registered;
this.opped = opped;
this.banned = banned;
lastSeen = 0L;
}
public UUID getUuid() {
return uuid;
public UUID getPlayerUuid() {
return playerUUID;
}
public String getName() {
return name;
public UUID getServerUUID() {
return serverUUID;
}
public long getRegistered() {
return registered;
}
public long getLastSeen() {
return lastSeen;
}
public void setLastSeen(long lastSeen) {
this.lastSeen = lastSeen;
}
public boolean isBanned() {
return banned;
}
@ -73,28 +66,26 @@ public class UserInfo {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!(o instanceof UserInfo)) return false;
UserInfo userInfo = (UserInfo) o;
return registered == userInfo.registered &&
lastSeen == userInfo.lastSeen &&
banned == userInfo.banned &&
opped == userInfo.opped &&
Objects.equals(uuid, userInfo.uuid) &&
Objects.equals(name, userInfo.name);
playerUUID.equals(userInfo.playerUUID) &&
serverUUID.equals(userInfo.serverUUID);
}
@Override
public int hashCode() {
return Objects.hash(uuid, name, registered, lastSeen, banned, opped);
return Objects.hash(playerUUID, serverUUID, registered, banned, opped);
}
@Override
public String toString() {
return "UserInfo{" +
"uuid=" + uuid +
", name='" + name + '\'' +
"playerUUID=" + playerUUID +
", serverUUID=" + serverUUID +
", registered=" + registered +
", lastSeen=" + lastSeen +
", banned=" + banned +
", opped=" + opped +
'}';

View File

@ -125,8 +125,9 @@ public class HookHandler implements SubSystem {
containers.put(pluginData, container);
}
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
String sourcePlugin = pluginData.getSourcePlugin();
logger.error("PluginData caused exception: " + sourcePlugin);
String pluginName = pluginData.getSourcePlugin();
logger.error("PluginData caused exception: " + pluginName +
", you can disable the integration under 'Plugins." + pluginName + ".Enabled'");
errorHandler.log(L.WARN, pluginData.getClass(), e);
}
}

View File

@ -81,4 +81,17 @@ public class Key<T> {
public int hashCode() {
return Objects.hash(type, keyName);
}
/**
* Cast an object to the type of the key.
*
* @param object Object to cast.
* @return The object with the type of T.
*/
public T typeCast(Object object) {
// Since Type can have a List<Subtype>, Class can not be obtained in order to use Class while casting.
// This could be done since Google Gson does it, but I don't know how they do it since
// the implementation is hidden.
return (T) object;
}
}

View File

@ -24,7 +24,6 @@ import com.djrapitops.plan.data.store.keys.ServerKeys;
import com.djrapitops.plan.data.store.mutators.*;
import com.djrapitops.plan.data.store.mutators.health.HealthInformation;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.properties.ServerProperties;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.settings.config.PlanConfig;
@ -59,7 +58,7 @@ import java.util.stream.Collectors;
* @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 {
public class AnalysisContainer extends DynamicDataContainer {
private final ServerContainer serverContainer;
@ -67,7 +66,6 @@ public class AnalysisContainer extends DataContainer {
private final Locale locale;
private final PlanConfig config;
private final Theme theme;
private final DBSystem dbSystem;
private final ServerProperties serverProperties;
private final Formatters formatters;
private final Graphs graphs;
@ -75,16 +73,12 @@ public class AnalysisContainer extends DataContainer {
private final Accordions accordions;
private final AnalysisPluginsTabContentCreator pluginsTabContentCreator;
private static final Key<Map<UUID, String>> serverNames = new Key<>(new Type<Map<UUID, String>>() {
}, "SERVER_NAMES");
public AnalysisContainer(
ServerContainer serverContainer,
String version,
Locale locale,
PlanConfig config,
Theme theme,
DBSystem dbSystem,
ServerProperties serverProperties,
Formatters formatters,
Graphs graphs,
@ -97,7 +91,6 @@ public class AnalysisContainer extends DataContainer {
this.locale = locale;
this.config = config;
this.theme = theme;
this.dbSystem = dbSystem;
this.serverProperties = serverProperties;
this.formatters = formatters;
this.graphs = graphs;
@ -148,9 +141,7 @@ public class AnalysisContainer extends DataContainer {
}
private void addServerProperties() {
putCachingSupplier(AnalysisKeys.SERVER_NAME, () ->
getUnsafe(serverNames).getOrDefault(serverContainer.getUnsafe(ServerKeys.SERVER_UUID), "Plan")
);
putCachingSupplier(AnalysisKeys.SERVER_NAME, () -> serverContainer.getValue(ServerKeys.NAME).orElse("Plan"));
putRawData(AnalysisKeys.PLAYERS_MAX, serverProperties.getMaxPlayers());
putRawData(AnalysisKeys.PLAYERS_ONLINE, serverProperties.getOnlinePlayers());
@ -308,10 +299,12 @@ public class AnalysisContainer extends DataContainer {
private void addSessionSuppliers() {
Key<SessionAccordion> sessionAccordion = new Key<>(SessionAccordion.class, "SESSION_ACCORDION");
putCachingSupplier(serverNames, () -> dbSystem.getDatabase().fetch().getServerNames());
putCachingSupplier(sessionAccordion, () -> accordions.serverSessionAccordion(
getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).all(),
getSupplier(serverNames),
() -> Collections.singletonMap(
serverContainer.getUnsafe(ServerKeys.SERVER_UUID),
serverContainer.getValue(ServerKeys.NAME).orElse("This server")
),
() -> getUnsafe(AnalysisKeys.PLAYER_NAMES)
));
putSupplier(AnalysisKeys.SESSION_ACCORDION_HTML, () -> getUnsafe(sessionAccordion).toHtml());
@ -511,7 +504,6 @@ public class AnalysisContainer extends DataContainer {
private final PlanConfig config;
private final Locale locale;
private final Theme theme;
private final DBSystem dbSystem;
private final ServerProperties serverProperties;
private final Formatters formatters;
private final Graphs graphs;
@ -525,7 +517,6 @@ public class AnalysisContainer extends DataContainer {
PlanConfig config,
Locale locale,
Theme theme,
DBSystem dbSystem,
ServerProperties serverProperties,
Formatters formatters,
Graphs graphs,
@ -537,7 +528,6 @@ public class AnalysisContainer extends DataContainer {
this.config = config;
this.locale = locale;
this.theme = theme;
this.dbSystem = dbSystem;
this.serverProperties = serverProperties;
this.formatters = formatters;
this.graphs = graphs;
@ -553,7 +543,6 @@ public class AnalysisContainer extends DataContainer {
locale,
config,
theme,
dbSystem,
serverProperties,
formatters,
graphs,

View File

@ -16,66 +16,56 @@
*/
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.utilities.formatting.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* Abstract representation of an object that holds the Values for different Keys.
* Interface for an object that can store arbitrary data referenced via {@link Key} objects.
* <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.
* Implementations should mainly be concerned on how the data given to it is stored.
* Retrieval has some details that should be followed.
*
* @author Rsl1122
*/
public class DataContainer {
private final Map<Key, Supplier> map;
private long timeToLive;
public DataContainer() {
this(TimeUnit.SECONDS.toMillis(30L));
}
public DataContainer(long timeToLive) {
this.timeToLive = timeToLive;
map = new HashMap<>();
}
public interface DataContainer {
/**
* Place your data inside the container.
* <p>
* What the container does with the object depends on the implementation.
*
* @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);
}
<T> void putRawData(Key<T> key, T obj);
public <T> void putSupplier(Key<T> key, Supplier<T> supplier) {
if (supplier == null) {
return;
}
map.put(key, supplier);
}
/**
* Place a data supplier inside the container.
* <p>
* What the container does with the supplier depends on the implementation.
*
* @param key Key of type T that identifies the data and will be used later when the data needs to be fetched.
* @param supplier Supplier to store
* @param <T> Type of the object
*/
<T> void putSupplier(Key<T> key, Supplier<T> supplier);
public <T> void putCachingSupplier(Key<T> key, Supplier<T> supplier) {
if (supplier == null) {
return;
}
map.put(key, new CachingSupplier<>(supplier, timeToLive));
}
public <T> Supplier<T> getSupplier(Key<T> key) {
return (Supplier<T>) map.get(key);
}
/**
* Place a caching data supplier inside the container.
* <p>
* If the supplier is called the value is cached according to the implementation of the container.
* What the container does with the supplier depends on the implementation.
*
* @param key Key of type T that identifies the data and will be used later when the data needs to be fetched.
* @param supplier Supplier to store
* @param <T> Type of the object
*/
<T> void putCachingSupplier(Key<T> key, Supplier<T> supplier);
/**
* Check if a Value with the given Key has been placed into the container.
@ -84,65 +74,79 @@ public class DataContainer {
* @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 map.containsKey(key);
}
<T> boolean supports(Key<T> key);
/**
* Get an Optional of the data identified by the Key.
* Get an Optional of the Value 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.
* It is recommended to check if the Optional is present as null values 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();
}
}
<T> Optional<T> getValue(Key<T> key);
public <T> T getUnsafe(Key<T> key) {
Supplier supplier = map.get(key);
if (supplier == null) {
throw new IllegalArgumentException("Unsupported Key: " + key.getKeyName());
}
return (T) supplier.get();
}
/**
* Get data identified by the Key, or throw an exception.
* <p>
* It is recommended to use {@link DataContainer#supports(Key)} before using this method.
*
* @param key Key that identifies the Value
* @param <T> Type of the object returned by Value
* @return the value
* @throws IllegalArgumentException If the key is not supported.
*/
<T> T getUnsafe(Key<T> key);
public <T> String getFormatted(Key<T> key, Formatter<Optional<T>> formatter) {
/**
* Get formatted Value identified by the Key.
* <p>
*
* @param key Key that identifies the Value
* @param formatter Formatter for the Optional returned by {@link DataContainer#getValue(Key)}
* @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.
*/
default <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) {
/**
* Get formatted Value identified by the Key, or throw an exception.
* <p>
* It is recommended to use {@link DataContainer#supports(Key)} before using this method.
*
* @param key Key that identifies the Value
* @param formatter Formatter for the value
* @param <T> Type of the object returned by Value
* @return the value
* @throws IllegalArgumentException If the key is not supported.
*/
default <T> String getFormattedUnsafe(Key<T> key, Formatter<T> formatter) {
T value = getUnsafe(key);
return formatter.apply(value);
}
public void putAll(Map<Key, Supplier> toPut) {
map.putAll(toPut);
}
/**
* Place all values from given DataContainer into this container.
*
* @param dataContainer Container with values.
*/
void putAll(DataContainer dataContainer);
public void putAll(DataContainer dataContainer) {
putAll(dataContainer.map);
}
/**
* Clear the container of all data.
*/
void clear();
public void clear() {
map.clear();
}
public Map<Key, Supplier> getMap() {
return map;
}
}
/**
* Return a Key - Value Map of the data in the container.
* <p>
* This method may call blocking methods if underlying implementation uses the given Suppliers.
*
* @return Map: Key - Object
*/
Map<Key, Object> getMap();
}

View File

@ -0,0 +1,107 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.data.store.containers;
import com.djrapitops.plan.data.store.Key;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
/**
* DataContainer implementation that delegates the method calls to other DataContainer implementations.
*
* @author Rsl1122
*/
public class DynamicDataContainer implements DataContainer {
private final SupplierDataContainer supplierDataContainer;
private final RawDataContainer rawDataContainer;
public DynamicDataContainer() {
supplierDataContainer = new SupplierDataContainer();
rawDataContainer = new RawDataContainer();
}
public DynamicDataContainer(long timeToLive) {
supplierDataContainer = new SupplierDataContainer(timeToLive);
rawDataContainer = new RawDataContainer();
}
@Override
public <T> void putRawData(Key<T> key, T obj) {
rawDataContainer.putRawData(key, obj);
}
@Override
public <T> void putSupplier(Key<T> key, Supplier<T> supplier) {
supplierDataContainer.putSupplier(key, supplier);
}
@Override
public <T> void putCachingSupplier(Key<T> key, Supplier<T> supplier) {
supplierDataContainer.putCachingSupplier(key, supplier);
}
@Override
public <T> boolean supports(Key<T> key) {
return rawDataContainer.supports(key) || supplierDataContainer.supports(key);
}
@Override
public <T> Optional<T> getValue(Key<T> key) {
Optional<T> raw = rawDataContainer.getValue(key);
if (raw.isPresent()) {
return raw;
} else {
return supplierDataContainer.getValue(key);
}
}
@Override
public <T> T getUnsafe(Key<T> key) {
if (rawDataContainer.supports(key)) {
return rawDataContainer.getUnsafe(key);
} else {
return supplierDataContainer.getUnsafe(key);
}
}
@Override
public void putAll(DataContainer dataContainer) {
if (dataContainer instanceof SupplierDataContainer) {
supplierDataContainer.putAll(dataContainer);
} else if (dataContainer instanceof RawDataContainer) {
rawDataContainer.putAll(dataContainer);
} else {
rawDataContainer.putAll(dataContainer.getMap());
}
}
@Override
public void clear() {
rawDataContainer.clear();
supplierDataContainer.clear();
}
@Override
public Map<Key, Object> getMap() {
Map<Key, Object> map = supplierDataContainer.getMap();
map.putAll(rawDataContainer.getMap());
return map;
}
}

View File

@ -23,6 +23,9 @@ 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.health.NetworkHealthInformation;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.ServerAggregateQueries;
import com.djrapitops.plan.db.access.queries.objects.TPSQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.properties.ServerProperties;
@ -54,7 +57,7 @@ import java.util.concurrent.TimeUnit;
* @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 {
public class NetworkContainer extends DynamicDataContainer {
private final ServerContainer bungeeContainer;
@ -62,10 +65,10 @@ public class NetworkContainer extends DataContainer {
private final PlanConfig config;
private final Locale locale;
private final Theme theme;
private final DBSystem dbSystem;
private final ServerProperties serverProperties;
private final Formatters formatters;
private final Graphs graphs;
private final Database database;
public NetworkContainer(
ServerContainer bungeeContainer,
@ -83,7 +86,7 @@ public class NetworkContainer extends DataContainer {
this.config = config;
this.locale = locale;
this.theme = theme;
this.dbSystem = dbSystem;
this.database = dbSystem.getDatabase();
this.serverProperties = serverProperties;
this.formatters = formatters;
this.graphs = graphs;
@ -97,22 +100,19 @@ public class NetworkContainer extends DataContainer {
}
private void addServerBoxes() {
putSupplier(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA, () -> dbSystem.getDatabase().fetch().getPlayersOnlineForServers(
putSupplier(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA, () -> database.query(TPSQueries.fetchPlayerOnlineDataOfServers(
getValue(NetworkKeys.BUKKIT_SERVERS).orElse(new ArrayList<>()))
);
putSupplier(NetworkKeys.SERVER_REGISTER_DATA, () -> dbSystem.getDatabase().fetch().getPlayersRegisteredForServers(
getValue(NetworkKeys.BUKKIT_SERVERS).orElse(new ArrayList<>()))
);
));
putSupplier(NetworkKeys.SERVERS_TAB, () -> {
StringBuilder serverBoxes = new StringBuilder();
Map<Integer, List<TPS>> playersOnlineData = getValue(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA).orElse(new HashMap<>());
Map<UUID, Integer> registerData = getValue(NetworkKeys.SERVER_REGISTER_DATA).orElse(new HashMap<>());
Map<UUID, Integer> userCounts = database.query(ServerAggregateQueries.serverUserCounts());
Collection<Server> servers = getValue(NetworkKeys.BUKKIT_SERVERS).orElse(new ArrayList<>());
servers.stream()
.sorted((one, two) -> String.CASE_INSENSITIVE_ORDER.compare(one.getName(), two.getName()))
.forEach(server -> {
TPSMutator tpsMutator = new TPSMutator(playersOnlineData.getOrDefault(server.getId(), new ArrayList<>()));
int registered = registerData.getOrDefault(server.getUuid(), 0);
int registered = userCounts.getOrDefault(server.getUuid(), 0);
NetworkServerBox serverBox = new NetworkServerBox(server, registered, tpsMutator, graphs);
serverBoxes.append(serverBox.toHtml());
});
@ -170,7 +170,7 @@ public class NetworkContainer extends DataContainer {
private void addPlayerInformation() {
putSupplier(NetworkKeys.PLAYERS_TOTAL, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR).count());
putSupplier(NetworkKeys.WORLD_MAP_SERIES, () ->
graphs.special().worldMap(PlayersMutator.forContainer(bungeeContainer)).toHighChartsSeries()
graphs.special().worldMap(database.query(ServerAggregateQueries.networkGeolocationCounts())).toHighChartsSeries()
);
Key<BarGraph> geolocationBarChart = new Key<>(BarGraph.class, "GEOLOCATION_BAR_GRAPH");
putSupplier(geolocationBarChart, () -> graphs.bar().geolocationBarGraph(getUnsafe(NetworkKeys.PLAYERS_MUTATOR)));

View File

@ -16,8 +16,15 @@
*/
package com.djrapitops.plan.data.store.containers;
import java.util.HashMap;
import java.util.UUID;
import com.djrapitops.plan.data.container.Ping;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.container.UserInfo;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.keys.PerServerKeys;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import java.util.*;
/**
* Container for data about a player linked to a single server.
@ -26,4 +33,87 @@ import java.util.UUID;
* @see com.djrapitops.plan.data.store.keys.PerServerKeys For Key objects.
*/
public class PerServerContainer extends HashMap<UUID, DataContainer> {
public <T> void putToContainerOfServer(UUID serverUUID, Key<T> key, T value) {
DataContainer container = getOrDefault(serverUUID, new DynamicDataContainer());
container.putRawData(key, value);
put(serverUUID, container);
}
public void putUserInfo(UserInfo userInfo) {
UUID serverUUID = userInfo.getServerUUID();
putToContainerOfServer(serverUUID, PerServerKeys.REGISTERED, userInfo.getRegistered());
putToContainerOfServer(serverUUID, PerServerKeys.BANNED, userInfo.isBanned());
putToContainerOfServer(serverUUID, PerServerKeys.OPERATOR, userInfo.isOperator());
}
public void putUserInfo(Collection<UserInfo> userInformation) {
for (UserInfo userInfo : userInformation) {
putUserInfo(userInfo);
}
}
public void putCalculatingSuppliers() {
for (DataContainer container : values()) {
container.putSupplier(PerServerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen());
container.putSupplier(PerServerKeys.WORLD_TIMES, () -> SessionsMutator.forContainer(container).toTotalWorldTimes());
container.putSupplier(PerServerKeys.PLAYER_DEATHS, () -> SessionsMutator.forContainer(container).toPlayerDeathList());
container.putSupplier(PerServerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(container).toPlayerKillList());
container.putSupplier(PerServerKeys.PLAYER_KILL_COUNT, () -> container.getUnsafe(PerServerKeys.PLAYER_KILLS).size());
container.putSupplier(PerServerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount());
container.putSupplier(PerServerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount());
container.putSupplier(PerServerKeys.MOB_DEATH_COUNT, () ->
container.getUnsafe(PerServerKeys.DEATH_COUNT) - container.getUnsafe(PerServerKeys.PLAYER_DEATH_COUNT)
);
}
}
public void putSessions(Collection<Session> sessions) {
if (sessions == null) {
return;
}
for (Session session : sessions) {
putSession(session);
}
}
private void putSession(Session session) {
if (session == null) {
return;
}
UUID serverUUID = session.getUnsafe(SessionKeys.SERVER_UUID);
DataContainer container = getOrDefault(serverUUID, new DynamicDataContainer());
if (!container.supports(PerServerKeys.SESSIONS)) {
container.putRawData(PerServerKeys.SESSIONS, new ArrayList<>());
}
container.getUnsafe(PerServerKeys.SESSIONS).add(session);
put(serverUUID, container);
}
public void putPing(List<Ping> pings) {
if (pings == null) {
return;
}
for (Ping ping : pings) {
putPing(ping);
}
}
private void putPing(Ping ping) {
if (ping == null) {
return;
}
UUID serverUUID = ping.getServerUUID();
DataContainer container = getOrDefault(serverUUID, new DynamicDataContainer());
if (!container.supports(PerServerKeys.PING)) {
container.putRawData(PerServerKeys.PING, new ArrayList<>());
}
container.getUnsafe(PerServerKeys.PING).add(ping);
put(serverUUID, container);
}
}

View File

@ -30,7 +30,7 @@ import java.util.Map;
* @author Rsl1122
* @see com.djrapitops.plan.data.store.keys.PlayerKeys For Key objects.
*/
public class PlayerContainer extends DataContainer {
public class PlayerContainer extends DynamicDataContainer {
private Map<Long, ActivityIndex> activityIndexCache;

View File

@ -0,0 +1,108 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.data.store.containers;
import com.djrapitops.plan.data.store.Key;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
/**
* DataContainer that stores everything as raw object value.
*
* @author Rsl1122
*/
public class RawDataContainer implements DataContainer {
private final Map<Key, Object> map;
/**
* Create a RawDataContainer.
*/
public RawDataContainer() {
map = new HashMap<>();
}
@Override
public <T> void putRawData(Key<T> key, T obj) {
if (obj == null) {
return;
}
map.put(key, obj);
}
@Override
public <T> void putSupplier(Key<T> key, Supplier<T> supplier) {
if (supplier == null) {
return;
}
putRawData(key, supplier.get());
}
@Override
public <T> void putCachingSupplier(Key<T> key, Supplier<T> supplier) {
putSupplier(key, supplier);
}
@Override
public <T> boolean supports(Key<T> key) {
return map.containsKey(key);
}
@Override
public <T> Optional<T> getValue(Key<T> key) {
try {
return Optional.ofNullable(key.typeCast(map.get(key)));
} catch (ClassCastException e) {
return Optional.empty();
}
}
@Override
public <T> T getUnsafe(Key<T> key) {
Object value = map.get(key);
if (value == null) {
throw new IllegalArgumentException("Unsupported Key: " + key.getKeyName());
}
return key.typeCast(value);
}
@Override
public void putAll(DataContainer dataContainer) {
if (dataContainer instanceof RawDataContainer) {
putAll(((RawDataContainer) dataContainer).map);
} else {
putAll(dataContainer.getMap());
}
}
void putAll(Map<Key, Object> map) {
this.map.putAll(map);
}
@Override
public void clear() {
map.clear();
}
@Override
public Map<Key, Object> getMap() {
return map;
}
}

View File

@ -22,5 +22,5 @@ package com.djrapitops.plan.data.store.containers;
* @author Rsl1122
* @see com.djrapitops.plan.data.store.keys.ServerKeys For Key objects.
*/
public class ServerContainer extends DataContainer {
public class ServerContainer extends DynamicDataContainer {
}

View File

@ -0,0 +1,134 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.data.store.containers;
import com.djrapitops.plan.data.store.CachingSupplier;
import com.djrapitops.plan.data.store.Key;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* DataContainer implementation that stores everything in {@link Supplier} objects.
*
* @author Rsl1122
*/
public class SupplierDataContainer implements DataContainer {
private final Map<Key, Supplier> map;
private long timeToLive;
/**
* Create a SupplierDataContainer with a default TTL of 30 seconds.
*/
public SupplierDataContainer() {
this(TimeUnit.SECONDS.toMillis(30L));
}
/**
* Create a SupplierDataContainer with a custom TTL.
* <p>
* The old value is not removed from memory until the supplier is called again.
*
* @param timeToLive TTL that determines how long a CachingSupplier value is deemed valid.
*/
public SupplierDataContainer(long timeToLive) {
this.timeToLive = timeToLive;
map = new HashMap<>();
}
@Override
public <T> void putRawData(Key<T> key, T obj) {
putSupplier(key, () -> obj);
}
@Override
public <T> void putSupplier(Key<T> key, Supplier<T> supplier) {
if (supplier == null) {
return;
}
map.put(key, supplier);
}
@Override
public <T> void putCachingSupplier(Key<T> key, Supplier<T> supplier) {
if (supplier == null) {
return;
}
map.put(key, new CachingSupplier<>(supplier, timeToLive));
}
private <T> Supplier<T> getSupplier(Key<T> key) {
return (Supplier<T>) map.get(key);
}
@Override
public <T> boolean supports(Key<T> key) {
return map.containsKey(key);
}
@Override
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();
}
}
@Override
public <T> T getUnsafe(Key<T> key) {
Supplier supplier = map.get(key);
if (supplier == null) {
throw new IllegalArgumentException("Unsupported Key: " + key.getKeyName());
}
return key.typeCast(supplier.get());
}
private void putAll(Map<Key, Supplier> toPut) {
map.putAll(toPut);
}
@Override
public void putAll(DataContainer dataContainer) {
if (dataContainer instanceof SupplierDataContainer) {
putAll(((SupplierDataContainer) dataContainer).map);
} else {
for (Map.Entry<Key, Object> entry : dataContainer.getMap().entrySet()) {
putRawData(entry.getKey(), entry.getValue());
}
}
}
@Override
public void clear() {
map.clear();
}
@Override
public Map<Key, Object> getMap() {
return map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().get()));
}
}

View File

@ -54,7 +54,9 @@ public class CommonKeys {
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> PLAYER_DEATH_COUNT = new Key<>(Integer.class, "player_death_count");
public static final Key<Integer> MOB_KILL_COUNT = new Key<>(Integer.class, "mob_kill_count");
public static final Key<Integer> MOB_DEATH_COUNT = new Key<>(Integer.class, "mob_death_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");

View File

@ -74,6 +74,7 @@ public class NetworkKeys {
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;
public static final Key<Map<Integer, List<TPS>>> NETWORK_PLAYER_ONLINE_DATA = new Key<>(new Type<Map<Integer, List<TPS>>>() {}, "NETWORK_PLAYER_ONLINE_DATA");
@Deprecated
public static final Key<Map<UUID, Integer>> SERVER_REGISTER_DATA = new Key<>(new Type<Map<UUID, Integer>>() {}, "SERVER_REGISTER_DATA");
private NetworkKeys() {

View File

@ -30,7 +30,7 @@ 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 com.djrapitops.plan.db.access.queries.containers.PerServerContainerQuery For Suppliers for each key
* @see PerServerContainer For the DataContainer.
*/
public class PerServerKeys {
@ -45,10 +45,14 @@ public class PerServerKeys {
public static final Key<List<Session>> SESSIONS = CommonKeys.SESSIONS;
public static final Key<WorldTimes> WORLD_TIMES = CommonKeys.WORLD_TIMES;
@Deprecated
public static final Key<List<PlayerKill>> PLAYER_KILLS = CommonKeys.PLAYER_KILLS;
@Deprecated
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> PLAYER_DEATH_COUNT = CommonKeys.PLAYER_DEATH_COUNT;
public static final Key<Integer> MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT;
public static final Key<Integer> MOB_DEATH_COUNT = CommonKeys.MOB_DEATH_COUNT;
public static final Key<Integer> DEATH_COUNT = CommonKeys.DEATH_COUNT;
public static final Key<Long> LAST_SEEN = CommonKeys.LAST_SEEN;

View File

@ -46,6 +46,7 @@ public class SessionKeys {
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;
@Deprecated
public static final Key<List<PlayerDeath>> PLAYER_DEATHS = CommonKeys.PLAYER_DEATHS;
@Deprecated

View File

@ -44,10 +44,6 @@ public class SessionsMutator {
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;
}
@ -208,6 +204,28 @@ public class SessionsMutator {
};
}
public static Map<UUID, List<Session>> sortByPlayers(List<Session> sessions) {
Map<UUID, List<Session>> sorted = new HashMap<>();
for (Session session : sessions) {
UUID playerUUID = session.getUnsafe(SessionKeys.UUID);
List<Session> playerSessions = sorted.getOrDefault(playerUUID, new ArrayList<>());
playerSessions.add(session);
sorted.put(playerUUID, playerSessions);
}
return sorted;
}
public static Map<UUID, List<Session>> sortByServers(List<Session> sessions) {
Map<UUID, List<Session>> sorted = new HashMap<>();
for (Session session : sessions) {
UUID serverUUID = session.getUnsafe(SessionKeys.SERVER_UUID);
List<Session> serverSessions = sorted.getOrDefault(serverUUID, new ArrayList<>());
serverSessions.add(session);
sorted.put(serverUUID, serverSessions);
}
return sorted;
}
public int toPlayerDeathCount() {
return toPlayerDeathList().size();
}

View File

@ -19,6 +19,7 @@ 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.containers.SupplierDataContainer;
import com.djrapitops.plan.data.store.keys.AnalysisKeys;
import com.djrapitops.plan.data.store.keys.NetworkKeys;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
@ -146,7 +147,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo {
for (Server server : servers) {
UUID serverUUID = server.getUuid();
DataContainer serverContainer = new DataContainer();
DataContainer serverContainer = new SupplierDataContainer();
serverContainer.putRawData(serverKey, server);
PlayersMutator serverPlayers = playersMutator.filterPlayedOnServer(serverUUID);

View File

@ -77,6 +77,8 @@ public class WorldTimes {
* @param changeTime Epoch ms the change occurred.
*/
public void updateState(String worldName, String gameMode, long changeTime) {
if (worldName == null || gameMode == null) return;
GMTimes currentGMTimes = times.get(currentWorld);
if (currentWorld.equals(worldName)) {
currentGMTimes.changeState(gameMode, changeTime);
@ -182,4 +184,7 @@ public class WorldTimes {
}
}
public boolean contains(String worldName) {
return times.containsKey(worldName);
}
}

View File

@ -14,31 +14,32 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.processing.processors.player;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.processing.CriticalRunnable;
import java.util.UUID;
package com.djrapitops.plan.db;
/**
* Updates the Kick count of a user.
* Abstract class representing a Database.
* <p>
* All Operations methods should be only called from an asynchronous thread.
*
* @author Rsl1122
*/
public class KickProcessor implements CriticalRunnable {
public abstract class AbstractDatabase implements Database {
private final UUID uuid;
protected DBAccessLock accessLock;
private State state;
private final Database database;
KickProcessor(UUID uuid, Database database) {
this.uuid = uuid;
this.database = database;
public AbstractDatabase() {
state = State.CLOSED;
accessLock = new DBAccessLock(this);
}
@Override
public void run() {
database.save().playerWasKicked(uuid);
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
accessLock.operabilityChanged();
}
}

View File

@ -0,0 +1,73 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction;
/**
* Database Lock that prevents queries and transactions from taking place before database schema is ready.
* <p>
* - OperationCriticalTransactions pass through the Access lock without blocking to allow the initial transactions.
* - Queries inside Transactions skip access log to allow OperationCriticalTransactions perform queries.
*
* @author Rsl1122
*/
public class DBAccessLock {
private final Database database;
private final Object lockObject;
public DBAccessLock(Database database) {
this.database = database;
this.lockObject = new Object();
}
public void checkAccess() {
checkAccess(false);
}
public void checkAccess(Transaction transaction) {
checkAccess(transaction instanceof OperationCriticalTransaction);
}
private void checkAccess(boolean isOperationCriticalTransaction) {
if (isOperationCriticalTransaction) {
return;
}
try {
while (database.getState() != Database.State.OPEN) {
synchronized (lockObject) {
lockObject.wait();
if (database.getState() == Database.State.CLOSED) {
throw new DBOpException("Database failed to open, Query has failed. (This exception is necessary to not keep query threads waiting)");
}
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void operabilityChanged() {
synchronized (lockObject) {
lockObject.notifyAll();
}
}
}

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases;
package com.djrapitops.plan.db;
import java.util.Optional;
@ -58,10 +58,10 @@ public enum DBType {
}
/**
* Used to check if the {@code DBType} supports <b>most</b> MySQL Queries.<p>
* Used to check if the {@code DBType} supports <b>most</b> MySQL MySQLSchemaQueries.<p>
* When specific Statements are not compatible, the {@code DBType} should be checked.
*
* @return if the database supports MySQL Queries
* @return if the database supports MySQL MySQLSchemaQueries
*/
public boolean supportsMySQLQueries() {
return supportingMySQLQueries;

View File

@ -0,0 +1,77 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.transactions.Transaction;
import java.util.concurrent.Future;
/**
* Interface for interacting with a Plan SQL database.
*
* @author Rsl1122
*/
public interface Database {
/**
* Initializes the Database.
* <p>
* Queries can be performed after this request has completed all required transactions for the database operations.
*
* @throws DBInitException if Database fails to initiate.
*/
void init();
void close();
/**
* Execute an SQL Query statement to get a result.
* <p>
* This method should only be called from an asynchronous thread.
*
* @param query QueryStatement to execute.
* @param <T> Type of the object to be returned.
* @return Result of the query.
*/
<T> T query(Query<T> query);
/**
* Execute an SQL Transaction.
*
* @param transaction Transaction to execute.
* @return Future that is finished when the transaction has been executed.
*/
Future<?> executeTransaction(Transaction transaction);
/**
* Used to get the {@code DBType} of the Database
*
* @return the {@code DBType}
* @see DBType
*/
DBType getType();
State getState();
enum State {
CLOSED,
PATCHING,
OPEN
}
}

View File

@ -14,19 +14,18 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.db;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.db.tasks.KeepAliveTask;
import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plan.utilities.java.ThrowableUtils;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.PluginTask;
@ -61,23 +60,21 @@ public class H2DB extends SQLDB {
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, timings, errorHandler);
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, errorHandler);
dbName = databaseFile.getName();
this.databaseFile = databaseFile;
}
@Override
public void setupDataSource() throws DBInitException {
public void setupDataSource() {
try {
connection = getNewConnection(databaseFile);
} catch (SQLException e) {
throw new DBInitException(e);
throw new DBInitException(e.getMessage(), e);
}
execute("SET REFERENTIAL_INTEGRITY FALSE");
startConnectionPingTask();
}
@ -145,22 +142,12 @@ public class H2DB extends SQLDB {
stopConnectionPingTask();
if (connection != null) {
logger.debug("H2DB " + dbName + ": Closed Connection");
logger.debug("H2 Connection close prompted by: " + ThrowableUtils.findCallerAfterClass(Thread.currentThread().getStackTrace(), H2DB.class));
logger.debug("H2 " + dbName + ": Closed Connection");
MiscUtils.close(connection);
}
}
@Override
public void commit(Connection connection) {
try {
connection.commit();
} catch (SQLException e) {
if (!e.getMessage().contains("cannot commit")) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
}
}
@Override
public void returnToPool(Connection connection) {
// Connection pool not in use, no action required.
@ -189,7 +176,6 @@ public class H2DB extends SQLDB {
private final NetworkContainer.Factory networkContainerFactory;
private final RunnableFactory runnableFactory;
private final PluginLogger logger;
private final Timings timings;
private final ErrorHandler errorHandler;
private PlanFiles files;
@ -202,7 +188,6 @@ public class H2DB extends SQLDB {
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
this.locale = locale;
@ -212,7 +197,6 @@ public class H2DB extends SQLDB {
this.networkContainerFactory = networkContainerFactory;
this.runnableFactory = runnableFactory;
this.logger = logger;
this.timings = timings;
this.errorHandler = errorHandler;
}
@ -228,7 +212,7 @@ public class H2DB extends SQLDB {
return new H2DB(databaseFile,
locale, config, serverInfo,
networkContainerFactory,
runnableFactory, logger, timings, errorHandler
runnableFactory, logger, errorHandler
);
}

View File

@ -14,12 +14,11 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.db;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
@ -64,7 +63,7 @@ public class MySQLDB extends SQLDB {
Timings timings,
ErrorHandler errorHandler
) {
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, pluginLogger, timings, errorHandler);
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, pluginLogger, errorHandler);
}
private static synchronized void increment() {
@ -80,7 +79,7 @@ public class MySQLDB extends SQLDB {
* Setups the {@link HikariDataSource}
*/
@Override
public void setupDataSource() throws DBInitException {
public void setupDataSource() {
try {
HikariConfig hikariConfig = new HikariConfig();
@ -88,7 +87,8 @@ public class MySQLDB extends SQLDB {
String port = config.get(DatabaseSettings.MYSQL_PORT);
String database = config.get(DatabaseSettings.MYSQL_DATABASE);
String launchOptions = config.get(DatabaseSettings.MYSQL_LAUNCH_OPTIONS);
if (launchOptions.isEmpty() || !launchOptions.startsWith("?") || launchOptions.endsWith("&")) {
// REGEX: match "?", match "word=word&" *-times, match "word=word"
if (launchOptions.isEmpty() || !launchOptions.matches("\\?((\\w*=\\w*)&)*(\\w*=\\w*)")) {
launchOptions = "?rewriteBatchedStatements=true&useSSL=false";
logger.error(locale.getString(PluginLang.DB_MYSQL_LAUNCH_OPTIONS_FAIL, launchOptions));
}
@ -109,9 +109,7 @@ public class MySQLDB extends SQLDB {
hikariConfig.setLeakDetectionThreshold(TimeUnit.MINUTES.toMillis(10L));
this.dataSource = new HikariDataSource(hikariConfig);
getConnection();
} catch (HikariPool.PoolInitializationException | SQLException e) {
} catch (HikariPool.PoolInitializationException e) {
throw new DBInitException("Failed to set-up HikariCP Datasource: " + e.getMessage(), e);
}
}
@ -127,11 +125,12 @@ public class MySQLDB extends SQLDB {
try {
setupDataSource();
// get new connection after restarting pool
return dataSource.getConnection();
connection = dataSource.getConnection();
} catch (DBInitException e) {
throw new DBOpException("Failed to restart DataSource after a connection was invalid: " + e.getMessage(), e);
}
}
if (connection.getAutoCommit()) connection.setAutoCommit(false);
return connection;
}
@ -155,11 +154,6 @@ public class MySQLDB extends SQLDB {
}
}
@Override
public void commit(Connection connection) {
returnToPool(connection);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -0,0 +1,273 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.api.exceptions.database.FatalDBException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.access.transactions.init.CreateIndexTransaction;
import com.djrapitops.plan.db.access.transactions.init.CreateTablesTransaction;
import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction;
import com.djrapitops.plan.db.patches.*;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.utilities.java.ThrowableUtils;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.function.BiFunction;
import java.util.function.Supplier;
/**
* Class containing main logic for different data related save and load functionality.
*
* @author Rsl1122
*/
public abstract class SQLDB extends AbstractDatabase {
private final Supplier<UUID> serverUUIDSupplier;
protected final Locale locale;
protected final PlanConfig config;
protected final NetworkContainer.Factory networkContainerFactory;
protected final RunnableFactory runnableFactory;
protected final PluginLogger logger;
protected final ErrorHandler errorHandler;
private Supplier<ExecutorService> transactionExecutorServiceProvider;
private ExecutorService transactionExecutor;
public SQLDB(
Supplier<UUID> serverUUIDSupplier,
Locale locale,
PlanConfig config,
NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory,
PluginLogger logger,
ErrorHandler errorHandler
) {
this.serverUUIDSupplier = serverUUIDSupplier;
this.locale = locale;
this.config = config;
this.networkContainerFactory = networkContainerFactory;
this.runnableFactory = runnableFactory;
this.logger = logger;
this.errorHandler = errorHandler;
this.transactionExecutorServiceProvider = () -> Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Plan " + getClass().getSimpleName() + "-transaction-thread-%d").build());
}
@Override
public void init() {
List<Runnable> unfinishedTransactions = closeTransactionExecutor(transactionExecutor);
this.transactionExecutor = transactionExecutorServiceProvider.get();
setState(State.PATCHING);
setupDataSource();
setupDatabase();
for (Runnable unfinishedTransaction : unfinishedTransactions) {
transactionExecutor.submit(unfinishedTransaction);
}
// If an OperationCriticalTransaction fails open is set to false.
// See executeTransaction method below.
if (getState() == State.CLOSED) {
throw new DBInitException("Failed to set-up Database");
}
}
private List<Runnable> closeTransactionExecutor(ExecutorService transactionExecutor) {
if (transactionExecutor == null || transactionExecutor.isShutdown() || transactionExecutor.isTerminated()) {
return Collections.emptyList();
}
transactionExecutor.shutdown();
try {
Long waitMs = config.getOrDefault(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY, TimeUnit.SECONDS.toMillis(20L));
if (!transactionExecutor.awaitTermination(waitMs, TimeUnit.MILLISECONDS)) {
List<Runnable> unfinished = transactionExecutor.shutdownNow();
int unfinishedCount = unfinished.size();
if (unfinishedCount > 0) {
logger.warn(unfinishedCount + " unfinished database transactions were not executed.");
}
return unfinished;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return Collections.emptyList();
}
Patch[] patches() {
return new Patch[]{
new Version10Patch(),
new GeoInfoLastUsedPatch(),
new SessionAFKTimePatch(),
new KillsServerIDPatch(),
new WorldTimesSeverIDPatch(),
new WorldsServerIDPatch(),
new NicknameLastSeenPatch(),
new VersionTableRemovalPatch(),
new DiskUsagePatch(),
new WorldsOptimizationPatch(),
new WorldTimesOptimizationPatch(),
new KillsOptimizationPatch(),
new SessionsOptimizationPatch(),
new PingOptimizationPatch(),
new NicknamesOptimizationPatch(),
new UserInfoOptimizationPatch(),
new GeoInfoOptimizationPatch(),
new TransferTableRemovalPatch(),
new IPHashPatch(),
new IPAnonPatch(),
new BadAFKThresholdValuePatch()
};
}
/**
* Ensures connection functions correctly and all tables exist.
* <p>
* Updates to latest schema.
*/
private void setupDatabase() {
executeTransaction(new CreateTablesTransaction());
for (Patch patch : patches()) {
executeTransaction(patch);
}
executeTransaction(new OperationCriticalTransaction() {
@Override
protected void performOperations() {
if (getState() == State.PATCHING) setState(State.OPEN);
}
});
registerIndexCreationTask();
}
private void registerIndexCreationTask() {
try {
runnableFactory.create("Database Index Creation", new AbsRunnable() {
@Override
public void run() {
executeTransaction(new CreateIndexTransaction());
}
}).runTaskLaterAsynchronously(TimeAmount.toTicks(1, TimeUnit.MINUTES));
} catch (Exception ignore) {
// Task failed to register because plugin is being disabled
}
}
/**
* Set up the source for connections.
*
* @throws DBInitException If the DataSource fails to be initialized.
*/
public abstract void setupDataSource();
@Override
public void close() {
setState(State.CLOSED);
closeTransactionExecutor(transactionExecutor);
}
public abstract Connection getConnection() throws SQLException;
public abstract void returnToPool(Connection connection);
@Override
public <T> T query(Query<T> query) {
accessLock.checkAccess();
return query.executeQuery(this);
}
@Override
public Future<?> executeTransaction(Transaction transaction) {
if (getState() == State.CLOSED) {
throw new DBOpException("Transaction tried to execute although database is closed.");
}
Exception origin = new Exception();
return CompletableFuture.supplyAsync(() -> {
accessLock.checkAccess(transaction);
transaction.executeTransaction(this);
return CompletableFuture.completedFuture(null);
}, getTransactionExecutor()).handle(errorHandler(origin));
}
private BiFunction<CompletableFuture<Object>, Throwable, CompletableFuture<Object>> errorHandler(Exception origin) {
return (obj, throwable) -> {
if (throwable == null) {
return CompletableFuture.completedFuture(null);
}
if (throwable instanceof FatalDBException) {
setState(State.CLOSED);
}
ThrowableUtils.appendEntryPointToCause(throwable, origin);
errorHandler.log(L.ERROR, getClass(), throwable);
return CompletableFuture.completedFuture(null);
};
}
private ExecutorService getTransactionExecutor() {
if (transactionExecutor == null) {
transactionExecutor = transactionExecutorServiceProvider.get();
}
return transactionExecutor;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SQLDB sqldb = (SQLDB) o;
return getType() == sqldb.getType();
}
@Override
public int hashCode() {
return Objects.hash(getType().getName());
}
public Supplier<UUID> getServerUUIDSupplier() {
return serverUUIDSupplier;
}
public NetworkContainer.Factory getNetworkContainerFactory() {
return networkContainerFactory;
}
public void setTransactionExecutorServiceProvider(Supplier<ExecutorService> transactionExecutorServiceProvider) {
this.transactionExecutorServiceProvider = transactionExecutorServiceProvider;
}
}

View File

@ -14,18 +14,18 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.db;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.db.tasks.KeepAliveTask;
import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plan.utilities.java.ThrowableUtils;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@ -59,20 +59,21 @@ public class SQLiteDB extends SQLDB {
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, timings, errorHandler);
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, errorHandler);
dbName = databaseFile.getName();
this.databaseFile = databaseFile;
}
@Override
public void setupDataSource() throws DBInitException {
public void setupDataSource() {
try {
if (connection != null) connection.close();
connection = getNewConnection(databaseFile);
} catch (SQLException e) {
throw new DBInitException(e);
throw new DBInitException(e.getMessage(), e);
}
startConnectionPingTask();
}
@ -140,6 +141,8 @@ public class SQLiteDB extends SQLDB {
@Override
public void close() {
logger.debug("SQLite Connection close prompted by: " + ThrowableUtils.findCallerAfterClass(Thread.currentThread().getStackTrace(), SQLiteDB.class));
super.close();
stopConnectionPingTask();
@ -149,17 +152,6 @@ public class SQLiteDB extends SQLDB {
}
}
@Override
public void commit(Connection connection) {
try {
connection.commit();
} catch (SQLException e) {
if (!e.getMessage().contains("cannot commit")) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
}
}
@Override
public void returnToPool(Connection connection) {
// Connection pool not in use, no action required.
@ -188,7 +180,6 @@ public class SQLiteDB extends SQLDB {
private final NetworkContainer.Factory networkContainerFactory;
private final RunnableFactory runnableFactory;
private final PluginLogger logger;
private final Timings timings;
private final ErrorHandler errorHandler;
private PlanFiles files;
@ -201,7 +192,6 @@ public class SQLiteDB extends SQLDB {
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
this.locale = locale;
@ -211,7 +201,6 @@ public class SQLiteDB extends SQLDB {
this.networkContainerFactory = networkContainerFactory;
this.runnableFactory = runnableFactory;
this.logger = logger;
this.timings = timings;
this.errorHandler = errorHandler;
}
@ -227,7 +216,7 @@ public class SQLiteDB extends SQLDB {
return new SQLiteDB(databaseFile,
locale, config, serverInfo,
networkContainerFactory,
runnableFactory, logger, timings, errorHandler
runnableFactory, logger, errorHandler
);
}

View File

@ -14,21 +14,24 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.comparators;
package com.djrapitops.plan.db.access;
import com.djrapitops.plan.data.container.UserInfo;
import java.util.Comparator;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Comparator for UserInfo so that most recently seen is first.
* SQL executing batch statement that closes appropriate elements.
*
* @author Rsl1122
*/
public class UserInfoLastPlayedComparator implements Comparator<UserInfo> {
public abstract class ExecBatchStatement extends ExecStatement {
public ExecBatchStatement(String sql) {
super(sql);
}
@Override
public int compare(UserInfo u1, UserInfo u2) {
return Long.compare(u2.getLastSeen(), u1.getLastSeen());
protected boolean callExecute(PreparedStatement statement) throws SQLException {
return statement.executeBatch().length > 0;
}
}
}

View File

@ -14,8 +14,11 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql.processing;
package com.djrapitops.plan.db.access;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@ -24,24 +27,35 @@ import java.sql.SQLException;
*
* @author Rsl1122
*/
public abstract class ExecStatement extends AbstractSQLStatement {
public abstract class ExecStatement implements Executable {
private final String sql;
public ExecStatement(String sql) {
super(sql);
this.sql = sql;
}
@Override
public boolean execute(Connection connection) {
try {
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
return execute(preparedStatement);
}
} catch (SQLException e) {
throw DBOpException.forCause(sql, e);
}
}
public boolean execute(PreparedStatement statement) throws SQLException {
startBenchmark();
try {
prepare(statement);
return callExecute(statement);
} finally {
statement.close();
stopBenchmark();
}
}
private boolean callExecute(PreparedStatement statement) throws SQLException {
protected boolean callExecute(PreparedStatement statement) throws SQLException {
if (sql.startsWith("UPDATE") || sql.startsWith("INSERT") || sql.startsWith("DELETE") || sql.startsWith("REPLACE")) {
return statement.executeUpdate() > 0;
} else {
@ -50,17 +64,6 @@ public abstract class ExecStatement extends AbstractSQLStatement {
}
}
public void executeBatch(PreparedStatement statement) throws SQLException {
startBatchBenchmark();
try {
prepare(statement);
statement.executeBatch();
} finally {
statement.close();
stopBatchBenchmark();
}
}
public abstract void prepare(PreparedStatement statement) throws SQLException;
public String getSql() {

View File

@ -14,15 +14,21 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.operation;
package com.djrapitops.plan.db.access;
import java.util.UUID;
import java.sql.Connection;
public interface RemoveOperations {
/**
* Interface for everything that updates rows in the database.
*
* @author Rsl1122
*/
public interface Executable {
void player(UUID uuid);
boolean execute(Connection connection);
void everything();
static Executable empty() {
return i -> true;
}
void webUser(String name);
}

View File

@ -14,33 +14,31 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.processing.processors.player;
package com.djrapitops.plan.db.access;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.processing.CriticalRunnable;
import java.util.UUID;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Ends a session and saves it to the database.
* SQL query of a COUNT statement that closes proper elements.
*
* @author Rsl1122
*/
public class EndSessionProcessor implements CriticalRunnable {
public abstract class HasMoreThanZeroQueryStatement extends QueryStatement<Boolean> {
private final UUID uuid;
private final long time;
private String countColumnName = "c";
private final SessionCache sessionCache;
public HasMoreThanZeroQueryStatement(String sql) {
super(sql);
}
EndSessionProcessor(UUID uuid, long time, SessionCache sessionCache) {
this.uuid = uuid;
this.time = time;
this.sessionCache = sessionCache;
public HasMoreThanZeroQueryStatement(String sql, String countColumnName) {
super(sql);
this.countColumnName = countColumnName;
}
@Override
public void run() {
sessionCache.endSession(uuid, time);
public Boolean processResults(ResultSet set) throws SQLException {
return set.next() && set.getInt(countColumnName) > 0;
}
}
}

View File

@ -14,19 +14,18 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql.statements;
package com.djrapitops.plan.db.access;
import com.djrapitops.plan.db.SQLDB;
/**
* Interface for SQL column enum compatibility.
* Interface for everything that returns results from the database.
*
* @param <T> Type of the result.
* @author Rsl1122
*/
public interface Column {
public interface Query<T> {
default String get() {
return toString();
}
String toString();
T executeQuery(SQLDB db);
}

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql.processing;
package com.djrapitops.plan.db.access;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

View File

@ -14,8 +14,12 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql.processing;
package com.djrapitops.plan.db.access;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.SQLDB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -25,8 +29,9 @@ import java.sql.SQLException;
*
* @author Rsl1122
*/
public abstract class QueryStatement<T> extends AbstractSQLStatement {
public abstract class QueryStatement<T> implements Query<T> {
private final String sql;
private final int fetchSize;
public QueryStatement(String sql) {
@ -34,12 +39,26 @@ public abstract class QueryStatement<T> extends AbstractSQLStatement {
}
public QueryStatement(String sql, int fetchSize) {
super(sql);
this.sql = sql;
this.fetchSize = fetchSize;
}
@Override
public T executeQuery(SQLDB db) {
Connection connection = null;
try {
connection = db.getConnection();
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
return executeQuery(preparedStatement);
}
} catch (SQLException e) {
throw DBOpException.forCause(sql, e);
} finally {
db.returnToPool(connection);
}
}
public T executeQuery(PreparedStatement statement) throws SQLException {
startBenchmark();
try {
statement.setFetchSize(fetchSize);
prepare(statement);
@ -48,7 +67,6 @@ public abstract class QueryStatement<T> extends AbstractSQLStatement {
}
} finally {
statement.close();
stopBenchmark();
}
}
@ -59,4 +77,9 @@ public abstract class QueryStatement<T> extends AbstractSQLStatement {
public String getSql() {
return sql;
}
@Override
public String toString() {
return "Query (" + sql + ')';
}
}

View File

@ -0,0 +1,331 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries;
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.container.TPS;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.data.time.GMTimes;
import com.djrapitops.plan.db.access.ExecBatchStatement;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.sql.tables.*;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.UUID;
/**
* Static method class for single item store queries.
*
* @author Rsl1122
*/
public class DataStoreQueries {
private DataStoreQueries() {
/* static method class */
}
/**
* Store the used command in the database.
*
* @param serverUUID UUID of the Plan server.
* @param commandName Name of the command that was used.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeUsedCommandInformation(UUID serverUUID, String commandName) {
return connection -> {
if (!updateCommandUsage(serverUUID, commandName).execute(connection)) {
insertNewCommandUsage(serverUUID, commandName).execute(connection);
}
return false;
};
}
private static Executable updateCommandUsage(UUID serverUUID, String commandName) {
return new ExecStatement(CommandUseTable.UPDATE_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
statement.setString(2, commandName);
}
};
}
private static Executable insertNewCommandUsage(UUID serverUUID, String commandName) {
return new ExecStatement(CommandUseTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, commandName);
statement.setInt(2, 1);
statement.setString(3, serverUUID.toString());
}
};
}
/**
* Store a finished session in the database.
*
* @param session Session, of which {@link Session#endSession(long)} has been called.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
* @throws IllegalArgumentException If {@link Session#endSession(long)} has not yet been called.
*/
public static Executable storeSession(Session session) {
session.getValue(SessionKeys.END).orElseThrow(() -> new IllegalArgumentException("Attempted to save a session that has not ended."));
return connection -> {
storeSessionInformation(session).execute(connection);
storeSessionKills(session).execute(connection);
return storeSessionWorldTimes(session).execute(connection);
};
}
private static Executable storeSessionInformation(Session session) {
return new ExecStatement(SessionsTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, session.getUnsafe(SessionKeys.UUID).toString());
statement.setLong(2, session.getUnsafe(SessionKeys.START));
statement.setLong(3, session.getUnsafe(SessionKeys.END));
statement.setInt(4, session.getUnsafe(SessionKeys.DEATH_COUNT));
statement.setInt(5, session.getUnsafe(SessionKeys.MOB_KILL_COUNT));
statement.setLong(6, session.getUnsafe(SessionKeys.AFK_TIME));
statement.setString(7, session.getUnsafe(SessionKeys.SERVER_UUID).toString());
}
};
}
private static Executable storeSessionKills(Session session) {
return new ExecBatchStatement(KillsTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
KillsTable.addSessionKillsToBatch(statement, session);
}
};
}
public static Executable insertWorldName(UUID serverUUID, String worldName) {
return new ExecStatement(WorldTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, worldName);
statement.setString(2, serverUUID.toString());
}
};
}
private static Executable storeSessionWorldTimes(Session session) {
if (session.getValue(SessionKeys.WORLD_TIMES).map(times -> times.getWorldTimes().isEmpty()).orElse(true)) {
return Executable.empty();
}
return new ExecBatchStatement(WorldTimesTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
WorldTimesTable.addSessionWorldTimesToBatch(statement, session, GMTimes.getGMKeyArray());
}
};
}
/**
* Store player's Geo Information in the database.
*
* @param playerUUID UUID of the player.
* @param geoInfo GeoInfo of the player.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeGeoInfo(UUID playerUUID, GeoInfo geoInfo) {
return connection -> {
if (!updateGeoInfo(playerUUID, geoInfo).execute(connection)) {
return insertGeoInfo(playerUUID, geoInfo).execute(connection);
}
return false;
};
}
private static Executable updateGeoInfo(UUID playerUUID, GeoInfo geoInfo) {
return new ExecStatement(GeoInfoTable.UPDATE_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, geoInfo.getDate());
statement.setString(2, playerUUID.toString());
statement.setString(3, geoInfo.getIpHash());
statement.setString(4, geoInfo.getGeolocation());
}
};
}
private static Executable insertGeoInfo(UUID playerUUID, GeoInfo geoInfo) {
return new ExecStatement(GeoInfoTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setString(2, geoInfo.getIp());
statement.setString(3, geoInfo.getIpHash());
statement.setString(4, geoInfo.getGeolocation());
statement.setLong(5, geoInfo.getDate());
}
};
}
/**
* Store a BaseUser for the player in the database.
*
* @param playerUUID UUID of the player.
* @param registered Time the player registered on the server for the first time.
* @param playerName Name of the player.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable registerBaseUser(UUID playerUUID, long registered, String playerName) {
return new ExecStatement(UsersTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setString(2, playerName);
statement.setLong(3, registered);
statement.setInt(4, 0); // times kicked
}
};
}
/**
* Update player's name in the database in case they have changed it.
*
* @param playerUUID UUID of the player.
* @param playerName Name of the player.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable updatePlayerName(UUID playerUUID, String playerName) {
String sql = "UPDATE " + UsersTable.TABLE_NAME + " SET " + UsersTable.USER_NAME + "=?" +
" WHERE " + UsersTable.USER_UUID + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerName);
statement.setString(2, playerUUID.toString());
}
};
}
/**
* Store UserInfo about a player on a server in the database.
*
* @param playerUUID UUID of the player.
* @param registered Time the player registered on the server.
* @param serverUUID UUID of the Plan server.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable registerUserInfo(UUID playerUUID, long registered, UUID serverUUID) {
return new ExecStatement(UserInfoTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setLong(2, registered);
statement.setString(3, serverUUID.toString());
statement.setBoolean(4, false); // Banned
statement.setBoolean(5, false); // Operator
}
};
}
/**
* Store Ping data of a player on a server.
*
* @param playerUUID UUID of the player.
* @param serverUUID UUID of the Plan server.
* @param ping Ping data entry
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storePing(UUID playerUUID, UUID serverUUID, Ping ping) {
return new ExecStatement(PingTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setString(2, serverUUID.toString());
statement.setLong(3, ping.getDate());
statement.setInt(4, ping.getMin());
statement.setInt(5, ping.getMax());
statement.setDouble(6, ping.getAverage());
}
};
}
/**
* Store TPS data of a server.
*
* @param serverUUID UUID of the Plan server.
* @param tps TPS data entry
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeTPS(UUID serverUUID, TPS tps) {
return new ExecStatement(TPSTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
statement.setLong(2, tps.getDate());
statement.setDouble(3, tps.getTicksPerSecond());
statement.setInt(4, tps.getPlayers());
statement.setDouble(5, tps.getCPUUsage());
statement.setLong(6, tps.getUsedMemory());
statement.setDouble(7, tps.getEntityCount());
statement.setDouble(8, tps.getChunksLoaded());
statement.setLong(9, tps.getFreeDiskSpace());
}
};
}
/**
* Store nickname information of a player on a server.
*
* @param playerUUID UUID of the player.
* @param nickname Nickname information.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storePlayerNickname(UUID playerUUID, Nickname nickname) {
return connection -> {
if (!updatePlayerNickname(playerUUID, nickname).execute(connection)) {
insertPlayerNickname(playerUUID, nickname).execute(connection);
}
return false;
};
}
private static Executable updatePlayerNickname(UUID playerUUID, Nickname nickname) {
return new ExecStatement(NicknamesTable.UPDATE_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, nickname.getDate());
statement.setString(2, nickname.getName());
statement.setString(3, playerUUID.toString());
statement.setString(4, nickname.getServerUUID().toString());
}
};
}
private static Executable insertPlayerNickname(UUID playerUUID, Nickname nickname) {
return new ExecStatement(NicknamesTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setString(2, nickname.getServerUUID().toString());
statement.setString(3, nickname.getName());
statement.setLong(4, nickname.getDate());
}
};
}
}

View File

@ -0,0 +1,149 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries;
import com.djrapitops.plan.data.container.TPS;
import com.djrapitops.plan.data.container.builders.TPSBuilder;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryAllStatement;
import com.djrapitops.plan.db.sql.tables.CommandUseTable;
import com.djrapitops.plan.db.sql.tables.ServerTable;
import com.djrapitops.plan.db.sql.tables.TPSTable;
import com.djrapitops.plan.db.sql.tables.WorldTable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
/**
* Static method class for queries that use large amount of memory.
*
* @author Rsl1122
*/
public class LargeFetchQueries {
private LargeFetchQueries() {
/* Static method class */
}
/**
* Query database for all command usage data.
*
* @return Multi map: Server UUID - (Command name - Usage count)
*/
public static Query<Map<UUID, Map<String, Integer>>> fetchAllCommandUsageData() {
String serverIDColumn = ServerTable.TABLE_NAME + "." + ServerTable.SERVER_ID;
String serverUUIDColumn = ServerTable.TABLE_NAME + "." + ServerTable.SERVER_UUID + " as s_uuid";
String sql = "SELECT " +
CommandUseTable.COMMAND + ", " +
CommandUseTable.TIMES_USED + ", " +
serverUUIDColumn +
" FROM " + CommandUseTable.TABLE_NAME +
" INNER JOIN " + ServerTable.TABLE_NAME + " on " + serverIDColumn + "=" + CommandUseTable.SERVER_ID;
return new QueryAllStatement<Map<UUID, Map<String, Integer>>>(sql, 10000) {
@Override
public Map<UUID, Map<String, Integer>> processResults(ResultSet set) throws SQLException {
Map<UUID, Map<String, Integer>> map = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString("s_uuid"));
Map<String, Integer> serverMap = map.getOrDefault(serverUUID, new HashMap<>());
String command = set.getString(CommandUseTable.COMMAND);
int timesUsed = set.getInt(CommandUseTable.TIMES_USED);
serverMap.put(command, timesUsed);
map.put(serverUUID, serverMap);
}
return map;
}
};
}
/**
* Query database for TPS data.
*
* @return Map: Server UUID - List of TPS data
*/
public static Query<Map<UUID, List<TPS>>> fetchAllTPSData() {
String serverIDColumn = ServerTable.TABLE_NAME + "." + ServerTable.SERVER_ID;
String serverUUIDColumn = ServerTable.TABLE_NAME + "." + ServerTable.SERVER_UUID + " as s_uuid";
String sql = "SELECT " +
TPSTable.DATE + ", " +
TPSTable.TPS + ", " +
TPSTable.PLAYERS_ONLINE + ", " +
TPSTable.CPU_USAGE + ", " +
TPSTable.RAM_USAGE + ", " +
TPSTable.ENTITIES + ", " +
TPSTable.CHUNKS + ", " +
TPSTable.FREE_DISK + ", " +
serverUUIDColumn +
" FROM " + TPSTable.TABLE_NAME +
" INNER JOIN " + ServerTable.TABLE_NAME + " on " + serverIDColumn + "=" + TPSTable.SERVER_ID;
return new QueryAllStatement<Map<UUID, List<TPS>>>(sql, 50000) {
@Override
public Map<UUID, List<TPS>> processResults(ResultSet set) throws SQLException {
Map<UUID, List<TPS>> serverMap = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString("s_uuid"));
List<TPS> tpsList = serverMap.getOrDefault(serverUUID, new ArrayList<>());
TPS tps = TPSBuilder.get()
.date(set.getLong(TPSTable.DATE))
.tps(set.getDouble(TPSTable.TPS))
.playersOnline(set.getInt(TPSTable.PLAYERS_ONLINE))
.usedCPU(set.getDouble(TPSTable.CPU_USAGE))
.usedMemory(set.getLong(TPSTable.RAM_USAGE))
.entities(set.getInt(TPSTable.ENTITIES))
.chunksLoaded(set.getInt(TPSTable.CHUNKS))
.freeDiskSpace(set.getLong(TPSTable.FREE_DISK))
.toTPS();
tpsList.add(tps);
serverMap.put(serverUUID, tpsList);
}
return serverMap;
}
};
}
/**
* Query database for world names.
*
* @return Map: Server UUID - List of world names
*/
public static Query<Map<UUID, Collection<String>>> fetchAllWorldNames() {
String sql = "SELECT * FROM " + WorldTable.TABLE_NAME;
return new QueryAllStatement<Map<UUID, Collection<String>>>(sql, 1000) {
@Override
public Map<UUID, Collection<String>> processResults(ResultSet set) throws SQLException {
Map<UUID, Collection<String>> worldMap = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString(WorldTable.SERVER_UUID));
Collection<String> worlds = worldMap.getOrDefault(serverUUID, new HashSet<>());
worlds.add(set.getString(WorldTable.NAME));
worldMap.put(serverUUID, worlds);
}
return worldMap;
}
};
}
}

View File

@ -0,0 +1,424 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.data.container.*;
import com.djrapitops.plan.data.store.keys.SessionKeys;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.data.time.GMTimes;
import com.djrapitops.plan.db.access.ExecBatchStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.sql.tables.*;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plugin.utilities.Verify;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Static method class for large storage queries.
*
* @author Rsl1122
*/
public class LargeStoreQueries {
private LargeStoreQueries() {
/* Static method class */
}
/**
* Execute a big batch of command use insert statements.
*
* @param ofServers Multi map: Server UUID - (Command name - Usage count)
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllCommandUsageData(Map<UUID, Map<String, Integer>> ofServers) {
if (ofServers.isEmpty()) {
return Executable.empty();
}
return new ExecBatchStatement(CommandUseTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
// Every Server
for (Map.Entry<UUID, Map<String, Integer>> serverEntry : ofServers.entrySet()) {
UUID serverUUID = serverEntry.getKey();
// Every Command
for (Map.Entry<String, Integer> entry : serverEntry.getValue().entrySet()) {
String command = entry.getKey();
int timesUsed = entry.getValue();
statement.setString(1, command);
statement.setInt(2, timesUsed);
statement.setString(3, serverUUID.toString());
statement.addBatch();
}
}
}
};
}
/**
* Execute a big batch of GeoInfo insert statements.
*
* @param ofUsers Map: Player UUID - List of GeoInfo
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllGeoInformation(Map<UUID, List<GeoInfo>> ofUsers) {
if (Verify.isEmpty(ofUsers)) {
return Executable.empty();
}
return new ExecBatchStatement(GeoInfoTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
// Every User
for (Map.Entry<UUID, List<GeoInfo>> playerEntry : ofUsers.entrySet()) {
UUID playerUUID = playerEntry.getKey();
// Every GeoInfo
for (GeoInfo info : playerEntry.getValue()) {
String ip = info.getIp();
String ipHash = info.getIpHash();
String geoLocation = info.getGeolocation();
long lastUsed = info.getDate();
statement.setString(1, playerUUID.toString());
statement.setString(2, ip);
statement.setString(3, ipHash);
statement.setString(4, geoLocation);
statement.setLong(5, lastUsed);
statement.addBatch();
}
}
}
};
}
/**
* Execute a big batch of nickname insert statements.
*
* @param ofServersAndUsers Multimap: Server UUID - (Player UUID - List of nicknames)
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllNicknameData(Map<UUID, Map<UUID, List<Nickname>>> ofServersAndUsers) {
if (Verify.isEmpty(ofServersAndUsers)) {
return Executable.empty();
}
return new ExecBatchStatement(NicknamesTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
// Every Server
for (Map.Entry<UUID, Map<UUID, List<Nickname>>> serverEntry : ofServersAndUsers.entrySet()) {
UUID serverUUID = serverEntry.getKey();
// Every User
for (Map.Entry<UUID, List<Nickname>> entry : serverEntry.getValue().entrySet()) {
UUID uuid = entry.getKey();
// Every Nickname
List<Nickname> nicknames = entry.getValue();
for (Nickname nickname : nicknames) {
statement.setString(1, uuid.toString());
statement.setString(2, serverUUID.toString());
statement.setString(3, nickname.getName());
statement.setLong(4, nickname.getDate());
statement.addBatch();
}
}
}
}
};
}
/**
* Execute a big batch of web user insert statements.
*
* @param users Collection of Plan WebUsers.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllPlanWebUsers(Collection<WebUser> users) {
if (Verify.isEmpty(users)) {
return Executable.empty();
}
return new ExecBatchStatement(SecurityTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (WebUser user : users) {
String userName = user.getName();
String pass = user.getSaltedPassHash();
int permLvl = user.getPermLevel();
statement.setString(1, userName);
statement.setString(2, pass);
statement.setInt(3, permLvl);
statement.addBatch();
}
}
};
}
/**
* Execute a big batch of server infromation insert statements.
*
* @param servers Collection of Plan Servers.
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllPlanServerInformation(Collection<Server> servers) {
if (Verify.isEmpty(servers)) {
return Executable.empty();
}
return new ExecBatchStatement(ServerTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (Server info : servers) {
UUID uuid = info.getUuid();
String name = info.getName();
String webAddress = info.getWebAddress();
if (uuid == null) {
continue;
}
statement.setString(1, uuid.toString());
statement.setString(2, name);
statement.setString(3, webAddress);
statement.setBoolean(4, true);
statement.setInt(5, info.getMaxPlayers());
statement.addBatch();
}
}
};
}
/**
* Execute a big batch of TPS insert statements.
*
* @param ofServers Map: Server UUID - List of TPS data
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllTPSData(Map<UUID, List<TPS>> ofServers) {
if (Verify.isEmpty(ofServers)) {
return Executable.empty();
}
return new ExecBatchStatement(TPSTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
// Every Server
for (Map.Entry<UUID, List<TPS>> entry : ofServers.entrySet()) {
UUID serverUUID = entry.getKey();
// Every TPS Data point
List<TPS> tpsList = entry.getValue();
for (TPS tps : tpsList) {
statement.setString(1, serverUUID.toString());
statement.setLong(2, tps.getDate());
statement.setDouble(3, tps.getTicksPerSecond());
statement.setInt(4, tps.getPlayers());
statement.setDouble(5, tps.getCPUUsage());
statement.setLong(6, tps.getUsedMemory());
statement.setDouble(7, tps.getEntityCount());
statement.setDouble(8, tps.getChunksLoaded());
statement.setLong(9, tps.getFreeDiskSpace());
statement.addBatch();
}
}
}
};
}
/**
* Execute a big batch of Per server UserInfo insert statements.
*
* @param ofServers Map: Server UUID - List of user information
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storePerServerUserInformation(Map<UUID, List<UserInfo>> ofServers) {
if (Verify.isEmpty(ofServers)) {
return Executable.empty();
}
return new ExecBatchStatement(UserInfoTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
// Every Server
for (Map.Entry<UUID, List<UserInfo>> entry : ofServers.entrySet()) {
UUID serverUUID = entry.getKey();
// Every User
for (UserInfo user : entry.getValue()) {
statement.setString(1, user.getPlayerUuid().toString());
statement.setLong(2, user.getRegistered());
statement.setString(3, serverUUID.toString());
statement.setBoolean(4, user.isBanned());
statement.setBoolean(5, user.isOperator());
statement.addBatch();
}
}
}
};
}
/**
* Execute a big batch of world name insert statements.
*
* @param ofServers Map: Server UUID - Collection of world names
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllWorldNames(Map<UUID, Collection<String>> ofServers) {
if (Verify.isEmpty(ofServers)) {
return Executable.empty();
}
return new ExecBatchStatement(WorldTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (Map.Entry<UUID, Collection<String>> entry : ofServers.entrySet()) {
UUID serverUUID = entry.getKey();
for (String world : entry.getValue()) {
statement.setString(1, world);
statement.setString(2, serverUUID.toString());
statement.addBatch();
}
}
}
};
}
/**
* Execute a big batch of user information insert statements.
*
* @param ofUsers Collection of BaseUsers
* @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction}
*/
public static Executable storeAllCommonUserInformation(Collection<BaseUser> ofUsers) {
if (Verify.isEmpty(ofUsers)) {
return Executable.empty();
}
return new ExecBatchStatement(UsersTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (BaseUser user : ofUsers) {
statement.setString(1, user.getUuid().toString());
statement.setString(2, user.getName());
statement.setLong(3, user.getRegistered());
statement.setInt(4, user.getTimesKicked());
statement.addBatch();
}
}
};
}
public static Executable storeAllSessionsWithoutKillOrWorldData(Collection<Session> sessions) {
if (Verify.isEmpty(sessions)) {
return Executable.empty();
}
return new ExecBatchStatement(SessionsTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (Session session : sessions) {
statement.setString(1, session.getUnsafe(SessionKeys.UUID).toString());
statement.setLong(2, session.getUnsafe(SessionKeys.START));
statement.setLong(3, session.getUnsafe(SessionKeys.END));
statement.setInt(4, session.getUnsafe(SessionKeys.DEATH_COUNT));
statement.setInt(5, session.getUnsafe(SessionKeys.MOB_KILL_COUNT));
statement.setLong(6, session.getUnsafe(SessionKeys.AFK_TIME));
statement.setString(7, session.getUnsafe(SessionKeys.SERVER_UUID).toString());
statement.addBatch();
}
}
};
}
public static Executable storeAllSessionsWithKillAndWorldData(Collection<Session> sessions) {
return connection -> {
storeAllSessionsWithoutKillOrWorldData(sessions).execute(connection);
storeSessionKillData(sessions).execute(connection);
return storeSessionWorldTimeData(sessions).execute(connection);
};
}
private static Executable storeSessionKillData(Collection<Session> sessions) {
if (Verify.isEmpty(sessions)) {
return Executable.empty();
}
return new ExecBatchStatement(KillsTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (Session session : sessions) {
KillsTable.addSessionKillsToBatch(statement, session);
}
}
};
}
private static Executable storeSessionWorldTimeData(Collection<Session> sessions) {
if (Verify.isEmpty(sessions)) {
return Executable.empty();
}
return new ExecBatchStatement(WorldTimesTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
String[] gms = GMTimes.getGMKeyArray();
for (Session session : sessions) {
WorldTimesTable.addSessionWorldTimesToBatch(statement, session, gms);
}
}
};
}
public static Executable storeAllPingData(Map<UUID, List<Ping>> ofUsers) {
if (Verify.isEmpty(ofUsers)) {
return Executable.empty();
}
return new ExecBatchStatement(PingTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (Map.Entry<UUID, List<Ping>> entry : ofUsers.entrySet()) {
UUID uuid = entry.getKey();
List<Ping> pings = entry.getValue();
for (Ping ping : pings) {
UUID serverUUID = ping.getServerUUID();
long date = ping.getDate();
int minPing = ping.getMin();
int maxPing = ping.getMax();
double avgPing = ping.getAverage();
statement.setString(1, uuid.toString());
statement.setString(2, serverUUID.toString());
statement.setLong(3, date);
statement.setInt(4, minPing);
statement.setInt(5, maxPing);
statement.setDouble(6, avgPing);
statement.addBatch();
}
}
}
};
}
}

View File

@ -0,0 +1,190 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.KillsTable;
import com.djrapitops.plan.db.sql.tables.SessionsTable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Static method class for queries that count together counts for a player on a per server basis.
* <p>
* Example:
* Fetch how much a player has played on servers
*
* @author Rsl1122
*/
public class PerServerAggregateQueries {
private PerServerAggregateQueries() {
/* Static method class */
}
/**
* Find last seen date on servers.
*
* @param playerUUID UUID of the player.
* @return Map: Server UUID - Last seen epoch ms.
*/
public static Query<Map<UUID, Long>> lastSeenOnServers(UUID playerUUID) {
String sql = "SELECT MAX(" + SessionsTable.SESSION_END + ") as last_seen, " +
SessionsTable.SERVER_UUID +
FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.USER_UUID + "=?" +
GROUP_BY + SessionsTable.SERVER_UUID;
return new QueryStatement<Map<UUID, Long>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<UUID, Long> processResults(ResultSet set) throws SQLException {
Map<UUID, Long> lastSeenMap = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID));
long lastSeen = set.getLong("last_seen");
lastSeenMap.put(serverUUID, lastSeen);
}
return lastSeenMap;
}
};
}
/**
* Find player kill count on servers.
*
* @param playerUUID UUID of the player.
* @return Map: Server UUID - Player kill count
*/
public static Query<Map<UUID, Integer>> playerKillCountOnServers(UUID playerUUID) {
String sql = "SELECT COUNT(1) as kill_count, " + KillsTable.SERVER_UUID + FROM + KillsTable.TABLE_NAME +
WHERE + KillsTable.KILLER_UUID + "=?" +
GROUP_BY + KillsTable.SERVER_UUID;
return new QueryStatement<Map<UUID, Integer>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<UUID, Integer> processResults(ResultSet set) throws SQLException {
Map<UUID, Integer> killCountMap = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID));
int lastSeen = set.getInt("kill_count");
killCountMap.put(serverUUID, lastSeen);
}
return killCountMap;
}
};
}
/**
* Find mob kill count on servers.
*
* @param playerUUID UUID of the player.
* @return Map: Server UUID - Mob kill count
*/
public static Query<Map<UUID, Integer>> mobKillCountOnServers(UUID playerUUID) {
String sql = "SELECT SUM(" + SessionsTable.MOB_KILLS + ") as kill_count, " +
SessionsTable.SERVER_UUID + FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.USER_UUID + "=?" +
GROUP_BY + SessionsTable.SERVER_UUID;
return new QueryStatement<Map<UUID, Integer>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<UUID, Integer> processResults(ResultSet set) throws SQLException {
Map<UUID, Integer> killCountMap = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID));
int lastSeen = set.getInt("kill_count");
killCountMap.put(serverUUID, lastSeen);
}
return killCountMap;
}
};
}
/**
* Find how many times a player killed the player on servers.
*
* @param playerUUID UUID of the player.
* @return Map: Server UUID - Mob kill count
*/
public static Query<Map<UUID, Integer>> playerDeathCountOnServers(UUID playerUUID) {
String sql = "SELECT COUNT(1) as death_count, " + KillsTable.SERVER_UUID + FROM + KillsTable.TABLE_NAME +
WHERE + KillsTable.VICTIM_UUID + "=?" +
GROUP_BY + KillsTable.SERVER_UUID;
return new QueryStatement<Map<UUID, Integer>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<UUID, Integer> processResults(ResultSet set) throws SQLException {
Map<UUID, Integer> killCountMap = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID));
int lastSeen = set.getInt("death_count");
killCountMap.put(serverUUID, lastSeen);
}
return killCountMap;
}
};
}
public static Query<Map<UUID, Integer>> totalDeathCountOnServers(UUID playerUUID) {
String sql = "SELECT SUM(" + SessionsTable.DEATHS + ") as death_count, " +
SessionsTable.SERVER_UUID + FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.USER_UUID + "=?" +
GROUP_BY + SessionsTable.SERVER_UUID;
return new QueryStatement<Map<UUID, Integer>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<UUID, Integer> processResults(ResultSet set) throws SQLException {
Map<UUID, Integer> killCountMap = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID));
int lastSeen = set.getInt("death_count");
killCountMap.put(serverUUID, lastSeen);
}
return killCountMap;
}
};
}
}

View File

@ -0,0 +1,107 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries;
import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.UserInfoTable;
import com.djrapitops.plan.db.sql.tables.UsersTable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Static method class for queries that return information related to a single player.
*
* @author Rsl1122
*/
public class PlayerFetchQueries {
private PlayerFetchQueries() {
/* static method class */
}
/**
* Query Player's name by player's UUID.
*
* @param playerUUID UUID of the player.
* @return Optional, Name if found.
*/
public static Query<Optional<String>> playerUserName(UUID playerUUID) {
String sql = "SELECT " + UsersTable.USER_NAME +
FROM + UsersTable.TABLE_NAME +
WHERE + UsersTable.USER_UUID + "=?";
return new QueryStatement<Optional<String>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Optional<String> processResults(ResultSet set) throws SQLException {
if (set.next()) {
return Optional.of(set.getString(UsersTable.USER_NAME));
}
return Optional.empty();
}
};
}
/**
* Check if the player's BaseUser is registered.
*
* @param playerUUID UUID of the player.
* @return True if the player's BaseUser is found
*/
public static Query<Boolean> isPlayerRegistered(UUID playerUUID) {
String sql = "SELECT COUNT(1) as c FROM " + UsersTable.TABLE_NAME +
WHERE + UsersTable.USER_UUID + "=?";
return new HasMoreThanZeroQueryStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
};
}
/**
* Check if the player's UserInfo is registered.
*
* @param playerUUID UUID of the player.
* @param serverUUID UUID of the Plan server.
* @return True if the player's UserInfo is found
*/
public static Query<Boolean> isPlayerRegisteredOnServer(UUID playerUUID, UUID serverUUID) {
String sql = "SELECT COUNT(1) as c FROM " + UserInfoTable.TABLE_NAME +
WHERE + UserInfoTable.USER_UUID + "=?" +
AND + UserInfoTable.SERVER_UUID + "=?";
return new HasMoreThanZeroQueryStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setString(2, serverUUID.toString());
}
};
}
}

View File

@ -0,0 +1,163 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryAllStatement;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Static method class for queries that count how many entries of particular kinds there are for a server.
*
* @author Rsl1122
*/
public class ServerAggregateQueries {
private ServerAggregateQueries() {
/* Static method class */
}
/**
* Count how many users are in the Plan database.
*
* @return Count of base users, all users in a network after Plan installation.
*/
public static Query<Integer> baseUserCount() {
String sql = "SELECT COUNT(1) as c FROM " + UsersTable.TABLE_NAME;
return new QueryAllStatement<Integer>(sql) {
@Override
public Integer processResults(ResultSet set) throws SQLException {
return set.next() ? set.getInt("c") : 0;
}
};
}
/**
* Count how many users are on a server in the network.
*
* @param serverUUID ServerUUID of the Plan server.
* @return Count of users registered to that server after Plan installation.
*/
public static Query<Integer> serverUserCount(UUID serverUUID) {
String sql = "SELECT COUNT(1) as c FROM " + UserInfoTable.TABLE_NAME +
WHERE + UserInfoTable.SERVER_UUID + "=?";
return new QueryStatement<Integer>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
return set.next() ? set.getInt("c") : 0;
}
};
}
/**
* Count how many users are on each server in the network.
* <p>
* Please note that counts can overlap as one user can join multiple servers.
* Use {@link ServerAggregateQueries#baseUserCount()} if you want to count total number of users.
*
* @return Map: Server UUID - Count of users registered to that server
*/
public static Query<Map<UUID, Integer>> serverUserCounts() {
String sql = "SELECT COUNT(1) as c, " + UserInfoTable.SERVER_UUID + FROM + UserInfoTable.TABLE_NAME +
GROUP_BY + UserInfoTable.SERVER_UUID;
return new QueryAllStatement<Map<UUID, Integer>>(sql, 100) {
@Override
public Map<UUID, Integer> processResults(ResultSet set) throws SQLException {
Map<UUID, Integer> ofServer = new HashMap<>();
while (set.next()) {
UUID serverUUID = UUID.fromString(set.getString(UserInfoTable.SERVER_UUID));
int count = set.getInt("c");
ofServer.put(serverUUID, count);
}
return ofServer;
}
};
}
/**
* Count how many times commands have been used on a server.
*
* @param serverUUID Server UUID of the Plan server.
* @return Map: Lowercase used command - Count of use times.
*/
public static Query<Map<String, Integer>> commandUsageCounts(UUID serverUUID) {
String sql = SELECT + CommandUseTable.COMMAND + ", " + CommandUseTable.TIMES_USED + FROM + CommandUseTable.TABLE_NAME +
WHERE + CommandUseTable.SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID;
return new QueryStatement<Map<String, Integer>>(sql, 5000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
}
@Override
public Map<String, Integer> processResults(ResultSet set) throws SQLException {
Map<String, Integer> commandUse = new HashMap<>();
while (set.next()) {
String cmd = set.getString(CommandUseTable.COMMAND).toLowerCase();
int amountUsed = set.getInt(CommandUseTable.TIMES_USED);
commandUse.put(cmd, amountUsed);
}
return commandUse;
}
};
}
public static Query<Map<String, Integer>> networkGeolocationCounts() {
String subQuery1 = SELECT +
GeoInfoTable.USER_UUID + ", " +
GeoInfoTable.GEOLOCATION + ", " +
GeoInfoTable.LAST_USED +
FROM + GeoInfoTable.TABLE_NAME;
String subQuery2 = SELECT +
GeoInfoTable.USER_UUID + ", " +
"MAX(" + GeoInfoTable.LAST_USED + ") as m" +
FROM + GeoInfoTable.TABLE_NAME +
GROUP_BY + GeoInfoTable.USER_UUID;
String sql = SELECT + GeoInfoTable.GEOLOCATION + ", COUNT(1) as c FROM (" +
"(" + subQuery1 + ") AS q1" +
" INNER JOIN (" + subQuery2 + ") AS q2 ON q1.uuid = q2.uuid)" +
WHERE + GeoInfoTable.LAST_USED + "=m" +
GROUP_BY + GeoInfoTable.GEOLOCATION;
return new QueryAllStatement<Map<String, Integer>>(sql) {
@Override
public Map<String, Integer> processResults(ResultSet set) throws SQLException {
Map<String, Integer> geolocationCounts = new HashMap<>();
while (set.next()) {
geolocationCounts.put(set.getString(GeoInfoTable.GEOLOCATION), set.getInt("c"));
}
return geolocationCounts;
}
};
}
}

View File

@ -0,0 +1,173 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries.containers;
import com.djrapitops.plan.data.container.*;
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.containers.SupplierDataContainer;
import com.djrapitops.plan.data.store.keys.PerServerKeys;
import com.djrapitops.plan.data.store.keys.PlayerKeys;
import com.djrapitops.plan.data.store.mutators.PerServerMutator;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.queries.objects.*;
import java.util.*;
/**
* 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.
*
* @author Rsl1122
*/
public class AllPlayerContainersQuery implements Query<List<PlayerContainer>> {
/**
* Create PerServerContainers for each player.
*
* @param sessions Map: Server UUID - Map: Player UUID - List of Sessions
* @param allUserInfo Map: Server UUID - List of Users
* @param allPings Map: Player UUID - List of Ping data
* @return Map: Player UUID - PerServerContainer
*/
private Map<UUID, PerServerContainer> getPerServerData(
Map<UUID, Map<UUID, List<Session>>> sessions,
Map<UUID, List<UserInfo>> allUserInfo,
Map<UUID, List<Ping>> allPings
) {
Map<UUID, PerServerContainer> perServerContainers = new HashMap<>();
for (Map.Entry<UUID, List<UserInfo>> entry : allUserInfo.entrySet()) {
UUID serverUUID = entry.getKey();
List<UserInfo> serverUserInfo = entry.getValue();
for (UserInfo userInfo : serverUserInfo) {
UUID uuid = userInfo.getPlayerUuid();
if (uuid == null) {
continue;
}
PerServerContainer perServerContainer = perServerContainers.getOrDefault(uuid, new PerServerContainer());
DataContainer container = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer());
container.putRawData(PlayerKeys.REGISTERED, userInfo.getRegistered());
container.putRawData(PlayerKeys.BANNED, userInfo.isBanned());
container.putRawData(PlayerKeys.OPERATOR, userInfo.isOperator());
perServerContainer.put(serverUUID, container);
perServerContainers.put(uuid, perServerContainer);
}
}
for (Map.Entry<UUID, Map<UUID, List<Session>>> entry : sessions.entrySet()) {
UUID serverUUID = entry.getKey();
Map<UUID, List<Session>> serverUserSessions = entry.getValue();
for (Map.Entry<UUID, List<Session>> sessionEntry : serverUserSessions.entrySet()) {
UUID playerUUID = sessionEntry.getKey();
PerServerContainer perServerContainer = perServerContainers.getOrDefault(playerUUID, new PerServerContainer());
DataContainer container = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer());
List<Session> serverSessions = sessionEntry.getValue();
container.putRawData(PerServerKeys.SESSIONS, serverSessions);
container.putSupplier(PerServerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen());
container.putSupplier(PerServerKeys.WORLD_TIMES, () -> SessionsMutator.forContainer(container).toTotalWorldTimes());
container.putSupplier(PerServerKeys.PLAYER_DEATHS, () -> SessionsMutator.forContainer(container).toPlayerDeathList());
container.putSupplier(PerServerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(container).toPlayerKillList());
container.putSupplier(PerServerKeys.PLAYER_KILL_COUNT, () -> container.getUnsafe(PerServerKeys.PLAYER_KILLS).size());
container.putSupplier(PerServerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount());
container.putSupplier(PerServerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount());
container.putSupplier(PerServerKeys.MOB_DEATH_COUNT, () ->
container.getUnsafe(PerServerKeys.DEATH_COUNT) - container.getUnsafe(PerServerKeys.PLAYER_DEATH_COUNT)
);
perServerContainer.put(serverUUID, container);
perServerContainers.put(playerUUID, perServerContainer);
}
}
for (Map.Entry<UUID, List<Ping>> entry : allPings.entrySet()) {
UUID uuid = entry.getKey();
for (Ping ping : entry.getValue()) {
UUID serverUUID = ping.getServerUUID();
PerServerContainer perServerContainer = perServerContainers.getOrDefault(uuid, new PerServerContainer());
DataContainer container = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer());
if (!container.supports(PerServerKeys.PING)) {
container.putRawData(PerServerKeys.PING, new ArrayList<>());
}
container.getUnsafe(PerServerKeys.PING).add(ping);
perServerContainer.put(serverUUID, container);
perServerContainers.put(uuid, perServerContainer);
}
}
return perServerContainers;
}
@Override
public List<PlayerContainer> executeQuery(SQLDB db) {
List<PlayerContainer> containers = new ArrayList<>();
Collection<BaseUser> users = db.query(BaseUserQueries.fetchAllBaseUsers());
Map<UUID, List<GeoInfo>> geoInfo = db.query(GeoInfoQueries.fetchAllGeoInformation());
Map<UUID, List<Ping>> allPings = db.query(PingQueries.fetchAllPingData());
Map<UUID, List<Nickname>> allNicknames = db.query(NicknameQueries.fetchAllNicknameDataByPlayerUUIDs());
Map<UUID, Map<UUID, List<Session>>> sessions = db.query(SessionQueries.fetchAllSessionsWithoutKillOrWorldData());
Map<UUID, List<UserInfo>> allUserInfo = db.query(UserInfoQueries.fetchAllUserInformation());
Map<UUID, PerServerContainer> perServerInfo = getPerServerData(sessions, allUserInfo, allPings);
for (BaseUser baseUser : users) {
PlayerContainer container = new PlayerContainer();
UUID uuid = baseUser.getUuid();
container.putRawData(PlayerKeys.UUID, uuid);
container.putRawData(PlayerKeys.REGISTERED, baseUser.getRegistered());
container.putRawData(PlayerKeys.NAME, baseUser.getName());
container.putRawData(PlayerKeys.KICK_COUNT, baseUser.getTimesKicked());
container.putRawData(PlayerKeys.GEO_INFO, geoInfo.get(uuid));
container.putRawData(PlayerKeys.PING, allPings.get(uuid));
container.putRawData(PlayerKeys.NICKNAMES, allNicknames.get(uuid));
container.putRawData(PlayerKeys.PER_SERVER, perServerInfo.get(uuid));
container.putCachingSupplier(PlayerKeys.SESSIONS, () -> {
List<Session> playerSessions = PerServerMutator.forContainer(container).flatMapSessions();
container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add);
return playerSessions;
}
);
// Calculating getters
container.putSupplier(PlayerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen());
container.putSupplier(PlayerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount());
container.putSupplier(PlayerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount());
containers.add(container);
}
return containers;
}
}

View File

@ -0,0 +1,87 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries.containers;
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.db.access.Query;
import java.util.List;
import java.util.UUID;
/**
* Static method class for queries that return some kind of {@link com.djrapitops.plan.data.store.containers.DataContainer}.
*
* @author Rsl1122
*/
public class ContainerFetchQueries {
private ContainerFetchQueries() {
/* Static method class */
}
/**
* Used to get a NetworkContainer, some limitations apply to values returned by DataContainer keys.
*
* @return a new NetworkContainer.
* @see NetworkContainerQuery
*/
public static Query<NetworkContainer> fetchNetworkContainer() {
return new NetworkContainerQuery();
}
/**
* Used to get a ServerContainer, some limitations apply to values returned by DataContainer keys.
*
* @param serverUUID UUID of the Server.
* @return a new ServerContainer.
* @see ServerContainerQuery
*/
public static Query<ServerContainer> fetchServerContainer(UUID serverUUID) {
return new ServerContainerQuery(serverUUID);
}
/**
* Used to get a PlayerContainer of a specific player.
* <p>
* Blocking methods are not called until DataContainer getter methods are called.
*
* @param playerUUID UUID of the player.
* @return a new PlayerContainer.
* @see PlayerContainerQuery
*/
public static Query<PlayerContainer> fetchPlayerContainer(UUID playerUUID) {
return new PlayerContainerQuery(playerUUID);
}
/**
* 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.
*/
public static Query<List<PlayerContainer>> fetchAllPlayerContainers() {
return new AllPlayerContainersQuery();
}
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access.queries.containers;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.data.store.containers.ServerContainer;
import com.djrapitops.plan.data.store.keys.NetworkKeys;
import com.djrapitops.plan.data.store.keys.ServerKeys;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.db.access.queries.objects.TPSQueries;
import com.djrapitops.plan.system.info.server.Server;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* 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.
*
* @author Rsl1122
*/
public class NetworkContainerQuery implements Query<NetworkContainer> {
private static Query<ServerContainer> getProxyServerContainer() {
return db -> {
Optional<Server> proxyInformation = db.query(ServerQueries.fetchProxyServerInformation());
if (!proxyInformation.isPresent()) {
return new ServerContainer();
}
UUID proxyUUID = proxyInformation.get().getUuid();
ServerContainer container = db.query(ContainerFetchQueries.fetchServerContainer(proxyUUID));
container.putCachingSupplier(ServerKeys.PLAYERS, () -> db.query(ContainerFetchQueries.fetchAllPlayerContainers()));
container.putCachingSupplier(ServerKeys.TPS, () -> db.query(TPSQueries.fetchTPSDataOfServer(proxyUUID)));
container.putSupplier(ServerKeys.WORLD_TIMES, null); // Additional Session information not supported
container.putSupplier(ServerKeys.PLAYER_KILLS, null);
container.putSupplier(ServerKeys.PLAYER_KILL_COUNT, null);
return container;
};
}
@Override
public NetworkContainer executeQuery(SQLDB db) {
ServerContainer bungeeContainer = db.query(getProxyServerContainer());
NetworkContainer networkContainer = db.getNetworkContainerFactory().forBungeeContainer(bungeeContainer);
networkContainer.putCachingSupplier(NetworkKeys.BUKKIT_SERVERS, () ->
db.query(ServerQueries.fetchPlanServerInformation()).values()
.stream().filter(Server::isNotProxy).collect(Collectors.toSet())
);
return networkContainer;
}
}

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