Merge pull request #1683 from plan-player-analytics/version-5.2

Version 5.2
This commit is contained in:
Risto Lahtela 2021-01-05 12:03:47 +02:00 committed by GitHub
commit 103649dcf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
147 changed files with 2005 additions and 1272 deletions

View File

@ -3,7 +3,8 @@ plugins {
}
dependencies {
compileOnly group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
compileOnly "org.apache.commons:commons-text:$commonsTextVersion"
testCompile "org.apache.commons:commons-text:$commonsTextVersion"
compileOnly "com.google.code.gson:gson:$gsonVersion"
}

View File

@ -22,8 +22,9 @@ import com.djrapitops.plan.extension.icon.Icon;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* Object for giving Plan table data.
@ -186,7 +187,17 @@ public final class Table {
return this; // Ignore row when all values are null or no values are present.
}
building.rows.add(Arrays.copyOf(values, 5));
Object[] row = new Object[5];
for (int i = 0; i < Math.min(values.length, 5); i++) {
Object value = values[i];
if (value instanceof Optional) {
value = ((Optional<?>) value).map(Objects::toString).orElse("-");
}
row[i] = value;
}
building.rows.add(row);
return this;
}

View File

@ -0,0 +1,61 @@
/*
* 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.extension.table;
import com.djrapitops.plan.extension.icon.Icon;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
class TableTest {
@Test
void tableWithVaryingRowLengthsHasNoErrors() {
Table.Factory table = Table.builder()
.columnOne("", Icon.called("").build())
.columnTwo("", Icon.called("").build())
.columnThree("", Icon.called("").build())
.columnFour("", Icon.called("").build())
.columnFive("", Icon.called("").build());
table.addRow();
table.addRow("a");
table.addRow("a", "b");
table.addRow("a");
table.addRow("a", "b", "c", "d", "e", "f");
table.addRow("a", "b", "c", "d");
List<Object[]> expected = Arrays.asList(
new Object[]{"a", null, null, null, null},
new Object[]{"a", "b", null, null, null},
new Object[]{"a", null, null, null, null},
new Object[]{"a", "b", "c", "d", "e"},
new Object[]{"a", "b", "c", "d", null}
);
List<Object[]> result = table.build().getRows();
for (int i = 0; i < expected.size(); i++) {
assertArrayEquals(expected.get(i), result.get(i));
}
assertEquals(expected.size(), result.size());
}
}

View File

@ -33,10 +33,10 @@ allprojects {
wrapper.gradleVersion = "6.2.1"
group "com.djrapitops"
version "5.1-SNAPSHOT"
version "5.2-SNAPSHOT"
ext.majorVersion = '5'
ext.minorVersion = '1'
ext.minorVersion = '2'
ext.buildVersion = buildVersion
ext.fullVersion = project.ext.majorVersion + '.' + project.ext.minorVersion + ' build ' + project.ext.buildVersion
@ -84,6 +84,7 @@ subprojects {
ext.caffeineVersion = "2.8.0"
ext.h2Version = "1.4.199"
ext.mysqlVersion = "8.0.22"
ext.sqliteVersion = "3.34.0"
ext.hikariVersion = "3.4.5"
ext.slf4jVersion = "1.7.30"
ext.geoIpVersion = "2.15.0"
@ -140,7 +141,7 @@ subprojects {
testCompile "com.jayway.awaitility:awaitility:1.7.0" // Awaitility (Concurrent wait conditions)
// Testing dependencies required by Plan
testCompile "org.xerial:sqlite-jdbc:3.34.0" // SQLite
testCompile "org.xerial:sqlite-jdbc:$sqliteVersion" // SQLite
testCompile "mysql:mysql-connector-java:$mysqlVersion" // MySQL
}

View File

@ -1,154 +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;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.gathering.ShutdownHook;
import com.djrapitops.plan.gathering.timed.BukkitPingCounter;
import com.djrapitops.plan.gathering.timed.ServerTPSCounter;
import com.djrapitops.plan.gathering.timed.SystemUsageBuffer;
import com.djrapitops.plan.gathering.timed.TPSCounter;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.upkeep.ConfigStoreTask;
import com.djrapitops.plan.storage.upkeep.DBCleanTask;
import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.RunnableFactory;
import org.bukkit.Bukkit;
import org.bukkit.World;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
/**
* TaskSystem responsible for registering tasks for Bukkit.
*
* @author Rsl1122
*/
@Singleton
public class BukkitTaskSystem extends TaskSystem {
private final Plan plugin;
private final PlanConfig config;
private final ShutdownHook shutdownHook;
private final JSONCache.CleanTask jsonCacheCleanTask;
private final LogsFolderCleanTask logsFolderCleanTask;
private final BukkitPingCounter pingCounter;
private final ConfigStoreTask configStoreTask;
private final DBCleanTask dbCleanTask;
private final ExtensionServerMethodCallerTask extensionServerMethodCallerTask;
private final TPSCounter tpsCounter;
private final SystemUsageBuffer.RamAndCpuTask ramAndCpuTask;
private final SystemUsageBuffer.DiskTask diskTask;
@Inject
public BukkitTaskSystem(
Plan plugin,
PlanConfig config,
ShutdownHook shutdownHook,
RunnableFactory runnableFactory,
ServerTPSCounter<World> tpsCounter,
BukkitPingCounter pingCounter,
ExtensionServerMethodCallerTask extensionServerMethodCallerTask,
LogsFolderCleanTask logsFolderCleanTask,
ConfigStoreTask configStoreTask,
DBCleanTask dbCleanTask,
JSONCache.CleanTask jsonCacheCleanTask,
SystemUsageBuffer.RamAndCpuTask ramAndCpuTask,
SystemUsageBuffer.DiskTask diskTask
) {
super(runnableFactory);
this.plugin = plugin;
this.config = config;
this.shutdownHook = shutdownHook;
this.jsonCacheCleanTask = jsonCacheCleanTask;
this.tpsCounter = tpsCounter;
this.pingCounter = pingCounter;
this.extensionServerMethodCallerTask = extensionServerMethodCallerTask;
this.logsFolderCleanTask = logsFolderCleanTask;
this.configStoreTask = configStoreTask;
this.dbCleanTask = dbCleanTask;
this.ramAndCpuTask = ramAndCpuTask;
this.diskTask = diskTask;
}
@Override
public void enable() {
registerTPSCounter();
registerPingCounter();
registerExtensionDataGatheringTask();
registerUpkeepTasks();
shutdownHook.register();
}
private void registerUpkeepTasks() {
// +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(configStoreTask).runTaskLaterAsynchronously(storeDelay);
registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS));
registerTask(dbCleanTask).runTaskTimerAsynchronously(
TimeAmount.toTicks(20, TimeUnit.SECONDS),
TimeAmount.toTicks(config.get(TimeSettings.CLEAN_DATABASE_PERIOD), TimeUnit.MILLISECONDS)
);
long minute = TimeAmount.toTicks(1, TimeUnit.MINUTES);
registerTask(jsonCacheCleanTask).runTaskTimerAsynchronously(minute, minute);
}
private void registerTPSCounter() {
long halfSecondTicks = TimeAmount.toTicks(500L, TimeUnit.MILLISECONDS);
long secondTicks = TimeAmount.toTicks(1L, TimeUnit.SECONDS);
long minuteTicks = TimeAmount.toTicks(1L, TimeUnit.MINUTES);
registerTask(tpsCounter).runTaskTimer(minuteTicks, secondTicks);
registerTask(ramAndCpuTask).runTaskTimerAsynchronously(minuteTicks - halfSecondTicks, secondTicks);
registerTask(diskTask).runTaskTimerAsynchronously(50L * secondTicks, minuteTicks);
}
private void registerPingCounter() {
try {
Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.isTrue(DataGatheringSettings.PING)) {
plugin.registerListener(pingCounter);
long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS);
registerTask(pingCounter).runTaskTimer(startDelay, 40L);
}
} catch (ExceptionInInitializerError | NoClassDefFoundError ignore) {
// Running CraftBukkit
}
}
private void registerExtensionDataGatheringTask() {
long extensionRefreshPeriod = TimeAmount.toTicks(config.get(TimeSettings.EXTENSION_DATA_REFRESH_PERIOD), TimeUnit.MILLISECONDS);
registerTask(extensionServerMethodCallerTask).runTaskTimerAsynchronously(
TimeAmount.toTicks(30, TimeUnit.SECONDS), extensionRefreshPeriod
);
}
@Override
public void disable() {
super.disable();
Bukkit.getScheduler().cancelTasks(plugin);
}
}

View File

@ -120,16 +120,19 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
*/
@Override
public void onDisable() {
if (serverShutdownSave != null) {
serverShutdownSave.performSave();
}
if (system != null) {
system.disable();
}
if (serverShutdownSave != null) serverShutdownSave.performSave();
cancelAllTasks();
if (system != null) system.disable();
logger.info(locale != null ? locale.getString(PluginLang.DISABLED) : PluginLang.DISABLED.getDefault());
}
@Override
public void cancelAllTasks() {
runnableFactory.cancelAllKnownTasks();
Bukkit.getScheduler().cancelTasks(this);
}
@Override
public String getVersion() {
return getDescription().getVersion();

View File

@ -26,6 +26,7 @@ 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;
import com.djrapitops.plan.modules.bukkit.BukkitTaskModule;
import dagger.BindsInstance;
import dagger.Component;
@ -45,7 +46,8 @@ import javax.inject.Singleton;
ServerCommandModule.class,
BukkitServerPropertiesModule.class,
BukkitSuperClassBindingModule.class
BukkitSuperClassBindingModule.class,
BukkitTaskModule.class
})
public interface PlanBukkitComponent {

View File

@ -52,7 +52,7 @@ class BukkitPartBuilder implements MessageBuilder {
try {
nextPart.part.appendLegacy(text);
} catch (NoSuchMethodError oldVersion) { // not supported in 1.8
nextPart.part.append(ChatColor.translateAlternateColorCodes('§', text));
nextPart.part.append(ChatColor.translateAlternateColorCodes('\u00a7', text));
}
return nextPart;
}

View File

@ -1,45 +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.gathering.importing;
import com.djrapitops.plan.gathering.importing.importers.OfflinePlayerImporter;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* ImportSystem implementation for Bukkit.
*
* @author Rsl1122
*/
@Singleton
public class BukkitImportSystem extends ImportSystem {
private final OfflinePlayerImporter offlinePlayerImporter;
@Inject
public BukkitImportSystem(
OfflinePlayerImporter offlinePlayerImporter
) {
this.offlinePlayerImporter = offlinePlayerImporter;
}
@Override
void registerImporters() {
registerImporter(offlinePlayerImporter);
}
}

View File

@ -23,9 +23,12 @@
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.PingStoreTransaction;
@ -60,7 +63,7 @@ import java.util.logging.Logger;
* @author games647
*/
@Singleton
public class BukkitPingCounter extends AbsRunnable implements Listener {
public class BukkitPingCounter extends TaskSystem.Task implements Listener {
//the server is pinging the client every 40 Ticks (2 sec) - so check it then
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178
@ -70,13 +73,23 @@ public class BukkitPingCounter extends AbsRunnable implements Listener {
private static MethodHandle PING_FIELD;
private static MethodHandle GET_HANDLE_METHOD;
private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final Plan plugin;
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
@Inject
public BukkitPingCounter(
Plan plugin,
PlanConfig config,
DBSystem dbSystem,
ServerInfo serverInfo,
RunnableFactory runnableFactory
) {
this.plugin = plugin;
BukkitPingCounter.loadPingMethodDetails();
this.config = config;
this.dbSystem = dbSystem;
@ -85,12 +98,6 @@ public class BukkitPingCounter extends AbsRunnable implements Listener {
playerHistory = new HashMap<>();
}
private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
private static void loadPingMethodDetails() {
PING_METHOD_AVAILABLE = isPingMethodAvailable();
@ -135,6 +142,17 @@ public class BukkitPingCounter extends AbsRunnable implements Listener {
}
}
@Override
public void register(RunnableFactory runnableFactory) {
Long startDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
if (startDelay < TimeUnit.HOURS.toMillis(1L) && config.isTrue(DataGatheringSettings.PING)) {
plugin.registerListener(this);
long delay = TimeAmount.toTicks(startDelay, TimeUnit.MILLISECONDS);
long period = 40L;
runnableFactory.create(null, this).runTaskTimer(delay, period);
}
}
@Override
public void run() {
long time = System.currentTimeMillis();

View File

@ -18,8 +18,11 @@ package com.djrapitops.plan.modules.bukkit;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.gathering.importing.importers.Importer;
import com.djrapitops.plan.gathering.importing.importers.OfflinePlayerImporter;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoSet;
/**
* Dagger module for binding Plan instance.
@ -31,4 +34,8 @@ public interface BukkitPlanModule {
@Binds
PlanPlugin bindPlanPlugin(Plan plugin);
@Binds
@IntoSet
Importer bindOfflinePlayerImporter(OfflinePlayerImporter importer);
}

View File

@ -17,13 +17,9 @@
package com.djrapitops.plan.modules.bukkit;
import com.djrapitops.plan.BukkitServerShutdownSave;
import com.djrapitops.plan.BukkitTaskSystem;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.gathering.BukkitSensor;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.ServerShutdownSave;
import com.djrapitops.plan.gathering.importing.BukkitImportSystem;
import com.djrapitops.plan.gathering.importing.ImportSystem;
import com.djrapitops.plan.gathering.listeners.BukkitListenerSystem;
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
import com.djrapitops.plan.identification.ServerInfo;
@ -53,15 +49,9 @@ public interface BukkitSuperClassBindingModule {
@Binds
ConfigSystem bindConfigSystem(BukkitConfigSystem configSystem);
@Binds
TaskSystem bindTaskSystem(BukkitTaskSystem taskSystem);
@Binds
ListenerSystem bindListenerSystem(BukkitListenerSystem listenerSystem);
@Binds
ImportSystem bindImportSystem(BukkitImportSystem importSystem);
@Binds
ServerShutdownSave bindServerShutdownSave(BukkitServerShutdownSave shutdownSave);

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.modules.bukkit;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.ShutdownHook;
import com.djrapitops.plan.gathering.timed.BukkitPingCounter;
import com.djrapitops.plan.gathering.timed.ServerTPSCounter;
import com.djrapitops.plan.gathering.timed.SystemUsageBuffer;
import com.djrapitops.plan.settings.upkeep.ConfigStoreTask;
import com.djrapitops.plan.storage.upkeep.DBCleanTask;
import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoSet;
import org.bukkit.World;
@Module
public interface BukkitTaskModule {
@Binds
@IntoSet
TaskSystem.Task bindTPSCounter(ServerTPSCounter<World> tpsCounter);
@Binds
@IntoSet
TaskSystem.Task bindPingCounter(BukkitPingCounter pingCounter);
@Binds
@IntoSet
TaskSystem.Task bindExtensionServerDataUpdater(ExtensionServerDataUpdater extensionServerDataUpdater);
@Binds
@IntoSet
TaskSystem.Task bindLogCleanTask(LogsFolderCleanTask logsFolderCleanTask);
@Binds
@IntoSet
TaskSystem.Task bindConfigStoreTask(ConfigStoreTask configStoreTask);
@Binds
@IntoSet
TaskSystem.Task bindDBCleanTask(DBCleanTask cleanTask);
@Binds
@IntoSet
TaskSystem.Task bindJSONCacheCleanTask(JSONCache.CleanTask cleanTask);
@Binds
@IntoSet
TaskSystem.Task bindRamAndCpuTask(SystemUsageBuffer.RamAndCpuTask ramAndCpuTask);
@Binds
@IntoSet
TaskSystem.Task bindDiskTask(SystemUsageBuffer.DiskTask diskTask);
@Binds
@IntoSet
TaskSystem.Task bindShutdownHookRegistration(ShutdownHook.Registrar registrar);
}

View File

@ -16,19 +16,16 @@
*/
package com.djrapitops.plan;
import com.djrapitops.plan.exceptions.EnableException;
import com.djrapitops.plan.settings.ConfigSettingKeyTest;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.settings.config.paths.key.Setting;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import utilities.RandomData;
import utilities.TestSettings;
import utilities.mocks.BukkitMockComponent;
import java.nio.file.Path;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -51,7 +48,7 @@ class BukkitSystemTest {
}
@Test
void bukkitSystemEnables() throws EnableException {
void bukkitSystemEnables() {
try {
system.enable();
assertTrue(system.isEnabled());
@ -61,13 +58,12 @@ class BukkitSystemTest {
}
@Test
void bukkitSystemHasDefaultConfigValuesAfterEnable() throws EnableException, IllegalAccessException {
void bukkitSystemHasDefaultConfigValuesAfterEnable() throws IllegalAccessException {
try {
system.enable();
PlanConfig config = system.getConfigSystem().getConfig();
Collection<Setting> serverSettings = ConfigSettingKeyTest.getServerSettings();
ConfigSettingKeyTest.assertValidDefaultValuesForAllSettings(config, serverSettings);
TestSettings.assertValidDefaultValuesForAllSettings(config, TestSettings.getServerSettings());
} finally {
system.disable();
}

View File

@ -1,129 +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;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.gathering.timed.BungeePingCounter;
import com.djrapitops.plan.gathering.timed.ProxyTPSCounter;
import com.djrapitops.plan.gathering.timed.SystemUsageBuffer;
import com.djrapitops.plan.gathering.timed.TPSCounter;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.upkeep.NetworkConfigStoreTask;
import com.djrapitops.plan.storage.upkeep.DBCleanTask;
import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask;
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;
/**
* TaskSystem responsible for registering tasks for Bungee.
*
* @author Rsl1122
*/
@Singleton
public class BungeeTaskSystem extends TaskSystem {
private final PlanBungee plugin;
private final PlanConfig config;
private final TPSCounter tpsCounter;
private final BungeePingCounter pingCounter;
private final LogsFolderCleanTask logsFolderCleanTask;
private final NetworkConfigStoreTask networkConfigStoreTask;
private final DBCleanTask dbCleanTask;
private final JSONCache.CleanTask jsonCacheCleanTask;
private final ExtensionServerMethodCallerTask extensionServerMethodCallerTask;
private final SystemUsageBuffer.RamAndCpuTask ramAndCpuTask;
private final SystemUsageBuffer.DiskTask diskTask;
@Inject
public BungeeTaskSystem(
PlanBungee plugin,
PlanConfig config,
RunnableFactory runnableFactory,
ProxyTPSCounter tpsCounter,
BungeePingCounter pingCounter,
LogsFolderCleanTask logsFolderCleanTask,
NetworkConfigStoreTask networkConfigStoreTask,
DBCleanTask dbCleanTask,
JSONCache.CleanTask jsonCacheCleanTask,
ExtensionServerMethodCallerTask extensionServerMethodCallerTask,
SystemUsageBuffer.RamAndCpuTask ramAndCpuTask,
SystemUsageBuffer.DiskTask diskTask
) {
super(runnableFactory);
this.plugin = plugin;
this.config = config;
this.tpsCounter = tpsCounter;
this.pingCounter = pingCounter;
this.logsFolderCleanTask = logsFolderCleanTask;
this.networkConfigStoreTask = networkConfigStoreTask;
this.dbCleanTask = dbCleanTask;
this.jsonCacheCleanTask = jsonCacheCleanTask;
this.extensionServerMethodCallerTask = extensionServerMethodCallerTask;
this.ramAndCpuTask = ramAndCpuTask;
this.diskTask = diskTask;
}
@Override
public void enable() {
registerTasks();
}
private void registerTPSCounter() {
long halfSecondTicks = TimeAmount.toTicks(500L, TimeUnit.MILLISECONDS);
long secondTicks = TimeAmount.toTicks(1L, TimeUnit.SECONDS);
long minuteTicks = TimeAmount.toTicks(1L, TimeUnit.MINUTES);
registerTask(tpsCounter).runTaskTimer(minuteTicks, secondTicks);
registerTask(ramAndCpuTask).runTaskTimerAsynchronously(minuteTicks - halfSecondTicks, secondTicks);
registerTask(diskTask).runTaskTimerAsynchronously(50L * secondTicks, minuteTicks);
}
private void registerTasks() {
registerTPSCounter();
registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS));
Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.isTrue(DataGatheringSettings.PING)) {
plugin.registerListener(pingCounter);
long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS);
registerTask(pingCounter).runTaskTimer(startDelay, 40L);
}
// +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(networkConfigStoreTask).runTaskLaterAsynchronously(storeDelay);
registerTask(dbCleanTask).runTaskTimerAsynchronously(
TimeAmount.toTicks(20, TimeUnit.SECONDS),
TimeAmount.toTicks(config.get(TimeSettings.CLEAN_DATABASE_PERIOD), TimeUnit.MILLISECONDS)
);
long minute = TimeAmount.toTicks(1, TimeUnit.MINUTES);
registerTask(jsonCacheCleanTask).runTaskTimerAsynchronously(minute, minute);
long extensionRefreshPeriod = TimeAmount.toTicks(config.get(TimeSettings.EXTENSION_DATA_REFRESH_PERIOD), TimeUnit.MILLISECONDS);
registerTask(extensionServerMethodCallerTask).runTaskTimerAsynchronously(
TimeAmount.toTicks(30, TimeUnit.SECONDS), extensionRefreshPeriod
);
}
}

View File

@ -74,6 +74,7 @@ public class PlanBungee extends BungeePlugin implements PlanPlugin {
@Override
public void onDisable() {
cancelAllTasks();
if (system != null) system.disable();
logger.info(locale.getString(PluginLang.DISABLED));

View File

@ -21,10 +21,7 @@ import com.djrapitops.plan.modules.APFModule;
import com.djrapitops.plan.modules.PlaceholderModule;
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;
import com.djrapitops.plan.modules.bungee.BungeeSuperClassBindingModule;
import com.djrapitops.plan.modules.bungee.*;
import dagger.BindsInstance;
import dagger.Component;
@ -45,7 +42,8 @@ import javax.inject.Singleton;
ProxySuperClassBindingModule.class,
BungeeSuperClassBindingModule.class,
BungeeServerPropertiesModule.class
BungeeServerPropertiesModule.class,
BungeeTaskModule.class
})
public interface PlanBungeeComponent {

View File

@ -23,9 +23,12 @@
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.PingStoreTransaction;
@ -50,10 +53,11 @@ import java.util.concurrent.TimeUnit;
* @author BrainStone
*/
@Singleton
public class BungeePingCounter extends AbsRunnable implements Listener {
public class BungeePingCounter extends TaskSystem.Task implements Listener {
private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final PlanBungee plugin;
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
@ -61,11 +65,13 @@ public class BungeePingCounter extends AbsRunnable implements Listener {
@Inject
public BungeePingCounter(
PlanBungee plugin,
PlanConfig config,
DBSystem dbSystem,
ServerInfo serverInfo,
RunnableFactory runnableFactory
) {
this.plugin = plugin;
this.config = config;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
@ -102,6 +108,18 @@ public class BungeePingCounter extends AbsRunnable implements Listener {
}
}
@Override
public void register(RunnableFactory runnableFactory) {
Long startDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
if (startDelay < TimeUnit.HOURS.toMillis(1L) && config.isTrue(DataGatheringSettings.PING)) {
plugin.registerListener(this);
long delay = TimeAmount.toTicks(startDelay, TimeUnit.MILLISECONDS);
long period = 40L;
runnableFactory.create(null, this).runTaskTimerAsynchronously(delay, period);
}
}
public void addPlayer(ProxiedPlayer player) {
playerHistory.put(player.getUniqueId(), new ArrayList<>());
}

View File

@ -67,6 +67,8 @@ public class BungeeServerInfo extends ServerInfo {
this.server = fromFile.load(null).orElseGet(() -> fromDatabase.load(null)
.orElseGet(this::registerServer));
this.server.setProxy(true); // Ensure isProxy if loaded from file
processing.submitNonCritical(this::updateStorage);
}
@ -112,6 +114,6 @@ public class BungeeServerInfo extends ServerInfo {
private Server createServerObject() {
UUID serverUUID = generateNewUUID();
String accessAddress = addresses.getAccessAddress().orElseThrow(() -> new EnableException("Velocity can not have '0.0.0.0' or '' as an address. Set up 'Server.IP' setting."));
return new Server(-1, serverUUID, "BungeeCord", accessAddress);
return new Server(-1, serverUUID, "BungeeCord", accessAddress, true);
}
}

View File

@ -16,8 +16,6 @@
*/
package com.djrapitops.plan.modules.bungee;
import com.djrapitops.plan.BungeeTaskSystem;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.gathering.BungeeSensor;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.listeners.BungeeListenerSystem;
@ -38,9 +36,6 @@ public interface BungeeSuperClassBindingModule {
@Binds
ServerInfo bindServerInfo(BungeeServerInfo serverInfo);
@Binds
TaskSystem bindTaskSystem(BungeeTaskSystem taskSystem);
@Binds
ListenerSystem bindListenerSystem(BungeeListenerSystem listenerSystem);

View File

@ -0,0 +1,71 @@
/*
* 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.modules.bungee;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.timed.BungeePingCounter;
import com.djrapitops.plan.gathering.timed.ProxyTPSCounter;
import com.djrapitops.plan.gathering.timed.SystemUsageBuffer;
import com.djrapitops.plan.settings.upkeep.NetworkConfigStoreTask;
import com.djrapitops.plan.storage.upkeep.DBCleanTask;
import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoSet;
@Module
public interface BungeeTaskModule {
@Binds
@IntoSet
TaskSystem.Task bindTPSCounter(ProxyTPSCounter counter);
@Binds
@IntoSet
TaskSystem.Task bindPingCounter(BungeePingCounter counter);
@Binds
@IntoSet
TaskSystem.Task bindNetworkConfigStoreTask(NetworkConfigStoreTask configStoreTask);
@Binds
@IntoSet
TaskSystem.Task bindExtensionServerDataUpdater(ExtensionServerDataUpdater extensionServerDataUpdater);
@Binds
@IntoSet
TaskSystem.Task bindLogCleanTask(LogsFolderCleanTask logsFolderCleanTask);
@Binds
@IntoSet
TaskSystem.Task bindDBCleanTask(DBCleanTask cleanTask);
@Binds
@IntoSet
TaskSystem.Task bindJSONCacheCleanTask(JSONCache.CleanTask cleanTask);
@Binds
@IntoSet
TaskSystem.Task bindRamAndCpuTask(SystemUsageBuffer.RamAndCpuTask ramAndCpuTask);
@Binds
@IntoSet
TaskSystem.Task bindDiskTask(SystemUsageBuffer.DiskTask diskTask);
}

View File

@ -10,6 +10,7 @@ dependencies {
compile "com.github.ben-manes.caffeine:caffeine:$caffeineVersion"
compile "com.h2database:h2:$h2Version"
compile "mysql:mysql-connector-java:$mysqlVersion"
compile "org.xerial:sqlite-jdbc:$sqliteVersion"
compile "com.zaxxer:HikariCP:$hikariVersion"
compile "org.slf4j:slf4j-nop:$slf4jVersion"
compile "org.slf4j:slf4j-api:$slf4jVersion"

View File

@ -0,0 +1,80 @@
/*
* 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.storage.database.queries.Query;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Service for sourcing, mapping and consuming data.
* <p>
* The service is in charge of two data flows:
* - push, given to consumers
* - pull, obtained from sources
* <p>
* The mappers facilitate a one way type transformation if needed.
* <p>
* The interface is very abstract about how data is obtained,
* but here are my general ideas of where the abstraction is coming from.
* - push cause one or multiple consumers to modify stored data
* - pull cause one or multiple suppliers to fetch stored data
* - mappers are stateless type transformers in memory
* <p>
* Example use-case:
* - PlayerJoinEvent is mapped to a generic event
* - that generic event is then consumed and mapped until the data is in a database.
* <p>
* - Some kind of data is wanted to place on a web page
* - It is requested and the suppliers and mappers give the wanted type of data.
* <p>
* Further research needed:
* - Can this limited type system represent types of data that need parameters
* (such as differentiate between two servers data)
*/
public interface DataService {
<M> DataService push(Class<M> type, M data);
<S> Optional<S> pull(Class<S> type);
<S, P> Optional<S> pull(Class<S> type, P parameter);
<A, B> B mapTo(Class<B> toType, A from);
default <S, P> Optional<S> pull(Class<S> type, Class<P> parameterType) {
return pull(type, () -> pull(parameterType).orElse(null));
}
default <S, P> Optional<S> pull(Class<S> type, Supplier<P> parameter) {
return pull(type, parameter.get());
}
<A, B> DataService registerMapper(Class<A> typeA, Class<B> typeB, Function<A, B> mapper);
<M> DataService registerConsumer(Class<M> type, Consumer<M> consumer);
<S> DataService registerSupplier(Class<S> type, Supplier<S> supplier);
<P, S> DataService registerSupplier(Class<S> type, Class<P> parameterType, Function<P, S> supplierWithParameter);
<P, S> DataService registerDBSupplier(Class<S> type, Class<P> parameterType, Function<P, Query<S>> supplierWithParameter);
}

View File

@ -0,0 +1,194 @@
/*
* 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.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.Query;
import dagger.Lazy;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@Singleton
public class DataSvc implements DataService {
private final MultiHashMap<Class, Mapper> mappers;
private final MultiHashMap<Class, Mapper> mappersReverse;
private final Map<Class, Supplier> suppliers;
private final Map<ClassPair, Function> suppliersWithParameter;
private final MultiHashMap<Class, Consumer> consumers;
private final Lazy<DBSystem> dbSystem;
@Inject
public DataSvc(
Lazy<DBSystem> dbSystem
) {
this.dbSystem = dbSystem;
mappers = new MultiHashMap<>();
mappersReverse = new MultiHashMap<>();
suppliers = new ConcurrentHashMap<>();
suppliersWithParameter = new ConcurrentHashMap<>();
consumers = new MultiHashMap<>();
}
@Override
public <A> DataService push(Class<A> type, A data) {
if (data == null) return this;
List<Mapper> mappers = this.mappers.get(type);
for (Mapper mapper : mappers) {
push(mapper.typeB, mapper.func.apply(data));
}
List<Consumer> consumers = this.consumers.get(type);
for (Consumer<A> consumer : consumers) {
consumer.accept(data);
}
if (mappers.isEmpty() && consumers.isEmpty()) {
System.out.println("WARN: Nothing consumed " + type);
}
return this;
}
@Override
public <A> Optional<A> pull(Class<A> type) {
Supplier present = this.suppliers.get(type);
if (present != null) return Optional.ofNullable((A) present.get());
List<Mapper> mappers = this.mappersReverse.get(type);
for (Mapper mapper : mappers) {
Optional found = pull(mapper.typeA)
.map(data -> mapper.func.apply(data));
if (found.isPresent()) return found;
}
System.out.println("WARN: Nothing supplied " + type);
return Optional.empty();
}
@Override
public <A, B> B mapTo(Class<B> toType, A from) {
List<Mapper> mappers = this.mappers.get(from.getClass());
for (Mapper mapper : mappers) {
if (mapper.typeB.equals(toType)) {
return toType.cast(mapper.func.apply(from));
}
}
// TODO Figure out type mapping resolution when it needs more than one mapping
System.out.println("WARN: No mapper for " + from.getClass() + " -> " + toType);
return null;
}
@Override
public <A, B> DataService registerMapper(Class<A> typeA, Class<B> typeB, Function<A, B> mapper) {
Mapper<A, B> asWrapper = new Mapper<>(typeA, typeB, mapper);
// TODO Prevent two mappers for same 2 types with a data structure
mappers.putOne(typeA, asWrapper);
mappersReverse.putOne(typeB, asWrapper);
return this;
}
@Override
public <A> DataService registerConsumer(Class<A> type, Consumer<A> consumer) {
consumers.putOne(type, consumer);
return this;
}
@Override
public <A> DataService registerSupplier(Class<A> type, Supplier<A> supplier) {
suppliers.put(type, supplier);
return this;
}
@Override
public <S, P> Optional<S> pull(Class<S> type, P parameter) {
if (parameter == null) return Optional.empty();
Function<P, S> function = suppliersWithParameter.get(new ClassPair<>(type, parameter.getClass()));
return function != null ? Optional.of(function.apply(parameter)) : Optional.empty();
}
@Override
public <P, S> DataService registerSupplier(Class<S> type, Class<P> parameterType, Function<P, S> supplierWithParameter) {
suppliersWithParameter.put(new ClassPair<>(type, parameterType), supplierWithParameter);
return this;
}
@Override
public <P, S> DataService registerDBSupplier(Class<S> type, Class<P> parameterType, Function<P, Query<S>> queryVisitor) {
return registerSupplier(type, parameterType, parameter -> dbSystem.get().getDatabase().query(queryVisitor.apply(parameter)));
}
private static class ClassPair<A, B> {
final Class<A> a;
final Class<B> b;
public ClassPair(Class<A> a, Class<B> b) {
this.a = a;
this.b = b;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassPair<?, ?> classPair = (ClassPair<?, ?>) o;
return a.equals(classPair.a) &&
b.equals(classPair.b);
}
@Override
public int hashCode() {
return Objects.hash(a, b);
}
}
private static class KeyValuePair<K, V> {
final K key;
final V value;
public KeyValuePair(K key, V value) {
this.key = key;
this.value = value;
}
}
private static class MultiHashMap<A, B> extends ConcurrentHashMap<A, List<B>> {
void putOne(A key, B value) {
List<B> values = getOrDefault(key, new ArrayList<>());
values.add(value);
put(key, values);
}
}
private static class Mapper<A, B> {
Class<A> typeA;
Class<B> typeB;
Function<A, B> func;
public Mapper(Class<A> typeA, Class<B> typeB, Function<A, B> func) {
this.typeA = typeA;
this.typeB = typeB;
this.func = func;
}
}
}

View File

@ -47,4 +47,8 @@ public interface PlanPlugin extends IPlugin {
}
void registerCommand(Subcommand command);
default void cancelAllTasks() {
getRunnableFactory().cancelAllKnownTasks();
}
}

View File

@ -17,31 +17,36 @@
package com.djrapitops.plan;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.PluginRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Set;
/**
* TaskSystem that registers tasks that were previously registered inside Plugin classes.
*
* Subclasses register actual tasks.
* TaskSystem that registers tasks for the plugin.
* See platform specific [Platform]TaskModule classes for what Tasks are registered.
*
* @author Rsl1122
*/
public abstract class TaskSystem implements SubSystem {
@Singleton
public class TaskSystem implements SubSystem {
protected final RunnableFactory runnableFactory;
private final RunnableFactory runnableFactory;
private final Set<Task> tasks;
protected TaskSystem(RunnableFactory runnableFactory) {
@Inject
public TaskSystem(
RunnableFactory runnableFactory,
Set<Task> tasks
) {
this.runnableFactory = runnableFactory;
this.tasks = tasks;
}
protected PluginRunnable registerTask(AbsRunnable runnable) {
String taskName = runnable.getClass().getSimpleName();
return registerTask(taskName, runnable);
}
public PluginRunnable registerTask(String name, AbsRunnable runnable) {
return runnableFactory.create(name, runnable);
@Override
public void enable() {
for (Task task : tasks) task.register(runnableFactory);
}
@Override
@ -49,4 +54,8 @@ public abstract class TaskSystem implements SubSystem {
runnableFactory.cancelAllKnownTasks();
}
public static abstract class Task extends AbsRunnable {
public abstract void register(RunnableFactory runnableFactory);
}
}

View File

@ -22,6 +22,6 @@ public class ConsoleChatFormatter extends ChatFormatter {
@Override
public int getWidth(String part) {
return part.length() - (StringUtils.countMatches(part, '§') * 2);
return part.length() - (StringUtils.countMatches(part, '\u00a7') * 2);
}
}

View File

@ -47,7 +47,7 @@ public class PlayerKillMutator {
Map<String, Object> killMap = new HashMap<>();
killMap.put("date", formatters.secondLong().apply(kill.getDate()));
killMap.put("victim", kill.getVictimName().orElse(kill.getVictim().toString()));
killMap.put("killer", kill.getKillerName().orElse("Missing UUID")); // TODO Kills should support killer UUID
killMap.put("killer", kill.getKillerName().orElse(kill.getKiller().toString()));
killMap.put("weapon", kill.getWeapon());
return killMap;
}

View File

@ -288,8 +288,8 @@ public class SessionsMutator {
kill -> {
Map<String, Object> killMap = new HashMap<>();
killMap.put("date", formatters.secondLong().apply(kill.getDate()));
killMap.put("victim", kill.getVictimName());
killMap.put("killer", sessionMap.get("player_name"));
killMap.put("victim", kill.getVictimName().orElse(kill.getVictim().toString()));
killMap.put("killer", playerName);
killMap.put("weapon", kill.getWeapon());
return killMap;
}

View File

@ -16,7 +16,6 @@
*/
package com.djrapitops.plan.delivery.export;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.identification.Server;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.ExportSettings;
@ -24,6 +23,7 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -41,8 +41,8 @@ public class ExportScheduler {
private final PlanConfig config;
private final DBSystem dbSystem;
private final TaskSystem taskSystem;
private final RunnableFactory runnableFactory;
private final Exporter exporter;
private final ErrorLogger errorLogger;
@ -50,13 +50,13 @@ public class ExportScheduler {
public ExportScheduler(
PlanConfig config,
DBSystem dbSystem,
TaskSystem taskSystem,
RunnableFactory runnableFactory,
Exporter exporter,
ErrorLogger errorLogger
) {
this.config = config;
this.dbSystem = dbSystem;
this.taskSystem = taskSystem;
this.runnableFactory = runnableFactory;
this.exporter = exporter;
this.errorLogger = errorLogger;
}
@ -68,7 +68,7 @@ public class ExportScheduler {
private void schedulePlayersPageExport() {
long period = TimeAmount.toTicks(config.get(ExportSettings.EXPORT_PERIOD), TimeUnit.MILLISECONDS);
taskSystem.registerTask("Players page export",
runnableFactory.create("Players page export",
new ExportTask(exporter, Exporter::exportPlayersPage, errorLogger)
).runTaskTimerAsynchronously(0L, period);
}
@ -84,14 +84,14 @@ public class ExportScheduler {
long offset = period / serverCount;
Optional<Server> proxy = servers.stream().filter(Server::isProxy).findFirst();
proxy.ifPresent(mainServer -> taskSystem.registerTask("Network export",
proxy.ifPresent(mainServer -> runnableFactory.create("Network export",
new ExportTask(exporter, same -> same.exportServerPage(mainServer), errorLogger))
.runTaskTimerAsynchronously(0L, period)
);
int offsetMultiplier = proxy.isPresent() ? 1 : 0; // Delay first server export if on a network.
for (Server server : servers) {
taskSystem.registerTask("Server export",
runnableFactory.create("Server export",
new ExportTask(exporter, same -> {
same.exportServerPage(server);
same.exportServerJSON(server);

View File

@ -215,7 +215,7 @@ public class NetworkPageExporter extends FileExporter {
() -> files.getResourceFromJar("web/" + resourceName).asWebResource());
Path to = toDirectory.resolve(resourceName);
if (resourceName.endsWith(".css")) {
if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) {
export(to, theme.replaceThemeColors(resource.asString()));
} else if ("js/network-values.js".equalsIgnoreCase(resourceName) || "js/sessionAccordion.js".equalsIgnoreCase(resourceName)) {
String relativePlayerLink = toRelativePathFromRoot("player");

View File

@ -191,7 +191,7 @@ public class PlayerPageExporter extends FileExporter {
() -> files.getResourceFromJar("web/" + resourceName).asWebResource());
Path to = toDirectory.resolve(resourceName);
if (resourceName.endsWith(".css")) {
if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) {
export(to, theme.replaceThemeColors(resource.asString()));
} else if (Resource.isTextResource(resourceName)) {
export(to, resource.asString());

View File

@ -164,7 +164,7 @@ public class PlayersPageExporter extends FileExporter {
() -> files.getResourceFromJar("web/" + resourceName).asWebResource());
Path to = toDirectory.resolve(resourceName);
if (resourceName.endsWith(".css")) {
if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) {
export(to, theme.replaceThemeColors(resource.asString()));
} else if (Resource.isTextResource(resourceName)) {
export(to, resource.asString());

View File

@ -234,7 +234,7 @@ public class ServerPageExporter extends FileExporter {
() -> files.getResourceFromJar("web/" + resourceName).asWebResource());
Path to = toDirectory.resolve(resourceName);
if (resourceName.endsWith(".css")) {
if (resourceName.endsWith(".css") || resourceName.endsWith("color-selector.js")) {
export(to, theme.replaceThemeColors(resource.asString()));
} else if (Resource.isTextResource(resourceName)) {
export(to, resource.asString());

View File

@ -16,6 +16,8 @@
*/
package com.djrapitops.plan.delivery.rendering.html.icon;
import org.apache.commons.lang3.StringUtils;
import java.util.Optional;
public enum Color {
@ -61,4 +63,8 @@ public enum Color {
public String getHtmlClass() {
return htmlClass;
}
public String getBackgroundColorClass() {
return StringUtils.replace(htmlClass, "col-", "bg-");
}
}

View File

@ -0,0 +1,90 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.extension.table.Table;
import java.util.List;
public class DynamicHtmlTable implements HtmlTable {
private final Header[] headers;
private final List<Object[]> rows;
public DynamicHtmlTable(Header[] headers, List<Object[]> rows) {
this.headers = headers;
this.rows = rows;
}
public DynamicHtmlTable(Table table) {
this(HtmlTable.mapToHeaders(table), table.getRows());
}
@Override
public String toHtml() {
return "<div class=\"table-responsive\">" +
"<table class=\"table table-bordered table-striped table-hover player-plugin-table dataTable\">" +
buildTableHeader() +
buildTableBody() +
"</table>" +
"</div>";
}
private String buildTableHeader() {
StringBuilder builtHeader = new StringBuilder("<thead><tr>");
for (Header header : headers) {
builtHeader.append("<th>")
.append(header.getIcon().toHtml())
.append(' ')
.append(header.getText())
.append("</th>");
}
builtHeader.append("</tr></thead>");
return builtHeader.toString();
}
private String buildTableBody() {
StringBuilder builtBody = new StringBuilder();
builtBody.append("<tbody>");
if (rows.isEmpty()) {
appendRow(builtBody, "No Data");
}
for (Object[] row : rows) {
appendRow(builtBody, row);
}
return builtBody.append("</tbody>").toString();
}
private void appendRow(StringBuilder builtBody, Object... row) {
int headerLength = row.length - 1;
builtBody.append("<tr>");
for (int i = 0; i < headers.length; i++) {
try {
if (i > headerLength) {
builtBody.append("<td>-");
} else {
builtBody.append("<td>");
Object value = row[i];
builtBody.append(value != null ? value : '-');
}
builtBody.append("</td>");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("Invalid formatter given at index " + i + ": " + e.getMessage(), e);
}
}
builtBody.append("</tr>");
}
}

View File

@ -0,0 +1,74 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.delivery.rendering.html.icon.Color;
import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
import com.djrapitops.plan.extension.table.Table;
import java.util.ArrayList;
public interface HtmlTable {
static HtmlTable fromExtensionTable(Table table, com.djrapitops.plan.extension.icon.Color tableColor) {
return fromExtensionTable(table, Color.getByName(tableColor.name()).orElse(Color.NONE));
}
static HtmlTable fromExtensionTable(Table table, Color tableColor) {
if (table.getRows().size() > 50) {
return new DynamicHtmlTable(table);
} else {
return new HtmlTableWithColoredHeader(table, tableColor);
}
}
static Header[] mapToHeaders(Table table) {
ArrayList<Header> headers = new ArrayList<>();
com.djrapitops.plan.extension.icon.Icon[] icons = table.getIcons();
String[] columns = table.getColumns();
for (int i = 0; i < columns.length; i++) {
String column = columns[i];
if (column == null) {
break;
}
headers.add(new Header(Icon.fromExtensionIcon(icons[i]), column));
}
return headers.toArray(new Header[0]);
}
String toHtml();
class Header {
private final Icon icon;
private final String text;
public Header(Icon icon, String text) {
this.icon = icon;
this.text = text;
}
public Icon getIcon() {
return icon;
}
public String getText() {
return text;
}
}
}

View File

@ -0,0 +1,94 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.delivery.rendering.html.icon.Color;
import com.djrapitops.plan.extension.table.Table;
import java.util.List;
public class HtmlTableWithColoredHeader implements HtmlTable {
private final Header[] headers;
private final Color headerColor;
private final List<Object[]> rows;
public HtmlTableWithColoredHeader(Header[] headers, Color headerColor, List<Object[]> rows) {
this.headers = headers;
this.headerColor = headerColor;
this.rows = rows;
}
public HtmlTableWithColoredHeader(Table table, Color headerColor) {
this(HtmlTable.mapToHeaders(table), headerColor, table.getRows());
}
@Override
public String toHtml() {
return "<div class=\"scrollbar\">" +
"<table class=\"table table-striped\">" +
buildTableHeader() +
buildTableBody() +
"</table>" +
"</div>";
}
private String buildTableHeader() {
StringBuilder builtHeader = new StringBuilder("<thead class=\"" + headerColor.getBackgroundColorClass() + "\"><tr>");
for (Header header : headers) {
builtHeader.append("<th>")
.append(header.getIcon().toHtml())
.append(' ')
.append(header.getText())
.append("</th>");
}
builtHeader.append("</tr></thead>");
return builtHeader.toString();
}
private String buildTableBody() {
StringBuilder builtBody = new StringBuilder();
builtBody.append("<tbody>");
if (rows.isEmpty()) {
appendRow(builtBody, "No Data");
}
for (Object[] row : rows) {
appendRow(builtBody, row);
}
return builtBody.append("</tbody>").toString();
}
private void appendRow(StringBuilder builtBody, Object... row) {
int headerLength = row.length - 1;
builtBody.append("<tr>");
for (int i = 0; i < headers.length; i++) {
try {
if (i > headerLength) {
builtBody.append("<td>-");
} else {
builtBody.append("<td>");
Object value = row[i];
builtBody.append(value != null ? value : '-');
}
builtBody.append("</td>");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("Invalid formatter given at index " + i + ": " + e.getMessage(), e);
}
}
builtBody.append("</tr>");
}
}

View File

@ -185,7 +185,7 @@ public class PlayerPluginTab implements Comparable<PlayerPluginTab> {
if (tableData.isWideTable()) {
hasWideTable = true;
}
builder.append(tableData.getHtmlTable().buildHtml());
builder.append(tableData.getHtmlTable().toHtml());
}
return builder.toString();
}

View File

@ -207,7 +207,7 @@ public class ServerPluginTabs {
private String buildTablesHtml(ExtensionTabData tabData) {
StringBuilder builder = new StringBuilder();
for (ExtensionTableData tableData : tabData.getTableData()) {
builder.append(tableData.getHtmlTable().buildHtml());
builder.append(tableData.getHtmlTable().toHtml());
}
return builder.toString();
}

View File

@ -16,11 +16,13 @@
*/
package com.djrapitops.plan.delivery.webserver.cache;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.web.resolver.MimeType;
import com.djrapitops.plan.delivery.web.resolver.Response;
import com.djrapitops.plan.delivery.webserver.resolver.json.RootJSONResolver;
import com.djrapitops.plan.storage.file.ResourceCache;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.RunnableFactory;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.lang3.StringUtils;
@ -141,7 +143,7 @@ public class JSONCache {
}
@Singleton
public static class CleanTask extends AbsRunnable {
public static class CleanTask extends TaskSystem.Task {
@Inject
public CleanTask() {
@ -153,5 +155,11 @@ public class JSONCache {
cleanUp();
ResourceCache.cleanUp();
}
@Override
public void register(RunnableFactory runnableFactory) {
long minute = TimeAmount.toTicks(1, TimeUnit.MINUTES);
runnableFactory.create(null, this).runTaskTimerAsynchronously(minute, minute);
}
}
}

View File

@ -16,10 +16,15 @@
*/
package com.djrapitops.plan.extension;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
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;
/**
* Task for updating {@link DataExtension} server values periodically.
@ -27,17 +32,29 @@ import javax.inject.Singleton;
* @author Rsl1122
*/
@Singleton
public class ExtensionServerMethodCallerTask extends AbsRunnable {
public class ExtensionServerDataUpdater extends TaskSystem.Task {
private final ExtensionSvc service;
private final PlanConfig config;
@Inject
public ExtensionServerMethodCallerTask(ExtensionSvc service) {
public ExtensionServerDataUpdater(
ExtensionSvc service,
PlanConfig config
) {
this.service = service;
this.config = config;
}
@Override
public void run() {
service.updateServerValues(CallEvents.SERVER_PERIODICAL);
}
@Override
public void register(RunnableFactory runnableFactory) {
long period = TimeAmount.toTicks(config.get(TimeSettings.EXTENSION_DATA_REFRESH_PERIOD), TimeUnit.MILLISECONDS);
long delay = TimeAmount.toTicks(30, TimeUnit.SECONDS);
runnableFactory.create(null, this).runTaskTimerAsynchronously(delay, period);
}
}

View File

@ -16,15 +16,10 @@
*/
package com.djrapitops.plan.extension.implementation.results;
import com.djrapitops.plan.data.element.TableContainer;
import com.djrapitops.plan.delivery.rendering.html.structure.HtmlTable;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.table.Table;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
@ -44,41 +39,8 @@ public class ExtensionTableData implements Comparable<ExtensionTableData> {
this.tableColor = tableColor;
}
public TableContainer getHtmlTable() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
List<Object[]> rows = table.getRows();
String[] header = buildHeader(columns, icons);
TableContainer htmlTable = new TableContainer(header);
if (rows.size() > 50) {
htmlTable.useJqueryDataTables(); // Use a jQuery data table since there are a lot of rows.
} else {
String colorName = com.djrapitops.plan.delivery.rendering.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.delivery.rendering.html.icon.Color.NONE).getHtmlClass()
.replace("col-", ""); // TODO after PluginData deprecation, change this thing
htmlTable.setColor(colorName);
}
for (Object[] row : rows) {
htmlTable.addRow(Arrays.stream(row).map(value -> value != null ? value.toString() : null).toArray(Serializable[]::new));
}
return htmlTable;
}
private String[] buildHeader(String[] columns, Icon[] icons) {
ArrayList<String> header = new ArrayList<>();
for (int i = 0; i < columns.length; i++) {
String column = columns[i];
if (column == null) {
break;
}
header.add(com.djrapitops.plan.delivery.rendering.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column);
}
return header.toArray(new String[0]);
public HtmlTable getHtmlTable() {
return HtmlTable.fromExtensionTable(table, tableColor);
}
@Override

View File

@ -16,6 +16,9 @@
*/
package com.djrapitops.plan.gathering;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.ExecutionException;
@ -78,4 +81,22 @@ public class ShutdownHook extends Thread {
Logger.getGlobal().log(Level.SEVERE, "Plan failed to save sessions on JVM shutdown.", e);
}
}
@Singleton
public static class Registrar extends TaskSystem.Task {
private final ShutdownHook shutdownHook;
@Inject
public Registrar(ShutdownHook shutdownHook) {this.shutdownHook = shutdownHook;}
@Override
public void run() {
shutdownHook.register();
}
@Override
public void register(RunnableFactory runnableFactory) {
runnableFactory.create(null, this).runTaskAsynchronously();
}
}
}

View File

@ -23,13 +23,13 @@ import java.util.Optional;
import java.util.UUID;
/**
* This class is used to store data about a player kill inside the UserInfo
* object.
* Represents a player vs player kill.
*
* @author Rsl1122
*/
public class PlayerKill implements DateHolder {
private final UUID killer;
private final UUID victim;
private final String weapon;
private final long date;
@ -40,31 +40,32 @@ public class PlayerKill implements DateHolder {
/**
* Creates a PlayerKill object with given parameters.
*
* @param killer UUID of the killer.
* @param victim UUID of the victim.
* @param weapon Weapon used.
* @param date Epoch millisecond at which the kill occurred.
*/
public PlayerKill(UUID victim, String weapon, long date) {
public PlayerKill(UUID killer, UUID victim, String weapon, long date) {
this.killer = killer;
this.victim = victim;
this.weapon = weapon;
this.date = date;
}
public PlayerKill(UUID victim, String weapon, long date, String victimName) {
this(victim, weapon, date);
public PlayerKill(UUID killer, UUID victim, String weapon, long date, String victimName) {
this(killer, victim, weapon, date);
this.victimName = victimName;
}
public PlayerKill(UUID victim, String weapon, long date, String victimName, String killerName) {
this(victim, weapon, date, victimName);
public PlayerKill(UUID killer, UUID victim, String weapon, long date, String victimName, String killerName) {
this(killer, victim, weapon, date, victimName);
this.killerName = killerName;
}
/**
* Get the victim's UUID.
*
* @return UUID of the victim.
*/
public UUID getKiller() {
return killer;
}
public UUID getVictim() {
return victim;
}
@ -97,25 +98,27 @@ public class PlayerKill implements DateHolder {
if (o == null || getClass() != o.getClass()) return false;
PlayerKill that = (PlayerKill) o;
return date == that.date &&
Objects.equals(killer, that.killer) &&
Objects.equals(victim, that.victim) &&
Objects.equals(weapon, that.weapon);
}
@Override
public int hashCode() {
return Objects.hash(victim, date, weapon);
return Objects.hash(killer, victim, date, weapon);
}
@Override
public String toString() {
return "PlayerKill{" +
"killer=" + killer + ", " +
"victim=" + victim + ", " +
"date=" + date + ", " +
"weapon='" + weapon + "'}";
}
public boolean isSelfKill() {
return getVictimName().map(v -> v.equals(killerName)).orElse(false);
return killer.equals(victim);
}
public boolean isNotSelfKill() {

View File

@ -1,39 +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.gathering.importing;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Placeholder for a ImportSystem.
*
* @author Rsl1122
*/
@Singleton
public class EmptyImportSystem extends ImportSystem {
@Inject
public EmptyImportSystem() {
// Inject constructor required for dagger
}
@Override
void registerImporters() {
// No importers to register.
}
}

View File

@ -20,30 +20,34 @@ import com.djrapitops.plan.SubSystem;
import com.djrapitops.plan.gathering.importing.importers.Importer;
import com.djrapitops.plugin.utilities.Verify;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
/**
* Abstract representation of an ImportSystem.
* <p>
* TODO it is possible to remove the abstract part of this class by binding Importers to a Map with Dagger
*
* @author Rsl1122
*/
public abstract class ImportSystem implements SubSystem {
@Singleton
public class ImportSystem implements SubSystem {
protected final Map<String, Importer> importers;
protected ImportSystem() {
importers = new HashMap<>();
@Inject
protected ImportSystem(Set<Importer> importers) {
this.importers = new HashMap<>();
for (Importer importer : importers) {
this.importers.put(importer.getName(), importer);
}
}
@Override
public void enable() {
registerImporters();
// Nothing to do
}
abstract void registerImporters();
public void registerImporter(Importer importer) {
Verify.nullCheck(importer, () -> new IllegalArgumentException("Importer cannot be null"));

View File

@ -16,17 +16,20 @@
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
/**
* Task for performing system resource usage checks asynchronously
@ -64,7 +67,7 @@ public class SystemUsageBuffer {
}
@Singleton
public static class RamAndCpuTask extends AbsRunnable {
public static class RamAndCpuTask extends TaskSystem.Task {
private final SystemUsageBuffer buffer;
private final PluginLogger logger;
@ -84,10 +87,17 @@ public class SystemUsageBuffer {
cancel();
}
}
@Override
public void register(RunnableFactory runnableFactory) {
long delay = TimeAmount.toTicks(1, TimeUnit.MINUTES) - TimeAmount.toTicks(500, TimeUnit.MILLISECONDS);
long period = TimeAmount.toTicks(1, TimeUnit.SECONDS);
runnableFactory.create(null, this).runTaskTimerAsynchronously(delay, period);
}
}
@Singleton
public static class DiskTask extends AbsRunnable {
public static class DiskTask extends TaskSystem.Task {
private final PlanConfig config;
private final SystemUsageBuffer buffer;
private final PluginLogger logger;
@ -121,6 +131,13 @@ public class SystemUsageBuffer {
cancel();
}
}
@Override
public void register(RunnableFactory runnableFactory) {
long delay = TimeAmount.toTicks(50, TimeUnit.SECONDS);
long period = TimeAmount.toTicks(1, TimeUnit.SECONDS);
runnableFactory.create(null, this).runTaskTimerAsynchronously(delay, period);
}
}
}

View File

@ -16,18 +16,22 @@
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import java.util.concurrent.TimeUnit;
/**
* Class responsible for calculating TPS every second.
*
* @author Rsl1122
*/
public abstract class TPSCounter extends AbsRunnable {
public abstract class TPSCounter extends TaskSystem.Task {
protected final PluginLogger logger;
protected final ErrorLogger errorLogger;
@ -51,6 +55,12 @@ public abstract class TPSCounter extends AbsRunnable {
}
}
public void register(RunnableFactory runnableFactory) {
long delay = TimeAmount.toTicks(1L, TimeUnit.MINUTES);
long period = TimeAmount.toTicks(1L, TimeUnit.SECONDS);
runnableFactory.create(null, this).runTaskTimer(delay, period);
}
public abstract void pulse();
}

View File

@ -30,16 +30,18 @@ public class Server implements Comparable<Server> {
private Integer id;
private String name;
private String webAddress;
private boolean proxy;
public Server(UUID uuid, String name, String webAddress) {
this(null, uuid, name, webAddress);
this(null, uuid, name, webAddress, false);
}
public Server(Integer id, UUID uuid, String name, String webAddress) {
public Server(Integer id, UUID uuid, String name, String webAddress, boolean proxy) {
this.id = id;
this.uuid = uuid;
this.name = name;
this.webAddress = webAddress;
this.proxy = proxy;
}
public Optional<Integer> getId() {
@ -105,7 +107,11 @@ public class Server implements Comparable<Server> {
}
public boolean isProxy() {
return "BungeeCord".equals(name);
return proxy;
}
public void setProxy(boolean proxy) {
this.proxy = proxy;
}
public boolean isNotProxy() {

View File

@ -72,10 +72,10 @@ public class ServerFileLoader extends Config implements ServerLoader {
UUID serverUUID = UUID.fromString(serverUUIDString);
String name = config.getNode(PluginSettings.SERVER_NAME.getPath())
.map(ConfigNode::getString)
.orElse("BungeeCord");
.orElse("Proxy");
String address = getString("Server.Web_address");
return Optional.of(new Server(id, serverUUID, name, address));
return Optional.of(new Server(id, serverUUID, name, address, false));
} catch (IOException e) {
throw new EnableException("Failed to read ServerInfoFile.yml: " + e.getMessage());
}

View File

@ -17,8 +17,6 @@
package com.djrapitops.plan.modules;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.importing.EmptyImportSystem;
import com.djrapitops.plan.gathering.importing.ImportSystem;
import com.djrapitops.plan.settings.ConfigSystem;
import com.djrapitops.plan.settings.ProxyConfigSystem;
import com.djrapitops.plan.storage.database.DBSystem;
@ -40,9 +38,6 @@ public interface ProxySuperClassBindingModule {
@Binds
ConfigSystem bindConfigSystem(ProxyConfigSystem configSystem);
@Binds
ImportSystem bindImportSystem(EmptyImportSystem emptyImportSystem);
@Binds
ServerSensor<?> bindServerSensor(ServerSensor<Object> sensor);

View File

@ -16,7 +16,10 @@
*/
package com.djrapitops.plan.modules;
import com.djrapitops.plan.DataService;
import com.djrapitops.plan.DataSvc;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.gathering.importing.importers.Importer;
import com.djrapitops.plan.settings.config.ExtensionSettings;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.locale.Locale;
@ -26,10 +29,13 @@ import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ElementsIntoSet;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
/**
@ -40,6 +46,12 @@ import java.util.function.Predicate;
@Module
public class SystemObjectProvidingModule {
@Provides
@ElementsIntoSet
Set<Importer> emptyImporterSet() {
return new HashSet<>();
}
@Provides
@Singleton
Locale provideLocale(LocaleSystem localeSystem) {
@ -78,4 +90,10 @@ public class SystemObjectProvidingModule {
return errorLogger;
}
@Provides
@Singleton
DataService provideDataService(DataSvc dataService) {
return dataService;
}
}

View File

@ -62,6 +62,6 @@ public class PlayerKillProcessor implements CriticalRunnable {
}
Session session = cachedSession.get();
session.playerKilled(new PlayerKill(victim, weaponName, time));
session.playerKilled(new PlayerKill(killer, victim, weaponName, time));
}
}

View File

@ -19,7 +19,6 @@ package com.djrapitops.plan.settings;
import com.djrapitops.plan.settings.config.ConfigReader;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.changes.ConfigUpdater;
import com.djrapitops.plan.settings.config.paths.DatabaseSettings;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.settings.network.ServerSettingsManager;
import com.djrapitops.plan.settings.theme.Theme;
@ -43,7 +42,6 @@ public class NukkitConfigSystem extends ConfigSystem {
private final ConfigUpdater configUpdater;
private final ServerSettingsManager serverSettingsManager;
private boolean firstInstall;
@Inject
public NukkitConfigSystem(
@ -62,7 +60,6 @@ public class NukkitConfigSystem extends ConfigSystem {
@Override
public void enable() {
firstInstall = !files.getConfigFile().exists();
super.enable();
if (config.isTrue(PluginSettings.PROXY_COPY_CONFIG)) {
serverSettingsManager.enable();
@ -81,10 +78,5 @@ public class NukkitConfigSystem extends ConfigSystem {
try (ConfigReader reader = new ConfigReader(files.getResourceFromJar("config.yml").asInputStream())) {
config.copyMissing(reader.read());
}
String dbType = config.get(DatabaseSettings.TYPE);
if ("sqlite".equalsIgnoreCase(dbType)) {
if (!firstInstall) logger.warn("'SQLite' is not supported on Nukkit, switching to 'H2'.");
config.set(DatabaseSettings.TYPE, "H2");
}
}
}

View File

@ -115,7 +115,7 @@ public enum CommandLang implements Lang {
RELOAD_COMPLETE("Cmd Info - Reload Complete", "§aReload Complete"),
RELOAD_FAILED("Cmd Info - Reload Failed", "§cSomething went wrong during reload of the plugin, a restart is recommended."),
NO_ADDRESS_NOTIFY("Cmd Notify - No Address", "§eNo address was available - using localhost as fallback. Set up 'Alternative_IP' settings."),
HOTSWAP_REMINDER("Manage - Remind HotSwap", "§eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin."),
HOTSWAP_REMINDER("Manage - Remind HotSwap", "§eRemember to swap to the new database (/plan db hotswap ${0}) & reload the plugin."),
PROGRESS_START("Manage - Start", "> §2Processing data.."),
PROGRESS("Manage - Progress", "${0} / ${1} processed.."),
PROGRESS_SUCCESS("Manage - Success", "> §aSuccess!"),

View File

@ -25,6 +25,7 @@ public enum JSLang implements Lang {
TEXT_PREDICTED_RETENTION("This value is a prediction based on previous players"),
TEXT_NO_SERVERS("No servers found in the database"),
TEXT_SERVER_INSTRUCTIONS("It appears that Plan is not installed on any game servers or not connected to the same database. See <a href=\"https://github.com/plan-player-analytics/Plan/wiki\">wiki</a> for Network tutorial."),
TEXT_NO_SERVER("No server to display online activity for"),
LABEL_REGISTERED_PLAYERS("Registered Players"),
LINK_SERVER_ANALYSIS("Server Analysis"),

View File

@ -17,7 +17,6 @@
package com.djrapitops.plan.settings.network;
import com.djrapitops.plan.SubSystem;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.Config;
import com.djrapitops.plan.settings.config.ConfigReader;
@ -35,6 +34,7 @@ import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -62,8 +62,8 @@ public class ServerSettingsManager implements SubSystem {
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final TaskSystem taskSystem;
private final ErrorLogger errorLogger;
private final RunnableFactory runnableFactory;
private final PluginLogger logger;
private FileWatcher watcher;
@ -73,7 +73,7 @@ public class ServerSettingsManager implements SubSystem {
PlanConfig config,
DBSystem dbSystem,
ServerInfo serverInfo,
TaskSystem taskSystem,
RunnableFactory runnableFactory,
PluginLogger logger,
ErrorLogger errorLogger
) {
@ -81,7 +81,7 @@ public class ServerSettingsManager implements SubSystem {
this.config = config;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.taskSystem = taskSystem;
this.runnableFactory = runnableFactory;
this.logger = logger;
this.errorLogger = errorLogger;
}
@ -125,7 +125,8 @@ public class ServerSettingsManager implements SubSystem {
private void scheduleDBCheckTask() {
long checkPeriod = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MILLISECONDS);
taskSystem.registerTask("Config Update DB Checker", new AbsRunnable() {
runnableFactory.create("Config Update DB Checker", new AbsRunnable() {
@Override
public void run() {
checkDBForNewConfigSettings(dbSystem.getDatabase());

View File

@ -16,16 +16,20 @@
*/
package com.djrapitops.plan.settings.upkeep;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.StoreConfigTransaction;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
/**
* Task that stores a server config in the database on boot.
@ -33,7 +37,7 @@ import javax.inject.Singleton;
* @author Rsl1122
*/
@Singleton
public class ConfigStoreTask extends AbsRunnable {
public class ConfigStoreTask extends TaskSystem.Task {
private final PlanFiles files;
private final PlanConfig config;
@ -63,4 +67,10 @@ public class ConfigStoreTask extends AbsRunnable {
logger.debug("Config Store Task - Config in db now up to date.");
cancel();
}
@Override
public void register(RunnableFactory runnableFactory) {
long delay = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MILLISECONDS) + 40;
runnableFactory.create(null, this).runTaskLaterAsynchronously(delay);
}
}

View File

@ -16,13 +16,18 @@
*/
package com.djrapitops.plan.settings.upkeep;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.network.NetworkSettingManager;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* Task on networks that stores server configs in /plugins/Plan/serverConfiguration in database on boot.
@ -30,14 +35,17 @@ import java.util.UUID;
* @author Rsl1122
*/
@Singleton
public class NetworkConfigStoreTask extends AbsRunnable {
public class NetworkConfigStoreTask extends TaskSystem.Task {
private final NetworkSettingManager networkSettingManager;
private final PlanConfig config;
@Inject
public NetworkConfigStoreTask(
PlanConfig config,
NetworkSettingManager networkSettingManager
) {
this.config = config;
this.networkSettingManager = networkSettingManager;
}
@ -47,6 +55,13 @@ public class NetworkConfigStoreTask extends AbsRunnable {
cancel();
}
@Override
public void register(RunnableFactory runnableFactory) {
long delay = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MILLISECONDS) + 40;
runnableFactory.create(null, this).runTaskLaterAsynchronously(delay);
}
private void updateDBConfigs() {
File[] configFiles = networkSettingManager.getConfigFiles();

View File

@ -99,8 +99,10 @@ public class H2DB extends SQLDB {
private void startConnectionPingTask() {
stopConnectionPingTask();
logger.warn("H2 database is going to be deprecated in version 5.2. It is recommended to move to MySQL or SQLite when possible.");
logger.warn("! ! ! ---------- ! ! !");
logger.warn("H2 database is DEPRECATED and WILL STOP WORKING in version 5.4 - It is recommended to move to MySQL or SQLite when possible.");
logger.warn("See https://github.com/plan-player-analytics/Plan/issues/1472 for details");
logger.warn("! ! ! ---------- ! ! !");
try {
// Maintains Connection.
connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getType().getName(),

View File

@ -94,7 +94,7 @@ public class MySQLDB extends SQLDB {
String database = config.get(DatabaseSettings.MYSQL_DATABASE);
String launchOptions = config.get(DatabaseSettings.MYSQL_LAUNCH_OPTIONS);
// REGEX: match "?", match "word=word&" *-times, match "word=word"
if (launchOptions.isEmpty() || !launchOptions.matches("\\?((\\w*=\\w*)&)*(\\w*=\\w*)")) {
if (launchOptions.isEmpty() || !launchOptions.matches("\\?(((\\w|[-])+=.+)&)*((\\w|[-])+=.+)")) {
launchOptions = "?rewriteBatchedStatements=true&useSSL=false";
logger.error(locale.getString(PluginLang.DB_MYSQL_LAUNCH_OPTIONS_FAIL, launchOptions));
}

View File

@ -173,7 +173,8 @@ public abstract class SQLDB extends AbstractDatabase {
new BadNukkitRegisterValuePatch(),
new LinkedToSecurityTablePatch(),
new LinkUsersToPlayersSecurityTablePatch(),
new LitebansTableHeaderPatch()
new LitebansTableHeaderPatch(),
new ServerIsProxyPatch()
};
}

View File

@ -151,19 +151,17 @@ public class LargeStoreQueries {
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();
for (Server server : servers) {
UUID uuid = server.getUuid();
if (uuid == null) {
continue;
}
statement.setString(1, uuid.toString());
statement.setString(2, name);
statement.setString(3, webAddress);
statement.setString(2, server.getName());
statement.setString(3, server.getWebAddress());
statement.setBoolean(4, true);
statement.setBoolean(5, server.isProxy());
statement.addBatch();
}
}

View File

@ -46,7 +46,9 @@ public class KillQueries {
}
public static Query<List<PlayerKill>> fetchPlayerKillsOnServer(UUID serverUUID, int limit) {
String sql = SELECT + KillsTable.VICTIM_UUID + ", " +
String sql = SELECT +
KillsTable.KILLER_UUID + ", " +
KillsTable.VICTIM_UUID + ", " +
"v." + UsersTable.USER_NAME + " as victim_name, " +
"k." + UsersTable.USER_NAME + " as killer_name," +
KillsTable.DATE + ", " +
@ -76,7 +78,9 @@ public class KillQueries {
}
public static Query<List<PlayerKill>> fetchPlayerKillsOfPlayer(UUID playerUUID) {
String sql = SELECT + KillsTable.VICTIM_UUID + ", " +
String sql = SELECT +
KillsTable.KILLER_UUID + ", " +
KillsTable.VICTIM_UUID + ", " +
"v." + UsersTable.USER_NAME + " as victim_name, " +
"k." + UsersTable.USER_NAME + " as killer_name," +
KillsTable.DATE + ", " +
@ -105,7 +109,9 @@ public class KillQueries {
}
public static Query<List<PlayerKill>> fetchPlayerDeathsOfPlayer(UUID playerUUID) {
String sql = SELECT + KillsTable.VICTIM_UUID + ", " +
String sql = SELECT +
KillsTable.KILLER_UUID + ", " +
KillsTable.VICTIM_UUID + ", " +
"v." + UsersTable.USER_NAME + " as victim_name, " +
"k." + UsersTable.USER_NAME + " as killer_name," +
KillsTable.DATE + ", " +
@ -137,10 +143,11 @@ public class KillQueries {
String victimName = set.getString("victim_name");
String killerName = set.getString("killer_name");
if (victimName != null && killerName != null) {
UUID killer = UUID.fromString(set.getString(KillsTable.KILLER_UUID));
UUID victim = UUID.fromString(set.getString(KillsTable.VICTIM_UUID));
long date = set.getLong(KillsTable.DATE);
String weapon = set.getString(KillsTable.WEAPON);
return Optional.of(new PlayerKill(victim, weapon, date, victimName, killerName));
return Optional.of(new PlayerKill(killer, victim, weapon, date, victimName, killerName));
}
return Optional.empty();
}

View File

@ -65,7 +65,9 @@ public class ServerQueries {
set.getInt(ServerTable.SERVER_ID),
serverUUID,
set.getString(ServerTable.NAME),
set.getString(ServerTable.WEB_ADDRESS)));
set.getString(ServerTable.WEB_ADDRESS),
set.getBoolean(ServerTable.PROXY)
));
}
return servers;
}
@ -106,7 +108,8 @@ public class ServerQueries {
set.getInt(ServerTable.SERVER_ID),
UUID.fromString(set.getString(ServerTable.SERVER_UUID)),
set.getString(ServerTable.NAME),
set.getString(ServerTable.WEB_ADDRESS)
set.getString(ServerTable.WEB_ADDRESS),
set.getBoolean(ServerTable.PROXY)
));
}
return Optional.empty();
@ -115,7 +118,31 @@ public class ServerQueries {
}
public static Query<Optional<Server>> fetchProxyServerInformation() {
return db -> db.query(fetchServerMatchingIdentifier("BungeeCord"));
String sql = SELECT + '*' + FROM + ServerTable.TABLE_NAME +
WHERE + ServerTable.INSTALLED + "=?" +
AND + ServerTable.PROXY + "=?" +
" LIMIT 1";
return new QueryStatement<Optional<Server>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true);
statement.setBoolean(2, true);
}
@Override
public Optional<Server> processResults(ResultSet set) throws SQLException {
if (set.next()) {
return Optional.of(new Server(
set.getInt(ServerTable.SERVER_ID),
UUID.fromString(set.getString(ServerTable.SERVER_UUID)),
set.getString(ServerTable.NAME),
set.getString(ServerTable.WEB_ADDRESS),
set.getBoolean(ServerTable.PROXY)
));
}
return Optional.empty();
}
};
}
public static Query<Map<UUID, String>> fetchServerNames() {
@ -165,11 +192,44 @@ public class ServerQueries {
set.getInt(ServerTable.SERVER_ID),
UUID.fromString(set.getString(ServerTable.SERVER_UUID)),
set.getString(ServerTable.NAME),
set.getString(ServerTable.WEB_ADDRESS)
set.getString(ServerTable.WEB_ADDRESS),
set.getBoolean(ServerTable.PROXY)
));
}
return matches;
}
};
}
public static Query<Integer> fetchServerCount() {
String sql = SELECT + "COUNT(1) as c" + FROM + ServerTable.TABLE_NAME +
WHERE + ServerTable.INSTALLED + "=?";
return new QueryStatement<Integer>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true);
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
return set.next() ? set.getInt("c") : 1;
}
};
}
public static Query<Integer> fetchBiggestServerID() {
String sql = SELECT + "MAX(" + ServerTable.SERVER_ID + ") as max_id" + FROM + ServerTable.TABLE_NAME +
WHERE + ServerTable.INSTALLED + "=?";
return new QueryStatement<Integer>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true);
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
return set.next() ? set.getInt("max_id") : 1;
}
};
}
}

View File

@ -67,6 +67,7 @@ public class SessionQueries {
WorldTimesTable.ADVENTURE + ',' +
WorldTimesTable.SPECTATOR + ',' +
WorldTable.NAME + ',' +
KillsTable.KILLER_UUID + ',' +
KillsTable.VICTIM_UUID + ',' +
"v." + UsersTable.USER_NAME + " as victim_name, " +
KillsTable.DATE + ',' +
@ -193,11 +194,12 @@ public class SessionQueries {
String victimName = set.getString("victim_name");
if (victimName != null) {
UUID killer = UUID.fromString(set.getString(KillsTable.KILLER_UUID));
UUID victim = UUID.fromString(set.getString(KillsTable.VICTIM_UUID));
long date = set.getLong(KillsTable.DATE);
String weapon = set.getString(KillsTable.WEAPON);
List<PlayerKill> playerKills = session.getPlayerKills();
PlayerKill newKill = new PlayerKill(victim, weapon, date, victimName);
PlayerKill newKill = new PlayerKill(killer, victim, weapon, date, victimName);
if (!playerKills.contains(newKill)) {
playerKills.add(newKill);
}

View File

@ -40,18 +40,20 @@ public class ServerTable {
public static final String NAME = "name";
public static final String WEB_ADDRESS = "web_address";
public static final String INSTALLED = "is_installed";
public static final String PROXY = "is_proxy";
@Deprecated
public static final String MAX_PLAYERS = "max_players";
public static final String INSERT_STATEMENT = Insert.values(TABLE_NAME,
SERVER_UUID, NAME,
WEB_ADDRESS, INSTALLED);
WEB_ADDRESS, INSTALLED, PROXY);
public static final String UPDATE_STATEMENT = Update.values(TABLE_NAME,
SERVER_UUID,
NAME,
WEB_ADDRESS,
INSTALLED)
INSTALLED,
PROXY)
.where(SERVER_UUID + "=?")
.toString();
@ -71,6 +73,7 @@ public class ServerTable {
.column(NAME, Sql.varchar(100))
.column(WEB_ADDRESS, Sql.varchar(100))
.column(INSTALLED, Sql.BOOL).notNull().defaultValue(true)
.column(PROXY, Sql.BOOL).notNull().defaultValue(false)
.column(MAX_PLAYERS, Sql.INT).notNull().defaultValue("-1")
.toString();
}

View File

@ -25,7 +25,7 @@ import static com.djrapitops.plan.storage.database.sql.tables.ServerTable.INSERT
import static com.djrapitops.plan.storage.database.sql.tables.ServerTable.UPDATE_STATEMENT;
/**
* Transaction for keeping Plan Server serverrmation up to date in the database.
* Transaction for keeping Plan Server information up to date in the database.
*
* @author Rsl1122
*/
@ -53,7 +53,8 @@ public class StoreServerInformationTransaction extends Transaction {
statement.setString(2, server.getName());
statement.setString(3, server.getWebAddress());
statement.setBoolean(4, true);
statement.setString(5, serverUUIDString);
statement.setBoolean(5, server.isProxy());
statement.setString(6, serverUUIDString);
}
};
}
@ -66,6 +67,7 @@ public class StoreServerInformationTransaction extends Transaction {
statement.setString(2, server.getName());
statement.setString(3, server.getWebAddress());
statement.setBoolean(4, true);
statement.setBoolean(5, server.isProxy());
}
};
}

View File

@ -16,7 +16,6 @@
*/
package com.djrapitops.plan.storage.database.transactions.commands;
import com.djrapitops.plan.delivery.domain.WebUser;
import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieStore;
import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries;
import com.djrapitops.plan.storage.database.sql.tables.SecurityTable;
@ -30,7 +29,7 @@ import static com.djrapitops.plan.storage.database.sql.building.Sql.DELETE_FROM;
import static com.djrapitops.plan.storage.database.sql.building.Sql.WHERE;
/**
* Transaction to remove a Plan {@link WebUser} from the database.
* Transaction to remove a Plan {@link com.djrapitops.plan.delivery.domain.auth.User} from the database.
*
* @author Rsl1122
*/

View File

@ -18,6 +18,8 @@ package com.djrapitops.plan.storage.database.transactions.patches;
/**
* Patch that removes plan_commandusages table.
* <p>
* See https://github.com/plan-player-analytics/Plan/issues/1240 for more details
*
* @author Rsl1122
*/

View File

@ -26,6 +26,8 @@ import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Patch that replaces plan_ips with plan_geolocations table.
* <p>
* Prepares Plan to be GDPR compliant through the easiest to make change.
*
* @author Rsl1122
*/

View File

@ -18,6 +18,11 @@ package com.djrapitops.plan.storage.database.transactions.patches;
import com.djrapitops.plan.storage.database.sql.tables.TPSTable;
/**
* Adds disk usage information to tps table.
*
* @author Rsl1122
*/
public class DiskUsagePatch extends Patch {
@Override

View File

@ -23,6 +23,11 @@ import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerTableValueTable;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionServerTableValueTable;
/**
* Increases the length of Strings in extension tables to 250 to avoid cutoffs and exceptions.
*
* @author Rsl1122
*/
public class ExtensionTableRowValueLengthPatch extends Patch {
private final String playerTable;

View File

@ -18,6 +18,11 @@ package com.djrapitops.plan.storage.database.transactions.patches;
import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable;
/**
* Adds last_used field to the geolocation table.
*
* @author Rsl1122
*/
public class GeoInfoLastUsedPatch extends Patch {
@Override

View File

@ -20,6 +20,11 @@ import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Replaces user_id foreign keys with user_uuid fields in geolocation table.
*
* @author Rsl1122
*/
public class GeoInfoOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -21,6 +21,13 @@ import com.djrapitops.plan.storage.database.sql.tables.KillsTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Replaces killer_id, victim_id and server_id foreign keys with respective uuid fields in kills table.
* <p>
* This was to "reduce the amount of joins when querying sessions".
*
* @author Rsl1122
*/
public class KillsOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -24,6 +24,14 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;
/**
* Adds server_id field to kills table.
* <p>
* The field is populated by querying the session table for server ids.
*
* @author Rsl1122
* @see KillsOptimizationPatch for removal of this field later
*/
public class KillsServerIDPatch extends Patch {
@Override

View File

@ -31,6 +31,12 @@ import java.util.Map;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Populates new linked_to_uuid field with the uuid of a username (same as minecraft name) or 'console'.
*
* @author Rsl1122
* @see LinkedToSecurityTablePatch for addition of the field.
*/
public class LinkUsersToPlayersSecurityTablePatch extends Patch {
@Override

View File

@ -19,6 +19,14 @@ package com.djrapitops.plan.storage.database.transactions.patches;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.SecurityTable;
/**
* Adds linked_to_uuid field to plan_security table that stores web users.
* <p>
* This patch allows web users to have a username other than the minecraft username.
*
* @author Rsl1122
* @see LinkUsersToPlayersSecurityTablePatch for the patch that populates the field afterwards.
*/
public class LinkedToSecurityTablePatch extends Patch {
@Override

View File

@ -35,6 +35,13 @@ import java.util.UUID;
import static com.djrapitops.plan.storage.database.sql.building.Sql.AND;
import static com.djrapitops.plan.storage.database.sql.building.Sql.WHERE;
/**
* Adds last_seen to nickname table by populating it with the data in actions table, and removes the actions table.
* <p>
* Actions table contained nickname change events and change to "last seen" saved space on the interface.
*
* @author Rsl1122
*/
public class NicknameLastSeenPatch extends Patch {
@Override

View File

@ -21,6 +21,13 @@ import com.djrapitops.plan.storage.database.sql.tables.NicknamesTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Replaces user_id and server_id foreign keys with respective uuid fields in nickname table.
* <p>
* This was to "reduce the amount of joins when querying sessions".
*
* @author Rsl1122
*/
public class NicknamesOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -21,6 +21,11 @@ import com.djrapitops.plan.storage.database.sql.tables.PingTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Replaces user_id and server_id foreign keys with respective uuid fields in ping table.
*
* @author Rsl1122
*/
public class PingOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -31,6 +31,15 @@ import java.util.UUID;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Changes register dates on networks to the smallest number found in the database.
* <p>
* Proxy servers do not store player register date information, so Game servers can hold earlier
* join date than the first session Plan sees. This patch changes the register date in
* plan_users if a smaller register date in plan_user_info is found.
*
* @author Rsl1122
*/
public class RegisterDateMinimizationPatch extends Patch {
private Map<UUID, Long> registerDates;

View File

@ -0,0 +1,56 @@
/*
* 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.storage.database.transactions.patches;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.ServerTable;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import static com.djrapitops.plan.storage.database.sql.building.Sql.WHERE;
/**
* Adds a is_proxy field to remove technical debt assuming name field "BungeeCord" being the proxy.
* <p>
* See https://github.com/plan-player-analytics/Plan/issues/1678 for more details
*
* @author Rsl1122
*/
public class ServerIsProxyPatch extends Patch {
@Override
public boolean hasBeenApplied() {
return hasColumn(ServerTable.TABLE_NAME, ServerTable.PROXY);
}
@Override
protected void applyPatch() {
addColumn(ServerTable.TABLE_NAME, ServerTable.PROXY + ' ' + Sql.BOOL + " DEFAULT 0");
String populateFieldSql = "UPDATE " + ServerTable.TABLE_NAME + " SET " + ServerTable.PROXY + "=?" +
WHERE + ServerTable.NAME + "=?";
execute(new ExecStatement(populateFieldSql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, true);
statement.setString(2, "BungeeCord");
}
});
}
}

View File

@ -18,6 +18,11 @@ package com.djrapitops.plan.storage.database.transactions.patches;
import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
/**
* Adds afk_time field to sessions table.
*
* @author Rsl1122
*/
public class SessionAFKTimePatch extends Patch {
@Override

View File

@ -21,6 +21,13 @@ import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Replaces user_id and server_id foreign keys with respective uuid fields in sessions table.
* <p>
* This was to "reduce the amount of joins when querying sessions".
*
* @author Rsl1122
*/
public class SessionsOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -16,6 +16,13 @@
*/
package com.djrapitops.plan.storage.database.transactions.patches;
/**
* Removes plan_transfer table, used for transferring html in the database.
* <p>
* The idea turned out to use a lot of disk space and improper use of a database.
*
* @author Rsl1122
*/
public class TransferTableRemovalPatch extends Patch {
@Override

View File

@ -21,6 +21,11 @@ import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Replaces user_id and server_id foreign keys with respective uuid fields in user info table.
*
* @author Rsl1122
*/
public class UserInfoOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -24,6 +24,18 @@ import java.util.Optional;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Table schema change patch for version 4.0.0 to support BungeeCord servers.
* <p>
* This patch makes the database compatible with further changes to the schema,
* bugs in this patch are possible, as the patch is untested against new schema.
* <p>
* Version 10 comes from "schema version" that was in use in the database to version changes
* before Patch system was implemented.
*
* @author Rsl1122
* @see VersionTableRemovalPatch for Patch that removes the schema versions
*/
public class Version10Patch extends Patch {
private Integer serverID;

View File

@ -16,6 +16,11 @@
*/
package com.djrapitops.plan.storage.database.transactions.patches;
/**
* Removes the table schema versioning table.
*
* @author Rsl1122
*/
public class VersionTableRemovalPatch extends Patch {
@Override

View File

@ -21,6 +21,13 @@ import com.djrapitops.plan.storage.database.sql.tables.WorldTimesTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Replaces server_id foreign keys with server_uuid field in world times table.
* <p>
* This was to "reduce the amount of joins when querying sessions".
*
* @author Rsl1122
*/
public class WorldTimesOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -26,6 +26,12 @@ import java.util.Map;
import static com.djrapitops.plan.storage.database.sql.building.Sql.WHERE;
/**
* Adds server_id field to world times table.
*
* @author Rsl1122
* @see WorldTimesOptimizationPatch for removal of this field later
*/
public class WorldTimesSeverIDPatch extends Patch {
@Override

View File

@ -21,6 +21,13 @@ import com.djrapitops.plan.storage.database.sql.tables.WorldTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.FROM;
/**
* Replaces server_id foreign keys with server_uuid field in world table.
* <p>
* This was to "reduce the amount of joins when querying sessions".
*
* @author Rsl1122
*/
public class WorldsOptimizationPatch extends Patch {
private final String tempTableName;

View File

@ -34,6 +34,12 @@ import java.util.stream.Collectors;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Adds server_id field to worlds table.
*
* @author Rsl1122
* @see WorldsOptimizationPatch for removal of the field.
*/
public class WorldsServerIDPatch extends Patch {
@Override

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.storage.upkeep;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalPlayerResultsTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalServerResultsTransaction;
@ -29,15 +30,18 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
import com.djrapitops.plan.storage.database.transactions.commands.RemovePlayerTransaction;
import com.djrapitops.plan.storage.database.transactions.init.RemoveDuplicateUserInfoTransaction;
import com.djrapitops.plan.storage.database.transactions.init.RemoveOldExtensionsTransaction;
import com.djrapitops.plan.storage.database.transactions.init.RemoveOldSampledDataTransaction;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -47,6 +51,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
@ -56,7 +61,7 @@ import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
* @author Rsl1122
*/
@Singleton
public class DBCleanTask extends AbsRunnable {
public class DBCleanTask extends TaskSystem.Task {
private final Locale locale;
private final DBSystem dbSystem;
@ -97,6 +102,7 @@ public class DBCleanTask extends AbsRunnable {
Database database = dbSystem.getDatabase();
try {
if (database.getState() != Database.State.CLOSED) {
database.executeTransaction(new RemoveOldSampledDataTransaction(
serverInfo.getServerUUID(),
config.get(TimeSettings.DELETE_TPS_DATA_AFTER),
@ -120,6 +126,30 @@ public class DBCleanTask extends AbsRunnable {
}
}
@Override
public void register(RunnableFactory runnableFactory) {
// Secondary task for registration due to database queries.
runnableFactory.create(null, new AbsRunnable() {
@Override
public void run() {
// Distribute clean task evenly between multiple servers.
// see https://github.com/plan-player-analytics/Plan/issues/1641 for why
Integer biggestId = dbSystem.getDatabase().query(ServerQueries.fetchBiggestServerID());
Integer id = serverInfo.getServer().getId().orElse(1);
double distributor = id * 1.0 / biggestId; // 0 < distributor <= 1
long distributingOverTime = config.get(TimeSettings.CLEAN_DATABASE_PERIOD);
// -40 seconds to start first at 20 seconds if only one server is present.
long startAfter = (long) (distributor * distributingOverTime) - 40L;
long delay = TimeAmount.toTicks(startAfter, TimeUnit.MILLISECONDS);
long period = TimeAmount.toTicks(config.get(TimeSettings.CLEAN_DATABASE_PERIOD), TimeUnit.MILLISECONDS);
runnableFactory.create(null, this).runTaskTimerAsynchronously(delay, period);
}
}).runTaskAsynchronously();
}
// VisibleForTesting
public int cleanOldPlayers(Database database) {
long now = System.currentTimeMillis();

View File

@ -16,11 +16,13 @@
*/
package com.djrapitops.plan.storage.upkeep;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -36,7 +38,7 @@ import java.util.concurrent.TimeUnit;
* @author Rsl1122
*/
@Singleton
public class LogsFolderCleanTask extends AbsRunnable {
public class LogsFolderCleanTask extends TaskSystem.Task {
private final File folder;
private final PlanConfig config;
@ -71,6 +73,12 @@ public class LogsFolderCleanTask extends AbsRunnable {
}
}
@Override
public void register(RunnableFactory runnableFactory) {
long delay = TimeAmount.toTicks(30L, TimeUnit.SECONDS);
runnableFactory.create(null, this).runTaskLaterAsynchronously(delay);
}
private void cleanFolder() {
long now = System.currentTimeMillis();
for (File file : Objects.requireNonNull(folder.listFiles())) {

View File

@ -1,35 +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.utilities.comparators;
import com.djrapitops.plan.delivery.domain.WebUser;
import java.util.Comparator;
/**
* Orders WebUsers in descending order by permission level.
*
* @author Rsl1122
*/
public class WebUserComparator implements Comparator<WebUser> {
@Override
public int compare(WebUser o1, WebUser o2) {
return Integer.compare(o2.getPermLevel(), o1.getPermLevel());
}
}

View File

@ -398,7 +398,7 @@ Manage - Fail Same server || 无法将服务器标记为
Manage - Fail, Confirmation || > §c[数据统计] 请添加 -a 以确认执行!${0}
Manage - List Importers || 导入器:
Manage - Progress || ${0} / ${1} processed..
Manage - Remind HotSwap || §e[数据统计] 请切换至新数据库并重载插件(/plan m hotswap ${0}
Manage - Remind HotSwap || §e[数据统计] 请切换至新数据库并重载插件(/plan db hotswap ${0}
Manage - Start || »§7 正在处理数据...
Manage - Success || §f» §2 成功!
Negative || 否

View File

@ -398,7 +398,7 @@ Manage - Fail Same server || Nelze označit tento server j
Manage - Fail, Confirmation || > §cPřidejte '-a' argument k potvrzení provedení: ${0}
Manage - List Importers || Importery:
Manage - Progress || ${0} / ${1} zpracovávání..
Manage - Remind HotSwap || §eNezapomeňte přehodit na novou databázi (/plan m hotswap ${0}) & reload pluginu.
Manage - Remind HotSwap || §eNezapomeňte přehodit na novou databázi (/plan db hotswap ${0}) & reload pluginu.
Manage - Start || > §2Zpracovávám data..
Manage - Success || > §aÚspěch!
Negative || Ne

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