Reduce the amount of tasks used for upkeep

- Ping gathering now uses a map of timestamps
  - Removes a task that waited for ping data to be reliable (one task / join)
- Cookie expiration now uses a map of timestamps
  - Removes a task that waited for cookie to expire (one task / login cookie)

Affects issues:
- Possibly fixed #1984
This commit is contained in:
Risto Lahtela 2022-01-06 15:43:48 +02:00
parent f26e17c437
commit a7478645bd
26 changed files with 285 additions and 119 deletions

View File

@ -44,6 +44,7 @@ import org.bukkit.event.player.PlayerQuitEvent;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -63,13 +64,13 @@ public class BukkitPingCounter extends TaskSystem.Task implements Listener {
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178
private final Map<UUID, Long> startRecording;
private final Map<UUID, List<DateObj<Integer>>> playerHistory; private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final Listeners listeners; private final Listeners listeners;
private final PlanConfig config; private final PlanConfig config;
private final DBSystem dbSystem; private final DBSystem dbSystem;
private final ServerInfo serverInfo; private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
private final boolean pingMethodAvailable; private final boolean pingMethodAvailable;
private PingMethod pingMethod; private PingMethod pingMethod;
@ -79,14 +80,13 @@ public class BukkitPingCounter extends TaskSystem.Task implements Listener {
Listeners listeners, Listeners listeners,
PlanConfig config, PlanConfig config,
DBSystem dbSystem, DBSystem dbSystem,
ServerInfo serverInfo, ServerInfo serverInfo
RunnableFactory runnableFactory
) { ) {
this.listeners = listeners; this.listeners = listeners;
this.config = config; this.config = config;
this.dbSystem = dbSystem; this.dbSystem = dbSystem;
this.serverInfo = serverInfo; this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory; startRecording = new ConcurrentHashMap<>();
playerHistory = new HashMap<>(); playerHistory = new HashMap<>();
Optional<PingMethod> pingMethod = loadPingMethod(); Optional<PingMethod> pingMethod = loadPingMethod();
@ -138,6 +138,16 @@ public class BukkitPingCounter extends TaskSystem.Task implements Listener {
@Override @Override
public void run() { public void run() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
Iterator<Map.Entry<UUID, Long>> starts = startRecording.entrySet().iterator();
while (starts.hasNext()) {
Map.Entry<UUID, Long> start = starts.next();
if (time >= start.getValue()) {
addPlayer(start.getKey());
starts.remove();
}
}
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator(); Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -164,11 +174,12 @@ public class BukkitPingCounter extends TaskSystem.Task implements Listener {
} }
} }
public void addPlayer(Player player) { public void addPlayer(UUID uuid) {
playerHistory.put(player.getUniqueId(), new ArrayList<>()); playerHistory.put(uuid, new ArrayList<>());
} }
public void removePlayer(Player player) { public void removePlayer(Player player) {
startRecording.remove(player.getUniqueId());
playerHistory.remove(player.getUniqueId()); playerHistory.remove(player.getUniqueId());
} }
@ -182,15 +193,11 @@ public class BukkitPingCounter extends TaskSystem.Task implements Listener {
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent joinEvent) { public void onPlayerJoin(PlayerJoinEvent joinEvent) {
Player player = joinEvent.getPlayer(); Player player = joinEvent.getPlayer();
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); Long pingDelayMs = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { if (pingDelayMs >= TimeUnit.HOURS.toMillis(2L)) {
return; return;
} }
runnableFactory.create(() -> { startRecording.put(player.getUniqueId(), System.currentTimeMillis() + pingDelayMs);
if (player.isOnline()) {
addPlayer(player);
}
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
} }
@EventHandler @EventHandler

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.modules.bukkit;
import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.web.ResourceWriteTask; import com.djrapitops.plan.delivery.web.ResourceWriteTask;
import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask; import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask;
import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask;
import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater; import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.ShutdownDataPreservation; import com.djrapitops.plan.gathering.ShutdownDataPreservation;
@ -93,4 +94,8 @@ public interface BukkitTaskModule {
@Binds @Binds
@IntoSet @IntoSet
TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask); TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask);
@Binds
@IntoSet
TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask);
} }

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.DaggerPlanBukkitComponent;
import com.djrapitops.plan.PlanBukkitComponent; import com.djrapitops.plan.PlanBukkitComponent;
import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.PlanSystem; import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.storage.database.SQLDB;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
@ -44,6 +45,7 @@ public class BukkitMockComponent {
public BukkitMockComponent(Path tempDir) { public BukkitMockComponent(Path tempDir) {
this.tempDir = tempDir; this.tempDir = tempDir;
SQLDB.setDownloadDriver(false);
} }
public PlanPlugin getPlanMock() throws Exception { public PlanPlugin getPlanMock() throws Exception {

View File

@ -44,6 +44,7 @@ import net.playeranalytics.plugin.server.Listeners;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -54,35 +55,43 @@ import java.util.concurrent.TimeUnit;
@Singleton @Singleton
public class BungeePingCounter extends TaskSystem.Task implements Listener { public class BungeePingCounter extends TaskSystem.Task implements Listener {
private final Map<UUID, Long> startRecording;
private final Map<UUID, List<DateObj<Integer>>> playerHistory; private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final Listeners listeners; private final Listeners listeners;
private final PlanConfig config; private final PlanConfig config;
private final DBSystem dbSystem; private final DBSystem dbSystem;
private final ServerInfo serverInfo; private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
@Inject @Inject
public BungeePingCounter( public BungeePingCounter(
Listeners listeners, Listeners listeners,
PlanConfig config, PlanConfig config,
DBSystem dbSystem, DBSystem dbSystem,
ServerInfo serverInfo, ServerInfo serverInfo
RunnableFactory runnableFactory
) { ) {
this.listeners = listeners; this.listeners = listeners;
this.config = config; this.config = config;
this.dbSystem = dbSystem; this.dbSystem = dbSystem;
this.serverInfo = serverInfo; this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory; startRecording = new ConcurrentHashMap<>();
playerHistory = new HashMap<>(); playerHistory = new HashMap<>();
} }
@Override @Override
public void run() { public void run() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
Iterator<Map.Entry<UUID, Long>> starts = startRecording.entrySet().iterator();
while (starts.hasNext()) {
Map.Entry<UUID, Long> start = starts.next();
if (time >= start.getValue()) {
addPlayer(start.getKey());
starts.remove();
}
}
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Map.Entry<UUID, List<DateObj<Integer>>> entry = iterator.next(); Map.Entry<UUID, List<DateObj<Integer>>> entry = iterator.next();
UUID uuid = entry.getKey(); UUID uuid = entry.getKey();
@ -119,12 +128,13 @@ public class BungeePingCounter extends TaskSystem.Task implements Listener {
} }
} }
public void addPlayer(ProxiedPlayer player) { public void addPlayer(UUID uuid) {
playerHistory.put(player.getUniqueId(), new ArrayList<>()); playerHistory.put(uuid, new ArrayList<>());
} }
public void removePlayer(ProxiedPlayer player) { public void removePlayer(ProxiedPlayer player) {
playerHistory.remove(player.getUniqueId()); playerHistory.remove(player.getUniqueId());
startRecording.remove(player.getUniqueId());
} }
private int getPing(ProxiedPlayer player) { private int getPing(ProxiedPlayer player) {
@ -134,15 +144,11 @@ public class BungeePingCounter extends TaskSystem.Task implements Listener {
@EventHandler @EventHandler
public void onPlayerJoin(ServerConnectedEvent joinEvent) { public void onPlayerJoin(ServerConnectedEvent joinEvent) {
ProxiedPlayer player = joinEvent.getPlayer(); ProxiedPlayer player = joinEvent.getPlayer();
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); Long pingDelayMs = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { if (pingDelayMs >= TimeUnit.HOURS.toMillis(2L)) {
return; return;
} }
runnableFactory.create(() -> { startRecording.put(player.getUniqueId(), System.currentTimeMillis() + pingDelayMs);
if (player.isConnected()) {
addPlayer(player);
}
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
} }
@EventHandler @EventHandler

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.modules.bungee;
import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.web.ResourceWriteTask; import com.djrapitops.plan.delivery.web.ResourceWriteTask;
import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask; import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask;
import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask;
import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater; import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.timed.BungeePingCounter; import com.djrapitops.plan.gathering.timed.BungeePingCounter;
@ -82,4 +83,8 @@ public interface BungeeTaskModule {
@Binds @Binds
@IntoSet @IntoSet
TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask); TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask);
@Binds
@IntoSet
TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask);
} }

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.DaggerPlanBungeeComponent;
import com.djrapitops.plan.PlanBungee; import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.PlanBungeeComponent; import com.djrapitops.plan.PlanBungeeComponent;
import com.djrapitops.plan.PlanSystem; import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.storage.database.SQLDB;
import java.nio.file.Path; import java.nio.file.Path;
@ -37,6 +38,7 @@ public class BungeeMockComponent {
public BungeeMockComponent(Path tempDir) { public BungeeMockComponent(Path tempDir) {
this.tempDir = tempDir; this.tempDir = tempDir;
SQLDB.setDownloadDriver(false);
} }
public PlanBungee getPlanMock() throws Exception { public PlanBungee getPlanMock() throws Exception {

View File

@ -0,0 +1,73 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.webserver.auth;
import com.djrapitops.plan.TaskSystem;
import dagger.Lazy;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
import net.playeranalytics.plugin.scheduling.TimeAmount;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Singleton
public class ActiveCookieExpiryCleanupTask extends TaskSystem.Task {
private final Lazy<ActiveCookieStore> activeCookieStore;
private final Map<String, Long> expiryDates;
@Inject
public ActiveCookieExpiryCleanupTask(Lazy<ActiveCookieStore> activeCookieStore) {
this.activeCookieStore = activeCookieStore;
this.expiryDates = new ConcurrentHashMap<>();
}
@Override
public void register(RunnableFactory runnableFactory) {
runnableFactory.create(this)
.runTaskTimerAsynchronously(
TimeAmount.toTicks(5, TimeUnit.SECONDS),
TimeAmount.toTicks(1, TimeUnit.SECONDS));
}
@Override
public void run() {
long time = System.currentTimeMillis();
Set<String> removed = new HashSet<>();
for (Map.Entry<String, Long> entry : expiryDates.entrySet()) {
Long expiryTime = entry.getValue();
if (expiryTime >= time) {
String cookie = entry.getKey();
activeCookieStore.get().removeCookie(cookie);
}
}
for (String removedCookie : removed) {
expiryDates.remove(removedCookie);
}
}
public void addExpiry(String cookie, Long time) {
expiryDates.put(cookie, time);
}
}

View File

@ -24,14 +24,13 @@ import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries;
import com.djrapitops.plan.storage.database.transactions.events.CookieChangeTransaction; import com.djrapitops.plan.storage.database.transactions.events.CookieChangeTransaction;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
import net.playeranalytics.plugin.scheduling.Task;
import net.playeranalytics.plugin.scheduling.TimeAmount;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.*; import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -42,28 +41,25 @@ public class ActiveCookieStore implements SubSystem {
public static long cookieExpiresAfter = TimeUnit.HOURS.toMillis(2L); public static long cookieExpiresAfter = TimeUnit.HOURS.toMillis(2L);
private static ActiveCookieStore activeCookieStore; private static ActiveCookieStore activeCookieStore;
private final ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask;
private final PlanConfig config; private final PlanConfig config;
private final DBSystem dbSystem; private final DBSystem dbSystem;
private final RunnableFactory runnableFactory;
private final Processing processing; private final Processing processing;
private final Collection<Task> expiryTasks;
@Inject @Inject
public ActiveCookieStore( public ActiveCookieStore(
ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask,
PlanConfig config, PlanConfig config,
DBSystem dbSystem, DBSystem dbSystem,
RunnableFactory runnableFactory,
Processing processing Processing processing
) { ) {
this.activeCookieExpiryCleanupTask = activeCookieExpiryCleanupTask;
ActiveCookieStore.activeCookieStore = this; ActiveCookieStore.activeCookieStore = this;
this.config = config; this.config = config;
this.dbSystem = dbSystem; this.dbSystem = dbSystem;
this.processing = processing; this.processing = processing;
this.runnableFactory = runnableFactory;
expiryTasks = new ArrayList<>();
} }
private static void removeCookieStatic(String cookie) { private static void removeCookieStatic(String cookie) {
@ -88,22 +84,13 @@ public class ActiveCookieStore implements SubSystem {
USERS_BY_COOKIE.putAll(dbSystem.getDatabase().query(WebUserQueries.fetchActiveCookies())); USERS_BY_COOKIE.putAll(dbSystem.getDatabase().query(WebUserQueries.fetchActiveCookies()));
for (Map.Entry<String, Long> entry : dbSystem.getDatabase().query(WebUserQueries.getCookieExpiryTimes()).entrySet()) { for (Map.Entry<String, Long> entry : dbSystem.getDatabase().query(WebUserQueries.getCookieExpiryTimes()).entrySet()) {
long timeToExpiry = Math.max(entry.getValue() - System.currentTimeMillis(), 0L); long timeToExpiry = Math.max(entry.getValue() - System.currentTimeMillis(), 0L);
expiryTasks.add(runnableFactory.create(() -> removeCookie(entry.getKey())) activeCookieExpiryCleanupTask.addExpiry(entry.getKey(), System.currentTimeMillis() + timeToExpiry);
.runTaskLaterAsynchronously(TimeAmount.toTicks(timeToExpiry, TimeUnit.MILLISECONDS)));
} }
} }
@Override @Override
public void disable() { public void disable() {
USERS_BY_COOKIE.clear(); USERS_BY_COOKIE.clear();
expiryTasks.forEach(task -> {
try {
task.cancel();
} catch (Exception e) {
// Ignore, task has already been cancelled
}
});
expiryTasks.clear();
} }
public Optional<User> checkCookie(String cookie) { public Optional<User> checkCookie(String cookie) {
@ -114,8 +101,7 @@ public class ActiveCookieStore implements SubSystem {
String cookie = DigestUtils.sha256Hex(user.getUsername() + UUID.randomUUID() + System.currentTimeMillis()); String cookie = DigestUtils.sha256Hex(user.getUsername() + UUID.randomUUID() + System.currentTimeMillis());
USERS_BY_COOKIE.put(cookie, user); USERS_BY_COOKIE.put(cookie, user);
saveNewCookie(user, cookie, System.currentTimeMillis()); saveNewCookie(user, cookie, System.currentTimeMillis());
expiryTasks.add(runnableFactory.create(() -> removeCookie(cookie)) activeCookieExpiryCleanupTask.addExpiry(cookie, System.currentTimeMillis() + cookieExpiresAfter);
.runTaskLaterAsynchronously(TimeAmount.toTicks(cookieExpiresAfter, TimeUnit.MILLISECONDS)));
return cookie; return cookie;
} }

View File

@ -16,7 +16,7 @@
*/ */
package com.djrapitops.plan.settings; package com.djrapitops.plan.settings;
import net.playeranalytics.plugin.scheduling.RunnableFactory; import com.djrapitops.plan.processing.Processing;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -24,16 +24,16 @@ import javax.inject.Singleton;
@Singleton @Singleton
public class SchedulerSvc implements SchedulerService { public class SchedulerSvc implements SchedulerService {
private final RunnableFactory runnableFactory; private final Processing processing;
@Inject @Inject
public SchedulerSvc(RunnableFactory runnableFactory) { public SchedulerSvc(Processing processing) {
this.runnableFactory = runnableFactory; this.processing = processing;
} }
@Override @Override
public void runAsync(Runnable runnable) { public void runAsync(Runnable runnable) {
runnableFactory.create(runnable).runTaskAsynchronously(); processing.submitNonCritical(runnable);
} }
public void register() { public void register() {

View File

@ -46,6 +46,7 @@ import net.playeranalytics.plugin.scheduling.TimeAmount;
import net.playeranalytics.plugin.server.PluginLogger; import net.playeranalytics.plugin.server.PluginLogger;
import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
@ -60,6 +61,8 @@ import java.util.function.Supplier;
*/ */
public abstract class SQLDB extends AbstractDatabase { public abstract class SQLDB extends AbstractDatabase {
private static boolean downloadDriver = true;
private static final List<Repository> DRIVER_REPOSITORIES = Arrays.asList( private static final List<Repository> DRIVER_REPOSITORIES = Arrays.asList(
new StandardRepository("https://papermc.io/repo/repository/maven-public/"), new StandardRepository("https://papermc.io/repo/repository/maven-public/"),
new StandardRepository("https://repo1.maven.org/maven2/") new StandardRepository("https://repo1.maven.org/maven2/")
@ -74,7 +77,7 @@ public abstract class SQLDB extends AbstractDatabase {
protected final PluginLogger logger; protected final PluginLogger logger;
protected final ErrorLogger errorLogger; protected final ErrorLogger errorLogger;
protected IsolatedClassLoader driverClassLoader; protected ClassLoader driverClassLoader;
private Supplier<ExecutorService> transactionExecutorServiceProvider; private Supplier<ExecutorService> transactionExecutorServiceProvider;
private ExecutorService transactionExecutor; private ExecutorService transactionExecutor;
@ -110,16 +113,24 @@ public abstract class SQLDB extends AbstractDatabase {
}; };
} }
public static void setDownloadDriver(boolean downloadDriver) {
SQLDB.downloadDriver = downloadDriver;
}
protected abstract List<String> getDependencyResource(); protected abstract List<String> getDependencyResource();
public void downloadDriver() { public void downloadDriver() {
DependencyManager dependencyManager = new DependencyManager(files.getDataDirectory().resolve("libraries")); if (downloadDriver) {
dependencyManager.loadFromResource(getDependencyResource()); DependencyManager dependencyManager = new DependencyManager(files.getDataDirectory().resolve("libraries"));
dependencyManager.download(null, DRIVER_REPOSITORIES); dependencyManager.loadFromResource(getDependencyResource());
dependencyManager.download(null, DRIVER_REPOSITORIES);
IsolatedClassLoader classLoader = new IsolatedClassLoader(); IsolatedClassLoader classLoader = new IsolatedClassLoader();
dependencyManager.load(null, classLoader); dependencyManager.load(null, classLoader);
this.driverClassLoader = classLoader; this.driverClassLoader = classLoader;
} else {
this.driverClassLoader = getClass().getClassLoader();
}
} }
@Override @Override
@ -262,9 +273,21 @@ public abstract class SQLDB extends AbstractDatabase {
public void close() { public void close() {
if (getState() == State.OPEN) setState(State.CLOSING); if (getState() == State.OPEN) setState(State.CLOSING);
closeTransactionExecutor(transactionExecutor); closeTransactionExecutor(transactionExecutor);
unloadDriverClassloader();
setState(State.CLOSED); setState(State.CLOSED);
} }
private void unloadDriverClassloader() {
try {
if (driverClassLoader != null && driverClassLoader instanceof IsolatedClassLoader) {
((IsolatedClassLoader) driverClassLoader).close();
}
driverClassLoader = null;
} catch (IOException e) {
errorLogger.error(e, ErrorContext.builder().build());
}
}
public abstract Connection getConnection() throws SQLException; public abstract Connection getConnection() throws SQLException;
public abstract void returnToPool(Connection connection); public abstract void returnToPool(Connection connection);

View File

@ -46,7 +46,7 @@ class ActiveCookieStoreTest {
when(dbSystem.getDatabase()).thenReturn(db); when(dbSystem.getDatabase()).thenReturn(db);
underTest = new ActiveCookieStore( underTest = new ActiveCookieStore(
Mockito.mock(PlanConfig.class), activeCookieExpiryCleanupTask, Mockito.mock(PlanConfig.class),
dbSystem, dbSystem,
new TestRunnableFactory(), new TestRunnableFactory(),
Mockito.mock(Processing.class) Mockito.mock(Processing.class)

View File

@ -78,7 +78,7 @@ public interface WebUserQueriesTest extends DatabaseTestPreparer {
userIsRegistered(); userIsRegistered();
User user = db().query(WebUserQueries.fetchUser(WEB_USERNAME)).orElseThrow(AssertionError::new); User user = db().query(WebUserQueries.fetchUser(WEB_USERNAME)).orElseThrow(AssertionError::new);
ActiveCookieStore cookieStore = new ActiveCookieStore(Mockito.mock(PlanConfig.class), dbSystem(), new TestRunnableFactory(), Mockito.mock(Processing.class)); ActiveCookieStore cookieStore = new ActiveCookieStore(activeCookieExpiryCleanupTask, Mockito.mock(PlanConfig.class), dbSystem(), new TestRunnableFactory(), Mockito.mock(Processing.class));
String cookie = cookieStore.generateNewCookie(user); String cookie = cookieStore.generateNewCookie(user);
@ -92,7 +92,7 @@ public interface WebUserQueriesTest extends DatabaseTestPreparer {
userIsRegistered(); userIsRegistered();
User user = db().query(WebUserQueries.fetchUser(WEB_USERNAME)).orElseThrow(AssertionError::new); User user = db().query(WebUserQueries.fetchUser(WEB_USERNAME)).orElseThrow(AssertionError::new);
ActiveCookieStore cookieStore = new ActiveCookieStore(Mockito.mock(PlanConfig.class), dbSystem(), new TestRunnableFactory(), Mockito.mock(Processing.class)); ActiveCookieStore cookieStore = new ActiveCookieStore(activeCookieExpiryCleanupTask, Mockito.mock(PlanConfig.class), dbSystem(), new TestRunnableFactory(), Mockito.mock(Processing.class));
String cookie = cookieStore.generateNewCookie(user); String cookie = cookieStore.generateNewCookie(user);
@ -106,7 +106,7 @@ public interface WebUserQueriesTest extends DatabaseTestPreparer {
userIsRegistered(); userIsRegistered();
User user = db().query(WebUserQueries.fetchUser(WEB_USERNAME)).orElseThrow(AssertionError::new); User user = db().query(WebUserQueries.fetchUser(WEB_USERNAME)).orElseThrow(AssertionError::new);
ActiveCookieStore cookieStore = new ActiveCookieStore(Mockito.mock(PlanConfig.class), dbSystem(), new TestRunnableFactory(), Mockito.mock(Processing.class)); ActiveCookieStore cookieStore = new ActiveCookieStore(activeCookieExpiryCleanupTask, Mockito.mock(PlanConfig.class), dbSystem(), new TestRunnableFactory(), Mockito.mock(Processing.class));
String cookie = cookieStore.generateNewCookie(user); String cookie = cookieStore.generateNewCookie(user);

View File

@ -18,6 +18,7 @@ package utilities.mocks;
import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.PlanSystem; import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.storage.database.SQLDB;
import com.djrapitops.plan.utilities.logging.PluginErrorLogger; import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
import net.playeranalytics.plugin.PlatformAbstractionLayer; import net.playeranalytics.plugin.PlatformAbstractionLayer;
import utilities.dagger.DaggerPlanPluginComponent; import utilities.dagger.DaggerPlanPluginComponent;
@ -40,6 +41,7 @@ public class PluginMockComponent {
public PluginMockComponent(Path tempDir) { public PluginMockComponent(Path tempDir) {
this.tempDir = tempDir; this.tempDir = tempDir;
SQLDB.setDownloadDriver(false);
} }
public PlanPlugin getPlanMock() throws Exception { public PlanPlugin getPlanMock() throws Exception {

View File

@ -41,6 +41,7 @@ import net.playeranalytics.plugin.server.Listeners;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -51,13 +52,13 @@ import java.util.concurrent.TimeUnit;
*/ */
public class FabricPingCounter extends TaskSystem.Task implements FabricListener { public class FabricPingCounter extends TaskSystem.Task implements FabricListener {
private final Map<UUID, Long> startRecording;
private final Map<UUID, List<DateObj<Integer>>> playerHistory; private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final Listeners listeners; private final Listeners listeners;
private final PlanConfig config; private final PlanConfig config;
private final DBSystem dbSystem; private final DBSystem dbSystem;
private final ServerInfo serverInfo; private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
private final MinecraftDedicatedServer server; private final MinecraftDedicatedServer server;
private boolean isEnabled = false; private boolean isEnabled = false;
@ -68,15 +69,14 @@ public class FabricPingCounter extends TaskSystem.Task implements FabricListener
PlanConfig config, PlanConfig config,
DBSystem dbSystem, DBSystem dbSystem,
ServerInfo serverInfo, ServerInfo serverInfo,
RunnableFactory runnableFactory,
MinecraftDedicatedServer server MinecraftDedicatedServer server
) { ) {
this.listeners = listeners; this.listeners = listeners;
this.config = config; this.config = config;
this.dbSystem = dbSystem; this.dbSystem = dbSystem;
this.serverInfo = serverInfo; this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory;
this.server = server; this.server = server;
startRecording = new ConcurrentHashMap<>();
playerHistory = new HashMap<>(); playerHistory = new HashMap<>();
ServerPlayConnectionEvents.JOIN.register((handler, sender, minecraftServer) -> onPlayerJoin(handler.player)); ServerPlayConnectionEvents.JOIN.register((handler, sender, minecraftServer) -> onPlayerJoin(handler.player));
ServerPlayConnectionEvents.DISCONNECT.register((handler, minecraftServer) -> onPlayerQuit(handler.player)); ServerPlayConnectionEvents.DISCONNECT.register((handler, minecraftServer) -> onPlayerQuit(handler.player));
@ -88,6 +88,16 @@ public class FabricPingCounter extends TaskSystem.Task implements FabricListener
return; return;
} }
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
Iterator<Map.Entry<UUID, Long>> starts = startRecording.entrySet().iterator();
while (starts.hasNext()) {
Map.Entry<UUID, Long> start = starts.next();
if (time >= start.getValue()) {
addPlayer(start.getKey());
starts.remove();
}
}
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator(); Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -126,12 +136,13 @@ public class FabricPingCounter extends TaskSystem.Task implements FabricListener
this.enable(); this.enable();
} }
public void addPlayer(ServerPlayerEntity player) { public void addPlayer(UUID uuid) {
playerHistory.put(player.getUuid(), new ArrayList<>()); playerHistory.put(uuid, new ArrayList<>());
} }
public void removePlayer(ServerPlayerEntity player) { public void removePlayer(ServerPlayerEntity player) {
playerHistory.remove(player.getUuid()); playerHistory.remove(player.getUuid());
startRecording.remove(player.getUuid());
} }
private int getPing(ServerPlayerEntity player) { private int getPing(ServerPlayerEntity player) {
@ -143,15 +154,11 @@ public class FabricPingCounter extends TaskSystem.Task implements FabricListener
return; return;
} }
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); Long pingDelayMs = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { if (pingDelayMs >= TimeUnit.HOURS.toMillis(2L)) {
return; return;
} }
runnableFactory.create(() -> { startRecording.put(player.getUuid(), System.currentTimeMillis() + pingDelayMs);
if (server.getPlayerManager().getPlayerList().contains(player)) {
addPlayer(player);
}
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
} }
public void onPlayerQuit(ServerPlayerEntity player) { public void onPlayerQuit(ServerPlayerEntity player) {

View File

@ -18,6 +18,7 @@ package net.playeranalytics.plan.modules.fabric;
import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.web.ResourceWriteTask; import com.djrapitops.plan.delivery.web.ResourceWriteTask;
import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask;
import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater; import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.ShutdownDataPreservation; import com.djrapitops.plan.gathering.ShutdownDataPreservation;
@ -88,4 +89,8 @@ public interface FabricTaskModule {
@Binds @Binds
@IntoSet @IntoSet
TaskSystem.Task bindResourceWriteTask(ResourceWriteTask resourceWriteTask); TaskSystem.Task bindResourceWriteTask(ResourceWriteTask resourceWriteTask);
@Binds
@IntoSet
TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask);
} }

View File

@ -18,6 +18,7 @@ package utilities.mocks;
import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.PlanSystem; import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.storage.database.SQLDB;
import net.minecraft.server.dedicated.MinecraftDedicatedServer; import net.minecraft.server.dedicated.MinecraftDedicatedServer;
import net.playeranalytics.plan.DaggerPlanFabricComponent; import net.playeranalytics.plan.DaggerPlanFabricComponent;
import net.playeranalytics.plan.PlanFabricComponent; import net.playeranalytics.plan.PlanFabricComponent;
@ -43,6 +44,7 @@ public class FabricMockComponent {
public FabricMockComponent(Path tempDir) { public FabricMockComponent(Path tempDir) {
this.tempDir = tempDir; this.tempDir = tempDir;
SQLDB.setDownloadDriver(false);
} }
public PlanPlugin getPlanMock() { public PlanPlugin getPlanMock() {

View File

@ -44,6 +44,7 @@ import net.playeranalytics.plugin.server.Listeners;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -57,33 +58,42 @@ import java.util.concurrent.TimeUnit;
@Singleton @Singleton
public class NukkitPingCounter extends TaskSystem.Task implements Listener { public class NukkitPingCounter extends TaskSystem.Task implements Listener {
private final Map<UUID, Long> startRecording;
private final Map<UUID, List<DateObj<Integer>>> playerHistory; private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final Listeners listeners; private final Listeners listeners;
private final PlanConfig config; private final PlanConfig config;
private final DBSystem dbSystem; private final DBSystem dbSystem;
private final ServerInfo serverInfo; private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
@Inject @Inject
public NukkitPingCounter( public NukkitPingCounter(
Listeners listeners, Listeners listeners,
PlanConfig config, PlanConfig config,
DBSystem dbSystem, DBSystem dbSystem,
ServerInfo serverInfo, ServerInfo serverInfo
RunnableFactory runnableFactory
) { ) {
this.listeners = listeners; this.listeners = listeners;
this.config = config; this.config = config;
this.dbSystem = dbSystem; this.dbSystem = dbSystem;
this.serverInfo = serverInfo; this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory; startRecording = new ConcurrentHashMap<>();
playerHistory = new HashMap<>(); playerHistory = new HashMap<>();
} }
@Override @Override
public void run() { public void run() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
Iterator<Map.Entry<UUID, Long>> starts = startRecording.entrySet().iterator();
while (starts.hasNext()) {
Map.Entry<UUID, Long> start = starts.next();
if (time >= start.getValue()) {
addPlayer(start.getKey());
starts.remove();
}
}
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator(); Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -121,26 +131,23 @@ public class NukkitPingCounter extends TaskSystem.Task implements Listener {
} }
} }
public void addPlayer(Player player) { public void addPlayer(UUID uuid) {
playerHistory.put(player.getUniqueId(), new ArrayList<>()); playerHistory.put(uuid, new ArrayList<>());
} }
public void removePlayer(Player player) { public void removePlayer(Player player) {
playerHistory.remove(player.getUniqueId()); playerHistory.remove(player.getUniqueId());
startRecording.remove(player.getUniqueId());
} }
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent joinEvent) { public void onPlayerJoin(PlayerJoinEvent joinEvent) {
Player player = joinEvent.getPlayer(); Player player = joinEvent.getPlayer();
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); Long pingDelayMs = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { if (pingDelayMs >= TimeUnit.HOURS.toMillis(2L)) {
return; return;
} }
runnableFactory.create(() -> { startRecording.put(player.getUniqueId(), System.currentTimeMillis() + pingDelayMs);
if (player.isOnline()) {
addPlayer(player);
}
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
} }
@EventHandler @EventHandler

View File

@ -20,6 +20,7 @@ import cn.nukkit.level.Level;
import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.web.ResourceWriteTask; import com.djrapitops.plan.delivery.web.ResourceWriteTask;
import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask; import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask;
import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask;
import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater; import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.ShutdownDataPreservation; import com.djrapitops.plan.gathering.ShutdownDataPreservation;
@ -93,4 +94,8 @@ public interface NukkitTaskModule {
@Binds @Binds
@IntoSet @IntoSet
TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask); TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask);
@Binds
@IntoSet
TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask);
} }

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.DaggerPlanNukkitComponent;
import com.djrapitops.plan.PlanNukkit; import com.djrapitops.plan.PlanNukkit;
import com.djrapitops.plan.PlanNukkitComponent; import com.djrapitops.plan.PlanNukkitComponent;
import com.djrapitops.plan.PlanSystem; import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.storage.database.SQLDB;
import java.nio.file.Path; import java.nio.file.Path;
@ -37,6 +38,7 @@ public class NukkitMockComponent {
public NukkitMockComponent(Path tempDir) { public NukkitMockComponent(Path tempDir) {
this.tempDir = tempDir; this.tempDir = tempDir;
SQLDB.setDownloadDriver(false);
} }
public PlanNukkit getPlanMock() throws Exception { public PlanNukkit getPlanMock() throws Exception {

View File

@ -41,6 +41,7 @@ import org.spongepowered.api.event.network.ClientConnectionEvent;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -50,33 +51,42 @@ import java.util.concurrent.TimeUnit;
*/ */
public class SpongePingCounter extends TaskSystem.Task { public class SpongePingCounter extends TaskSystem.Task {
private final Map<UUID, Long> startRecording;
private final Map<UUID, List<DateObj<Integer>>> playerHistory; private final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final Listeners listeners; private final Listeners listeners;
private final PlanConfig config; private final PlanConfig config;
private final DBSystem dbSystem; private final DBSystem dbSystem;
private final ServerInfo serverInfo; private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
@Inject @Inject
public SpongePingCounter( public SpongePingCounter(
Listeners listeners, Listeners listeners,
PlanConfig config, PlanConfig config,
DBSystem dbSystem, DBSystem dbSystem,
ServerInfo serverInfo, ServerInfo serverInfo
RunnableFactory runnableFactory
) { ) {
this.listeners = listeners; this.listeners = listeners;
this.config = config; this.config = config;
this.dbSystem = dbSystem; this.dbSystem = dbSystem;
this.serverInfo = serverInfo; this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory;
playerHistory = new HashMap<>(); playerHistory = new HashMap<>();
startRecording = new ConcurrentHashMap<>();
} }
@Override @Override
public void run() { public void run() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
Iterator<Map.Entry<UUID, Long>> starts = startRecording.entrySet().iterator();
while (starts.hasNext()) {
Map.Entry<UUID, Long> start = starts.next();
if (time >= start.getValue()) {
addPlayer(start.getKey());
starts.remove();
}
}
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator(); Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -114,12 +124,13 @@ public class SpongePingCounter extends TaskSystem.Task {
} }
} }
public void addPlayer(Player player) { public void addPlayer(UUID uuid) {
playerHistory.put(player.getUniqueId(), new ArrayList<>()); playerHistory.put(uuid, new ArrayList<>());
} }
public void removePlayer(Player player) { public void removePlayer(Player player) {
playerHistory.remove(player.getUniqueId()); playerHistory.remove(player.getUniqueId());
startRecording.remove(player.getUniqueId());
} }
private int getPing(Player player) { private int getPing(Player player) {
@ -129,15 +140,11 @@ public class SpongePingCounter extends TaskSystem.Task {
@Listener @Listener
public void onPlayerJoin(ClientConnectionEvent.Join joinEvent) { public void onPlayerJoin(ClientConnectionEvent.Join joinEvent) {
Player player = joinEvent.getTargetEntity(); Player player = joinEvent.getTargetEntity();
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); Long pingDelayMs = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { if (pingDelayMs >= TimeUnit.HOURS.toMillis(2L)) {
return; return;
} }
runnableFactory.create(() -> { startRecording.put(player.getUniqueId(), System.currentTimeMillis() + pingDelayMs);
if (player.isOnline()) {
addPlayer(player);
}
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
} }
@Listener @Listener

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.modules.sponge;
import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.web.ResourceWriteTask; import com.djrapitops.plan.delivery.web.ResourceWriteTask;
import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask; import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask;
import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask;
import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater; import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.ShutdownDataPreservation; import com.djrapitops.plan.gathering.ShutdownDataPreservation;
@ -93,4 +94,8 @@ public interface SpongeTaskModule {
@Binds @Binds
@IntoSet @IntoSet
TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask); TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask);
@Binds
@IntoSet
TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask);
} }

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.DaggerPlanSpongeComponent;
import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.PlanSpongeComponent; import com.djrapitops.plan.PlanSpongeComponent;
import com.djrapitops.plan.PlanSystem; import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.storage.database.SQLDB;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.spongepowered.api.Game; import org.spongepowered.api.Game;
import org.spongepowered.api.MinecraftVersion; import org.spongepowered.api.MinecraftVersion;
@ -50,6 +51,7 @@ public class SpongeMockComponent {
public SpongeMockComponent(Path tempDir) { public SpongeMockComponent(Path tempDir) {
this.tempDir = tempDir; this.tempDir = tempDir;
SQLDB.setDownloadDriver(false);
} }
public PlanPlugin getPlanMock() throws Exception { public PlanPlugin getPlanMock() throws Exception {

View File

@ -43,6 +43,7 @@ import net.playeranalytics.plugin.server.Listeners;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -55,6 +56,7 @@ import java.util.concurrent.TimeUnit;
@Singleton @Singleton
public class VelocityPingCounter extends TaskSystem.Task { public class VelocityPingCounter extends TaskSystem.Task {
private final Map<UUID, Long> startRecording;
final Map<UUID, List<DateObj<Integer>>> playerHistory; final Map<UUID, List<DateObj<Integer>>> playerHistory;
private final Listeners listeners; private final Listeners listeners;
@ -62,7 +64,6 @@ public class VelocityPingCounter extends TaskSystem.Task {
private final PlanConfig config; private final PlanConfig config;
private final DBSystem dbSystem; private final DBSystem dbSystem;
private final ServerInfo serverInfo; private final ServerInfo serverInfo;
private final RunnableFactory runnableFactory;
@Inject @Inject
public VelocityPingCounter( public VelocityPingCounter(
@ -70,21 +71,30 @@ public class VelocityPingCounter extends TaskSystem.Task {
PlanVelocity plugin, PlanVelocity plugin,
PlanConfig config, PlanConfig config,
DBSystem dbSystem, DBSystem dbSystem,
ServerInfo serverInfo, ServerInfo serverInfo
RunnableFactory runnableFactory
) { ) {
this.listeners = listeners; this.listeners = listeners;
this.plugin = plugin; this.plugin = plugin;
this.config = config; this.config = config;
this.dbSystem = dbSystem; this.dbSystem = dbSystem;
this.serverInfo = serverInfo; this.serverInfo = serverInfo;
this.runnableFactory = runnableFactory; startRecording = new ConcurrentHashMap<>();
playerHistory = new HashMap<>(); playerHistory = new HashMap<>();
} }
@Override @Override
public void run() { public void run() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
Iterator<Map.Entry<UUID, Long>> starts = startRecording.entrySet().iterator();
while (starts.hasNext()) {
Map.Entry<UUID, Long> start = starts.next();
if (time >= start.getValue()) {
addPlayer(start.getKey());
starts.remove();
}
}
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator(); Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -122,8 +132,8 @@ public class VelocityPingCounter extends TaskSystem.Task {
} }
} }
void addPlayer(Player player) { void addPlayer(UUID playerUuid) {
playerHistory.put(player.getUniqueId(), new ArrayList<>()); playerHistory.put(playerUuid, new ArrayList<>());
} }
public void removePlayer(Player player) { public void removePlayer(Player player) {
@ -137,15 +147,11 @@ public class VelocityPingCounter extends TaskSystem.Task {
@Subscribe @Subscribe
public void onPlayerJoin(ServerConnectedEvent joinEvent) { public void onPlayerJoin(ServerConnectedEvent joinEvent) {
Player player = joinEvent.getPlayer(); Player player = joinEvent.getPlayer();
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY); Long pingDelayMs = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) { if (pingDelayMs >= TimeUnit.HOURS.toMillis(2L)) {
return; return;
} }
runnableFactory.create(() -> { startRecording.put(player.getUniqueId(), System.currentTimeMillis() + pingDelayMs);
if (player.isActive()) {
addPlayer(player);
}
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
} }
@Subscribe @Subscribe

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.modules.velocity;
import com.djrapitops.plan.TaskSystem; import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.delivery.web.ResourceWriteTask; import com.djrapitops.plan.delivery.web.ResourceWriteTask;
import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask; import com.djrapitops.plan.delivery.web.WebAssetVersionCheckTask;
import com.djrapitops.plan.delivery.webserver.auth.ActiveCookieExpiryCleanupTask;
import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage; import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage;
import com.djrapitops.plan.extension.ExtensionServerDataUpdater; import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
import com.djrapitops.plan.gathering.timed.ProxyTPSCounter; import com.djrapitops.plan.gathering.timed.ProxyTPSCounter;
@ -82,4 +83,8 @@ public interface VelocityTaskModule {
@Binds @Binds
@IntoSet @IntoSet
TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask); TaskSystem.Task bindWebAssetVersionCheckTask(WebAssetVersionCheckTask webAssetVersionCheckTask);
@Binds
@IntoSet
TaskSystem.Task bindActiveCookieStoreExpiryTask(ActiveCookieExpiryCleanupTask activeCookieExpiryCleanupTask);
} }

View File

@ -58,10 +58,10 @@ class VelocityPingCounterTest {
@Test @Test
void offlinePlayerIsRemovedFromPlayerHistory() { void offlinePlayerIsRemovedFromPlayerHistory() {
VelocityPingCounter counter = new VelocityPingCounter(Mockito.mock(Listeners.class), plugin, null, null, null, null); VelocityPingCounter counter = new VelocityPingCounter(Mockito.mock(Listeners.class), plugin, null, null, null);
assertTrue(counter.playerHistory.isEmpty()); assertTrue(counter.playerHistory.isEmpty());
counter.addPlayer(player); counter.addPlayer(player.getUniqueId());
assertFalse(counter.playerHistory.isEmpty()); assertFalse(counter.playerHistory.isEmpty());
counter.run(); counter.run();

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.DaggerPlanVelocityComponent;
import com.djrapitops.plan.PlanSystem; import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.PlanVelocity; import com.djrapitops.plan.PlanVelocity;
import com.djrapitops.plan.PlanVelocityComponent; import com.djrapitops.plan.PlanVelocityComponent;
import com.djrapitops.plan.storage.database.SQLDB;
import java.nio.file.Path; import java.nio.file.Path;
@ -37,6 +38,7 @@ public class VelocityMockComponent {
public VelocityMockComponent(Path tempDir) { public VelocityMockComponent(Path tempDir) {
this.tempDir = tempDir; this.tempDir = tempDir;
SQLDB.setDownloadDriver(false);
} }
public PlanVelocity getPlanMock() throws Exception { public PlanVelocity getPlanMock() throws Exception {