diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/PlayerOnlineListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/PlayerOnlineListener.java index eac71670c..9bf42705f 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/PlayerOnlineListener.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/PlayerOnlineListener.java @@ -25,6 +25,7 @@ import com.djrapitops.plan.extension.ExtensionSvc; import com.djrapitops.plan.gathering.cache.NicknameCache; import com.djrapitops.plan.gathering.cache.SessionCache; import com.djrapitops.plan.gathering.domain.ActiveSession; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.gathering.geolocation.GeolocationCache; import com.djrapitops.plan.gathering.listeners.Status; import com.djrapitops.plan.identification.ServerInfo; @@ -114,7 +115,9 @@ public class PlayerOnlineListener implements Listener { boolean banned = result == PlayerLoginEvent.Result.KICK_BANNED; String joinAddress = event.getHostname(); if (!joinAddress.isEmpty()) { - joinAddresses.put(playerUUID, joinAddress.substring(0, joinAddress.lastIndexOf(':'))); + joinAddress = joinAddress.substring(0, joinAddress.lastIndexOf(':')); + joinAddresses.put(playerUUID, joinAddress); + dbSystem.getDatabase().executeTransaction(new StoreJoinAddressTransaction(joinAddress)); } dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> banned)); } catch (Exception e) { @@ -166,7 +169,7 @@ public class PlayerOnlineListener implements Listener { BukkitAFKListener.afkTracker.performedAction(playerUUID, time); String world = player.getWorld().getName(); - String gm = Optional.ofNullable(player.getGameMode()).map(gameMode -> gameMode.name()).orElse("Unknown"); + String gm = Optional.ofNullable(player.getGameMode()).map(Enum::name).orElse("Unknown"); Database database = dbSystem.getDatabase(); database.executeTransaction(new WorldNameStoreTransaction(serverUUID, world)); @@ -192,8 +195,10 @@ public class PlayerOnlineListener implements Listener { ActiveSession session = new ActiveSession(playerUUID, serverUUID, time, world, gm); session.getExtraData().put(PlayerName.class, new PlayerName(playerName)); session.getExtraData().put(ServerName.class, new ServerName(serverInfo.getServer().getIdentifiableName())); + session.getExtraData().put(JoinAddress.class, new JoinAddress(getHostName)); sessionCache.cacheSession(playerUUID, session) - .ifPresent(previousSession -> database.executeTransaction(new SessionEndTransaction(previousSession))); + .map(SessionEndTransaction::new) + .ifPresent(database::executeTransaction); database.executeTransaction(new NicknameStoreTransaction( playerUUID, new Nickname(displayName, time, serverUUID), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PerServerContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PerServerContainer.java index c44ecaa81..c78bcecfb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PerServerContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PerServerContainer.java @@ -48,7 +48,6 @@ public class PerServerContainer extends HashMap { putToContainerOfServer(serverUUID, PerServerKeys.REGISTERED, userInfo.getRegistered()); putToContainerOfServer(serverUUID, PerServerKeys.BANNED, userInfo.isBanned()); putToContainerOfServer(serverUUID, PerServerKeys.OPERATOR, userInfo.isOperator()); - putToContainerOfServer(serverUUID, PerServerKeys.JOIN_ADDRESS, userInfo.getJoinAddress()); } public void putUserInfo(Collection userInformation) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/SessionsMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/SessionsMutator.java index 50a7af905..cbeabdd3d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/SessionsMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/SessionsMutator.java @@ -27,6 +27,7 @@ import com.djrapitops.plan.delivery.rendering.html.Html; import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; import com.djrapitops.plan.delivery.rendering.json.graphs.pie.WorldPie; import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.settings.config.WorldAliasSettings; import com.djrapitops.plan.utilities.analysis.Median; @@ -289,6 +290,8 @@ public class SessionsMutator { WorldPie worldPie = graphs.pie().worldPie(session.getExtraData(WorldTimes.class).orElseGet(WorldTimes::new)); sessionMap.put("world_series", worldPie.getSlices()); sessionMap.put("gm_series", worldPie.toHighChartsDrillDownMaps()); + sessionMap.put("join_address", session.getExtraData(JoinAddress.class) + .map(JoinAddress::getAddress).orElse("-")); session.getExtraData(AveragePing.class).ifPresent(averagePing -> sessionMap.put("avg_ping", formatters.decimals().apply(averagePing.getValue()) + " ms") diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java index 4cf0c9438..89983d1d2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java @@ -50,6 +50,7 @@ import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQuerie import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries; import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; import com.djrapitops.plan.storage.database.queries.objects.*; +import com.djrapitops.plan.storage.database.sql.tables.JoinAddressTable; import com.djrapitops.plan.utilities.java.Lists; import com.djrapitops.plan.utilities.java.Maps; import net.playeranalytics.plugin.scheduling.TimeAmount; @@ -123,7 +124,7 @@ public class GraphJSONCreator { } public Map optimizedPerformanceGraphJSON(ServerUUID serverUUID, URIQuery query) { - long after = getAfter(query); // TODO Implement if performance issues become apparent. +// long after = getAfter(query); // TODO Implement if performance issues become apparent. long now = System.currentTimeMillis(); long twoMonthsAgo = now - TimeUnit.DAYS.toMillis(60); @@ -438,7 +439,7 @@ public class GraphJSONCreator { public Map playerHostnamePieJSONAsMap() { String[] pieColors = theme.getPieColors(ThemeVal.GRAPH_WORLD_PIE); - Map joinAddresses = dbSystem.getDatabase().query(UserInfoQueries.joinAddresses()); + Map joinAddresses = dbSystem.getDatabase().query(JoinAddressQueries.latestJoinAddresses()); translateUnknown(joinAddresses); @@ -450,7 +451,7 @@ public class GraphJSONCreator { public Map playerHostnamePieJSONAsMap(ServerUUID serverUUID) { String[] pieColors = theme.getPieColors(ThemeVal.GRAPH_WORLD_PIE); - Map joinAddresses = dbSystem.getDatabase().query(UserInfoQueries.joinAddresses(serverUUID)); + Map joinAddresses = dbSystem.getDatabase().query(JoinAddressQueries.latestJoinAddresses(serverUUID)); translateUnknown(joinAddresses); @@ -461,9 +462,9 @@ public class GraphJSONCreator { } public void translateUnknown(Map joinAddresses) { - Integer unknown = joinAddresses.get("unknown"); + Integer unknown = joinAddresses.get(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); if (unknown != null) { - joinAddresses.remove("unknown"); + joinAddresses.remove(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); joinAddresses.put(locale.getString(GenericLang.UNKNOWN).toLowerCase(), unknown); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java index b31bd8d21..8f8f5a19b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java @@ -151,6 +151,7 @@ public class DBOpException extends IllegalStateException implements ExceptionWit public boolean isUserIdConstraintViolation() { return context != null && context.getRelated().contains(DBOpException.CONSTRAINT_VIOLATION) - && getMessage().contains("user_id"); + && getCause() != null + && getCause().getMessage().contains("user_id"); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/DataMap.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/DataMap.java index 6f17595d3..5ee2b8ec7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/DataMap.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/DataMap.java @@ -41,6 +41,9 @@ public class DataMap { return Optional.ofNullable(ofType.cast(data.get(ofType.getName()))); } + public void remove(Class ofType) { + data.remove(ofType.getName()); + } @Override public boolean equals(Object o) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/FinishedSession.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/FinishedSession.java index dc5a4a45e..2f4a4e389 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/FinishedSession.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/FinishedSession.java @@ -17,7 +17,9 @@ package com.djrapitops.plan.gathering.domain; import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.identification.ServerUUID; +import com.djrapitops.plan.storage.database.sql.tables.JoinAddressTable; import com.google.gson.Gson; import org.apache.commons.lang3.StringUtils; @@ -47,7 +49,7 @@ public class FinishedSession implements DateHolder { this.start = start; this.end = end; this.afkTime = afkTime; - this.extraData = extraData; + this.extraData = extraData != null ? extraData : new DataMap(); } public UUID getPlayerUUID() { @@ -113,50 +115,6 @@ public class FinishedSession implements DateHolder { return getStart(); } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - FinishedSession that = (FinishedSession) o; - return start == that.start && end == that.end && - afkTime == that.afkTime && - Objects.equals(playerUUID, that.playerUUID) && - Objects.equals(serverUUID, that.serverUUID) && - Objects.equals(getExtraData(WorldTimes.class), that.getExtraData(WorldTimes.class)) && - Objects.equals(getExtraData(PlayerKills.class), that.getExtraData(PlayerKills.class)) && - Objects.equals(getExtraData(MobKillCounter.class), that.getExtraData(MobKillCounter.class)) && - Objects.equals(getExtraData(DeathCounter.class), that.getExtraData(DeathCounter.class)); - } - - @Override - public int hashCode() { - return Objects.hash(playerUUID, serverUUID, start, end, afkTime, extraData); - } - - @Override - public String toString() { - return "FinishedSession{" + - "playerUUID=" + playerUUID + - ", serverUUID=" + serverUUID + - ", start=" + start + - ", end=" + end + - ", afkTime=" + afkTime + - ", extraData=" + extraData + - '}'; - } - - public static class Id { - private final int value; - - public Id(int value) { - this.value = value; - } - - public int get() { - return value; - } - } - /** * Deserialize csv format of the session. * @@ -182,9 +140,43 @@ public class FinishedSession implements DateHolder { extraData.put(PlayerKills.class, gson.fromJson(asArray[6], PlayerKills.class)); extraData.put(MobKillCounter.class, gson.fromJson(asArray[7], MobKillCounter.class)); extraData.put(DeathCounter.class, gson.fromJson(asArray[8], DeathCounter.class)); + extraData.put(JoinAddress.class, new JoinAddress(asArray[9])); return Optional.of(new FinishedSession(playerUUID, serverUUID, start, end, afkTime, extraData)); } + @Override + public int hashCode() { + return Objects.hash(playerUUID, serverUUID, start, end, afkTime, extraData); + } + + @Override + public String toString() { + return "FinishedSession{" + + "playerUUID=" + playerUUID + + ", serverUUID=" + serverUUID + + ", start=" + start + + ", end=" + end + + ", afkTime=" + afkTime + + ", extraData=" + extraData + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FinishedSession that = (FinishedSession) o; + return start == that.start && end == that.end && + afkTime == that.afkTime && + Objects.equals(playerUUID, that.playerUUID) && + Objects.equals(serverUUID, that.serverUUID) && + Objects.equals(getExtraData(WorldTimes.class), that.getExtraData(WorldTimes.class)) && + Objects.equals(getExtraData(PlayerKills.class), that.getExtraData(PlayerKills.class)) && + Objects.equals(getExtraData(MobKillCounter.class), that.getExtraData(MobKillCounter.class)) && + Objects.equals(getExtraData(DeathCounter.class), that.getExtraData(DeathCounter.class)) && + Objects.equals(getExtraData(JoinAddress.class), that.getExtraData(JoinAddress.class)); + } + /** * Serialize into csv format. * @@ -199,6 +191,19 @@ public class FinishedSession implements DateHolder { getExtraData(WorldTimes.class).orElseGet(WorldTimes::new).toJson() + ';' + getExtraData(PlayerKills.class).orElseGet(PlayerKills::new).toJson() + ';' + getExtraData(MobKillCounter.class).orElseGet(MobKillCounter::new).toJson() + ';' + - getExtraData(DeathCounter.class).orElseGet(DeathCounter::new).toJson(); + getExtraData(DeathCounter.class).orElseGet(DeathCounter::new).toJson() + ';' + + getExtraData(JoinAddress.class).orElseGet(() -> new JoinAddress(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP)).getAddress(); + } + + public static class Id { + private final int value; + + public Id(int value) { + this.value = value; + } + + public int get() { + return value; + } } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/UserInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/UserInfo.java index ae2f87bbb..862999d19 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/UserInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/UserInfo.java @@ -55,6 +55,10 @@ public class UserInfo { return serverUUID; } + /** + * @deprecated Join address is now stored in {@link FinishedSession#getExtraData()}, this method may become unreliable in the future. + */ + @Deprecated public String getJoinAddress() { return joinAddress; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/event/JoinAddress.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/event/JoinAddress.java index 15f5509b6..052170001 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/event/JoinAddress.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/event/JoinAddress.java @@ -16,21 +16,41 @@ */ package com.djrapitops.plan.gathering.domain.event; +import java.util.Objects; +import java.util.function.Supplier; + public class JoinAddress { - private final String address; + private final Supplier address; public JoinAddress(String address) { + this(() -> address); + } + + public JoinAddress(Supplier address) { this.address = address; } public String getAddress() { - return address; + return address.get(); } @Override public String toString() { return "JoinAddress{" + - "address='" + address + '\'' + + "address='" + address.get() + '\'' + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + JoinAddress that = (JoinAddress) o; + return Objects.equals(getAddress(), that.getAddress()); + } + + @Override + public int hashCode() { + return Objects.hash(getAddress()); + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/modules/DataPipelineModule.java b/Plan/common/src/main/java/com/djrapitops/plan/modules/DataPipelineModule.java index 82d8587fc..4655b60b4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/modules/DataPipelineModule.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/modules/DataPipelineModule.java @@ -76,9 +76,7 @@ public class DataPipelineModule { service.pull(ActiveSession.class, uuid) .map(ActiveSession::getExtraData) .flatMap(extra -> extra.get(PlayerKills.class))) - .registerSink(UUID.class, MobKill.class, (uuid, kill) -> { - service.pull(MobKillCounter.class, uuid).ifPresent(Counter::add); - }) + .registerSink(UUID.class, MobKill.class, (uuid, kill) -> service.pull(MobKillCounter.class, uuid).ifPresent(Counter::add)) .registerSink(UUID.class, PlayerKill.class, (uuid, kill) -> { service.pull(PlayerKills.class, kill.getKiller().getUuid()).ifPresent(playerKills -> playerKills.add(kill)); service.pull(DeathCounter.class, kill.getVictim().getUuid()).ifPresent(Counter::add); @@ -91,7 +89,7 @@ public class DataPipelineModule { DataService.Pipeline playerLeaveToSession(SessionCache sessionCache) { return service -> service .registerOptionalMapper(UUID.class, PlayerLeave.class, FinishedSession.class, sessionCache::endSession) - .registerDatabaseSink(UUID.class, FinishedSession.class, SessionEndTransaction::new); + .registerDatabaseSink(UUID.class, FinishedSession.class, (playerUUID, session) -> new SessionEndTransaction(session)); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java index 7093c462c..6bd3abaab 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java @@ -225,7 +225,8 @@ public abstract class SQLDB extends AbstractDatabase { new SessionsOptimizationPatch(), new UserInfoHostnameAllowNullPatch(), new RegisterDateMinimizationPatch(), - new UsersTableNameLengthPatch() + new UsersTableNameLengthPatch(), + new SessionJoinAddressPatch() }; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/DataStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/DataStoreQueries.java index ad7d2eb33..9fb970001 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/DataStoreQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/DataStoreQueries.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database.queries; import com.djrapitops.plan.delivery.domain.Nickname; import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.sql.tables.*; import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; @@ -68,6 +69,8 @@ public class DataStoreQueries { statement.setInt(5, session.getMobKillCount()); statement.setLong(6, session.getAfkTime()); statement.setString(7, session.getServerUUID().toString()); + statement.setString(8, session.getExtraData(JoinAddress.class) + .map(JoinAddress::getAddress).orElse(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP)); } }; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java index 1ab2a4e78..75adb79c3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java @@ -19,9 +19,12 @@ package com.djrapitops.plan.storage.database.queries; import com.djrapitops.plan.delivery.domain.Nickname; import com.djrapitops.plan.delivery.domain.World; import com.djrapitops.plan.delivery.domain.auth.User; +import com.djrapitops.plan.exceptions.database.DBOpException; import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.identification.ServerUUID; +import com.djrapitops.plan.storage.database.queries.objects.JoinAddressQueries; import com.djrapitops.plan.storage.database.queries.objects.WorldTimesQueries; import com.djrapitops.plan.storage.database.sql.tables.*; import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; @@ -285,6 +288,8 @@ public class LargeStoreQueries { statement.setInt(5, session.getMobKillCount()); statement.setLong(6, session.getAfkTime()); statement.setString(7, session.getServerUUID().toString()); + statement.setString(8, session.getExtraData(JoinAddress.class) + .map(JoinAddress::getAddress).orElse(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP)); statement.addBatch(); } } @@ -294,6 +299,8 @@ public class LargeStoreQueries { public static Executable storeAllSessionsWithKillAndWorldData(Collection sessions) { return connection -> { Set existingWorlds = WorldTimesQueries.fetchWorlds().executeWithConnection(connection); + List existingJoinAddresses = JoinAddressQueries.allJoinAddresses().executeWithConnection(connection); + storeAllJoinAddresses(sessions, existingJoinAddresses).execute(connection); storeAllWorldNames(sessions, existingWorlds).execute(connection); storeAllSessionsWithoutKillOrWorldData(sessions).execute(connection); storeSessionKillData(sessions).execute(connection); @@ -301,16 +308,41 @@ public class LargeStoreQueries { }; } + private static Executable storeAllJoinAddresses(Collection sessions, List existingJoinAddresses) { + return new ExecBatchStatement(JoinAddressTable.INSERT_STATEMENT) { + @Override + public void prepare(PreparedStatement statement) { + sessions.stream() + .map(FinishedSession::getExtraData) + .map(extraData -> extraData.get(JoinAddress.class)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(JoinAddress::getAddress) + .map(String::toLowerCase) + .filter(address -> !existingJoinAddresses.contains(address)) + .distinct() + .forEach(address -> { + try { + statement.setString(1, address); + statement.addBatch(); + } catch (SQLException e) { + throw DBOpException.forCause(JoinAddressTable.INSERT_STATEMENT, e); + } + }); + } + }; + } + private static Executable storeAllWorldNames(Collection sessions, Set existingWorlds) { Set worlds = sessions.stream().flatMap(session -> { - ServerUUID serverUUID = session.getServerUUID(); - return session.getExtraData(WorldTimes.class) - .map(WorldTimes::getWorldTimes) - .map(Map::keySet) - .orElseGet(Collections::emptySet) - .stream() - .map(worldName -> new World(worldName, serverUUID)); - }).filter(world -> !existingWorlds.contains(world)) + ServerUUID serverUUID = session.getServerUUID(); + return session.getExtraData(WorldTimes.class) + .map(WorldTimes::getWorldTimes) + .map(Map::keySet) + .orElseGet(Collections::emptySet) + .stream() + .map(worldName -> new World(worldName, serverUUID)); + }).filter(world -> !existingWorlds.contains(world)) .collect(Collectors.toSet()); if (worlds.isEmpty()) return Executable.empty(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PerServerContainerQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PerServerContainerQuery.java index 5509c0ac4..5a9a8bccd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PerServerContainerQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PerServerContainerQuery.java @@ -23,6 +23,7 @@ import com.djrapitops.plan.delivery.domain.keys.Key; import com.djrapitops.plan.delivery.domain.keys.PerServerKeys; import com.djrapitops.plan.gathering.domain.FinishedSession; import com.djrapitops.plan.gathering.domain.UserInfo; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.SQLDB; import com.djrapitops.plan.storage.database.queries.PerServerAggregateQueries; @@ -64,6 +65,12 @@ public class PerServerContainerQuery implements Query { for (Map.Entry> entry : sessions.entrySet()) { ServerUUID serverUUID = entry.getKey(); List serverSessions = entry.getValue(); + if (!serverSessions.isEmpty()) { + serverSessions.get(0).getExtraData().get(JoinAddress.class).map(JoinAddress::getAddress) + .ifPresent(address -> + perServerContainer.putToContainerOfServer(serverUUID, PerServerKeys.JOIN_ADDRESS, address) + ); + } DataContainer serverContainer = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer()); serverContainer.putRawData(PerServerKeys.SESSIONS, serverSessions); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/JoinAddressFilter.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/JoinAddressFilter.java index 9e437e319..3083d5c97 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/JoinAddressFilter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/filter/filters/JoinAddressFilter.java @@ -18,7 +18,7 @@ package com.djrapitops.plan.storage.database.queries.filter.filters; import com.djrapitops.plan.delivery.domain.datatransfer.InputFilterDto; import com.djrapitops.plan.storage.database.DBSystem; -import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries; +import com.djrapitops.plan.storage.database.queries.objects.JoinAddressQueries; import javax.inject.Inject; import javax.inject.Singleton; @@ -46,11 +46,11 @@ public class JoinAddressFilter extends MultiOptionFilter { } private List getSelectionOptions() { - return dbSystem.getDatabase().query(UserInfoQueries.uniqueJoinAddresses()); + return dbSystem.getDatabase().query(JoinAddressQueries.uniqueJoinAddresses()); } @Override public Set getMatchingUserIds(InputFilterDto query) { - return dbSystem.getDatabase().query(UserInfoQueries.userIdsOfPlayersWithJoinAddresses(getSelected(query))); + return dbSystem.getDatabase().query(JoinAddressQueries.userIdsOfPlayersWithJoinAddresses(getSelected(query))); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/JoinAddressQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/JoinAddressQueries.java new file mode 100644 index 000000000..e46e3017f --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/JoinAddressQueries.java @@ -0,0 +1,142 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.objects; + +import com.djrapitops.plan.identification.ServerUUID; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.JoinAddressTable; +import com.djrapitops.plan.storage.database.sql.tables.ServerTable; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import org.apache.commons.text.TextStringBuilder; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; + +public class JoinAddressQueries { + + private JoinAddressQueries() { + /* Static method class */ + } + + public static Query> latestJoinAddresses() { + String selectLatestJoinAddresses = SELECT + + "COUNT(1) as total," + + JoinAddressTable.JOIN_ADDRESS + + FROM + SessionsTable.TABLE_NAME + " a" + + LEFT_JOIN + SessionsTable.TABLE_NAME + " b on a." + SessionsTable.ID + ">(selectLatestJoinAddresses, 100) { + @Override + public Map processResults(ResultSet set) throws SQLException { + Map joinAddresses = new TreeMap<>(); + while (set.next()) { + joinAddresses.put(set.getString(JoinAddressTable.JOIN_ADDRESS), set.getInt("total")); + } + return joinAddresses; + } + }; + } + + public static Query> latestJoinAddresses(ServerUUID serverUUID) { + String selectLatestJoinAddresses = SELECT + + "COUNT(1) as total," + + JoinAddressTable.JOIN_ADDRESS + + FROM + SessionsTable.TABLE_NAME + " a" + + LEFT_JOIN + SessionsTable.TABLE_NAME + " b on a." + SessionsTable.ID + ">(selectLatestJoinAddresses, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map joinAddresses = new TreeMap<>(); + while (set.next()) { + joinAddresses.put(set.getString(JoinAddressTable.JOIN_ADDRESS), set.getInt("total")); + } + return joinAddresses; + } + }; + } + + public static QueryStatement> allJoinAddresses() { + String sql = SELECT + JoinAddressTable.JOIN_ADDRESS + + FROM + JoinAddressTable.TABLE_NAME + + ORDER_BY + JoinAddressTable.JOIN_ADDRESS + " ASC"; + return new QueryAllStatement>(sql, 100) { + @Override + public List processResults(ResultSet set) throws SQLException { + List joinAddresses = new ArrayList<>(); + while (set.next()) joinAddresses.add(set.getString(JoinAddressTable.JOIN_ADDRESS)); + return joinAddresses; + } + }; + } + + public static Query> uniqueJoinAddresses() { + return db -> { + List addresses = db.query(allJoinAddresses()); + if (!addresses.contains(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP)) { + addresses.add(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + } + return addresses; + }; + } + + public static Query> userIdsOfPlayersWithJoinAddresses(List joinAddresses) { + String sql = SELECT + DISTINCT + SessionsTable.USER_ID + + FROM + JoinAddressTable.TABLE_NAME + " j" + + INNER_JOIN + SessionsTable.TABLE_NAME + " s on s." + SessionsTable.JOIN_ADDRESS_ID + "=j." + JoinAddressTable.ID + + WHERE + JoinAddressTable.JOIN_ADDRESS + " IN (" + + new TextStringBuilder().appendWithSeparators(joinAddresses.stream().map(item -> '?').iterator(), ",") + + ')'; // Don't append addresses directly, SQL injection hazard + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + for (int i = 0; i < joinAddresses.size(); i++) { + statement.setString(i + 1, joinAddresses.get(i).toLowerCase()); + } + } + + @Override + public Set processResults(ResultSet set) throws SQLException { + return UserInfoQueries.extractUserIds(set, SessionsTable.USER_ID); + } + }; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java index 7386a8be6..72f0cb4f9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java @@ -22,6 +22,7 @@ import com.djrapitops.plan.delivery.domain.ServerIdentifier; import com.djrapitops.plan.delivery.domain.ServerName; import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.queries.Query; @@ -70,6 +71,7 @@ public class SessionQueries { WorldTimesTable.ADVENTURE + ',' + WorldTimesTable.SPECTATOR + ',' + WorldTable.NAME + ',' + + "j." + JoinAddressTable.JOIN_ADDRESS + " as join_address," + KillsTable.KILLER_UUID + ',' + KillsTable.VICTIM_UUID + ',' + "v." + UsersTable.USER_NAME + " as victim_name, " + @@ -77,6 +79,7 @@ public class SessionQueries { KillsTable.DATE + ',' + KillsTable.WEAPON + FROM + SessionsTable.TABLE_NAME + " s" + + INNER_JOIN + JoinAddressTable.TABLE_NAME + " j on s." + SessionsTable.JOIN_ADDRESS_ID + "=j." + JoinAddressTable.ID + INNER_JOIN + UsersTable.TABLE_NAME + " u on u." + UsersTable.ID + "=s." + SessionsTable.USER_ID + INNER_JOIN + ServerTable.TABLE_NAME + " server on server." + ServerTable.ID + "=s." + SessionsTable.SERVER_ID + LEFT_JOIN + UserInfoTable.TABLE_NAME + " u_info on (u_info." + UserInfoTable.USER_ID + "=s." + SessionsTable.USER_ID + AND + "u_info." + UserInfoTable.SERVER_ID + "=s." + SessionsTable.SERVER_ID + ')' + @@ -114,7 +117,7 @@ public class SessionQueries { String sql = SELECT_SESSIONS_STATEMENT + WHERE + "s." + SessionsTable.USER_ID + "=" + UsersTable.SELECT_USER_ID + ORDER_BY_SESSION_START_DESC; - return new QueryStatement>>(sql, 50000) { + return new QueryStatement>>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, playerUUID.toString()); @@ -159,6 +162,7 @@ public class SessionQueries { extraData.put(FinishedSession.Id.class, new FinishedSession.Id(set.getInt(SessionsTable.ID))); extraData.put(MobKillCounter.class, new MobKillCounter(set.getInt(SessionsTable.MOB_KILLS))); extraData.put(DeathCounter.class, new DeathCounter(set.getInt(SessionsTable.DEATHS))); + extraData.put(JoinAddress.class, new JoinAddress(set.getString("join_address"))); Optional existingWorldTimes = extraData.get(WorldTimes.class); Optional existingPlayerKills = extraData.get(PlayerKills.class); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserInfoQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserInfoQueries.java index bfdf39ecc..78fd70d9c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserInfoQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserInfoQueries.java @@ -158,82 +158,6 @@ public class UserInfoQueries { }; } - public static Query> joinAddresses() { - String sql = SELECT + - "COUNT(1) as total," + - "LOWER(COALESCE(" + UserInfoTable.JOIN_ADDRESS + ", ?)) as address" + - FROM + '(' + - SELECT + DISTINCT + - UserInfoTable.USER_ID + ',' + - UserInfoTable.JOIN_ADDRESS + - FROM + UserInfoTable.TABLE_NAME + - ") q1" + - GROUP_BY + "address" + - ORDER_BY + "address ASC"; - - return new QueryStatement>(sql, 100) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, "Unknown"); - } - - @Override - public Map processResults(ResultSet set) throws SQLException { - Map joinAddresses = new TreeMap<>(); - while (set.next()) { - joinAddresses.put(set.getString("address"), set.getInt("total")); - } - return joinAddresses; - } - }; - } - - public static Query> joinAddresses(ServerUUID serverUUID) { - String sql = SELECT + - "COUNT(1) as total," + - "LOWER(COALESCE(" + UserInfoTable.JOIN_ADDRESS + ", ?)) as address" + - FROM + UserInfoTable.TABLE_NAME + - WHERE + UserInfoTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + - GROUP_BY + "address" + - ORDER_BY + "address ASC"; - - return new QueryStatement>(sql, 100) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, "Unknown"); - statement.setString(2, serverUUID.toString()); - } - - @Override - public Map processResults(ResultSet set) throws SQLException { - Map joinAddresses = new TreeMap<>(); - while (set.next()) { - joinAddresses.put(set.getString("address"), set.getInt("total")); - } - return joinAddresses; - } - }; - } - - public static Query> uniqueJoinAddresses() { - String sql = SELECT + DISTINCT + "LOWER(COALESCE(" + UserInfoTable.JOIN_ADDRESS + ", ?)) as address" + - FROM + UserInfoTable.TABLE_NAME + - ORDER_BY + "address ASC"; - return new QueryStatement>(sql, 100) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, "unknown"); - } - - @Override - public List processResults(ResultSet set) throws SQLException { - List joinAddresses = new ArrayList<>(); - while (set.next()) joinAddresses.add(set.getString("address")); - return joinAddresses; - } - }; - } - public static Query> userIdsOfOperators() { return getUserIdsForBooleanGroup(UserInfoTable.OP, true); } @@ -257,9 +181,13 @@ public class UserInfoQueries { } public static Set extractUserIds(ResultSet set) throws SQLException { + return extractUserIds(set, UsersTable.ID); + } + + public static Set extractUserIds(ResultSet set, String column) throws SQLException { Set userIds = new HashSet<>(); while (set.next()) { - userIds.add(set.getInt(UsersTable.ID)); + userIds.add(set.getInt(column)); } return userIds; } @@ -276,35 +204,6 @@ public class UserInfoQueries { return getUserIdsForBooleanGroup(UserInfoTable.BANNED, false); } - public static Query> userIdsOfPlayersWithJoinAddresses(List joinAddresses) { - String selectLowercaseJoinAddresses = SELECT + - "u." + UsersTable.ID + ',' + - "LOWER(COALESCE(" + UserInfoTable.JOIN_ADDRESS + ", ?)) as address" + - FROM + UserInfoTable.TABLE_NAME + - INNER_JOIN + UsersTable.TABLE_NAME + " u on u." + UsersTable.ID + '=' + UserInfoTable.TABLE_NAME + '.' + UserInfoTable.USER_ID; - String sql = SELECT + DISTINCT + UsersTable.ID + - FROM + '(' + selectLowercaseJoinAddresses + ") q1" + - WHERE + "address IN (" + - new TextStringBuilder().appendWithSeparators(joinAddresses.stream().map(item -> '?').iterator(), ",") + - ')'; // Don't append addresses directly, SQL injection hazard - - return new QueryStatement>(sql) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, "unknown"); - for (int i = 1; i <= joinAddresses.size(); i++) { - String address = joinAddresses.get(i - 1); - statement.setString(i + 1, address); - } - } - - @Override - public Set processResults(ResultSet set) throws SQLException { - return extractUserIds(set); - } - }; - } - public static Query> userIdsOfRegisteredBetween(long after, long before, List serverUUIDs) { String selectServerIds = SELECT + ServerTable.ID + FROM + ServerTable.TABLE_NAME + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/JoinAddressTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/JoinAddressTable.java new file mode 100644 index 000000000..0ec07a7f7 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/JoinAddressTable.java @@ -0,0 +1,44 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.sql.tables; + +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder; +import com.djrapitops.plan.storage.database.sql.building.Sql; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; + +public class JoinAddressTable { + + public static final String TABLE_NAME = "plan_join_address"; + public static final String ID = "id"; + public static final String JOIN_ADDRESS = "join_address"; + public static final String SELECT_ID = '(' + SELECT + ID + FROM + TABLE_NAME + WHERE + JOIN_ADDRESS + "=LOWER(?))"; + public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + + " (" + JOIN_ADDRESS + ") VALUES (LOWER(?))"; + public static final String DEFAULT_VALUE_FOR_LOOKUP = "unknown"; + + private JoinAddressTable() {} + + public static String createTableSQL(DBType dbType) { + return CreateTableBuilder.create(TABLE_NAME, dbType) + .column(ID, Sql.INT).primaryKey() + .column(JOIN_ADDRESS, Sql.varchar(255)).unique() + .toString(); + } + +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SessionsTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SessionsTable.java index c63820dd0..31382caf4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SessionsTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SessionsTable.java @@ -32,6 +32,7 @@ import static com.djrapitops.plan.storage.database.sql.building.Sql.*; * {@link Version10Patch} * {@link SessionAFKTimePatch} * {@link SessionsOptimizationPatch} + * {@link com.djrapitops.plan.storage.database.transactions.patches.SessionJoinAddressPatch} * * @author AuroraLS3 */ @@ -47,6 +48,7 @@ public class SessionsTable { public static final String MOB_KILLS = "mob_kills"; public static final String DEATHS = "deaths"; public static final String AFK_TIME = "afk_time"; + public static final String JOIN_ADDRESS_ID = "join_address_id"; public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + USER_ID + ',' @@ -55,8 +57,9 @@ public class SessionsTable { + DEATHS + ',' + MOB_KILLS + ',' + AFK_TIME + ',' - + SERVER_ID - + ") VALUES (" + UsersTable.SELECT_USER_ID + ", ?, ?, ?, ?, ?, " + ServerTable.SELECT_SERVER_ID + ")"; + + SERVER_ID + ',' + + JOIN_ADDRESS_ID + + ") VALUES (" + UsersTable.SELECT_USER_ID + ", ?, ?, ?, ?, ?, " + ServerTable.SELECT_SERVER_ID + ", " + JoinAddressTable.SELECT_ID + ")"; public static final String SELECT_SESSION_ID_STATEMENT = "(SELECT " + TABLE_NAME + '.' + ID + FROM + TABLE_NAME + WHERE + TABLE_NAME + '.' + USER_ID + "=" + UsersTable.SELECT_USER_ID + @@ -78,6 +81,7 @@ public class SessionsTable { .column(MOB_KILLS, Sql.INT).notNull() .column(DEATHS, Sql.INT).notNull() .column(AFK_TIME, Sql.LONG).notNull() + .column(JOIN_ADDRESS_ID, INT).notNull().defaultValue("1") // References JoinAddressTable.ID, but no foreign key to allow null values. .foreignKey(USER_ID, UsersTable.TABLE_NAME, UsersTable.ID) .foreignKey(SERVER_ID, ServerTable.TABLE_NAME, ServerTable.ID) .toString(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UserInfoTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UserInfoTable.java index 285729e70..debc96aea 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UserInfoTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UserInfoTable.java @@ -42,6 +42,10 @@ public class UserInfoTable { public static final String REGISTERED = "registered"; public static final String OP = "opped"; public static final String BANNED = "banned"; + /** + * @deprecated Join address is now stored in {@link JoinAddressTable}, this column may become unreliable in the future. + */ + @Deprecated public static final String JOIN_ADDRESS = "join_address"; public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java index cef9f3786..89fd25434 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java @@ -17,6 +17,7 @@ package com.djrapitops.plan.storage.database.transactions.commands; import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.events.StoreJoinAddressTransaction; import com.djrapitops.plan.storage.database.transactions.patches.Patch; /** @@ -39,6 +40,7 @@ public class RemoveEverythingTransaction extends Patch { clearTable(KillsTable.TABLE_NAME); clearTable(WorldTimesTable.TABLE_NAME); clearTable(SessionsTable.TABLE_NAME); + clearTable(JoinAddressTable.TABLE_NAME); clearTable(WorldTable.TABLE_NAME); clearTable(PingTable.TABLE_NAME); clearTable(UserInfoTable.TABLE_NAME); @@ -57,6 +59,8 @@ public class RemoveEverythingTransaction extends Patch { clearTable(ExtensionTabTable.TABLE_NAME); clearTable(ExtensionPluginTable.TABLE_NAME); clearTable(ExtensionIconTable.TABLE_NAME); + + executeOther(new StoreJoinAddressTransaction(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP)); } private void clearTable(String tableName) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/SessionEndTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/SessionEndTransaction.java index 72a09c7eb..2c0fa7032 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/SessionEndTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/SessionEndTransaction.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database.transactions.events; import com.djrapitops.plan.exceptions.database.DBOpException; import com.djrapitops.plan.gathering.domain.FinishedSession; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.storage.database.queries.DataStoreQueries; import com.djrapitops.plan.storage.database.transactions.Transaction; @@ -32,10 +33,6 @@ public class SessionEndTransaction extends Transaction { private final FinishedSession session; - public SessionEndTransaction(UUID playerUUID, FinishedSession session) { - this(session); - } - public SessionEndTransaction(FinishedSession session) { this.session = session; } @@ -43,19 +40,36 @@ public class SessionEndTransaction extends Transaction { @Override protected void performOperations() { try { - execute(DataStoreQueries.storeSession(session)); + storeSession(); } catch (DBOpException failed) { if (failed.isUserIdConstraintViolation()) { - retry(); + retry(failed); } else { throw failed; } } } - private void retry() { - UUID playerUUID = session.getPlayerUUID(); - executeOther(new PlayerRegisterTransaction(playerUUID, System::currentTimeMillis, playerUUID.toString())); + private void storeSession() { + storeJoinAddressIfPresent(); execute(DataStoreQueries.storeSession(session)); } + + private void storeJoinAddressIfPresent() { + session.getExtraData(JoinAddress.class) + .map(JoinAddress::getAddress) + .map(StoreJoinAddressTransaction::new) + .ifPresent(this::executeOther); + } + + private void retry(DBOpException failed) { + try { + UUID playerUUID = session.getPlayerUUID(); + executeOther(new PlayerRegisterTransaction(playerUUID, System::currentTimeMillis, playerUUID.toString())); + storeSession(); + } catch (DBOpException anotherFail) { + anotherFail.addSuppressed(failed); + throw anotherFail; + } + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/StoreJoinAddressTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/StoreJoinAddressTransaction.java new file mode 100644 index 000000000..d488fbd0c --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/StoreJoinAddressTransaction.java @@ -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 . + */ +package com.djrapitops.plan.storage.database.transactions.events; + +import com.djrapitops.plan.delivery.domain.container.CachingSupplier; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.sql.tables.JoinAddressTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Transaction; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.function.Supplier; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; + +public class StoreJoinAddressTransaction extends Transaction { + + private final Supplier joinAddress; + + public StoreJoinAddressTransaction(String joinAddress) { + this(() -> joinAddress); + } + + public StoreJoinAddressTransaction(Supplier joinAddress) { + this.joinAddress = new CachingSupplier<>(joinAddress); + } + + @Override + protected boolean shouldBeExecuted() { + return !query(hasAddressAlready()); + } + + private Query hasAddressAlready() { + String sql = SELECT + "COUNT(*) as c" + + FROM + JoinAddressTable.TABLE_NAME + + WHERE + JoinAddressTable.JOIN_ADDRESS + "=?"; + return new HasMoreThanZeroQueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + String address = joinAddress.get(); + statement.setString(1, address.toLowerCase()); + } + }; + } + + @Override + protected void performOperations() { + execute(new ExecStatement(JoinAddressTable.INSERT_STATEMENT) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, joinAddress.get().toLowerCase()); + } + }); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateIndexTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateIndexTransaction.java index 1c7e73f12..c9bb68aae 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateIndexTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateIndexTransaction.java @@ -63,6 +63,9 @@ public class CreateIndexTransaction extends Transaction { createIndex(TPSTable.TABLE_NAME, "plan_tps_date_index", TPSTable.DATE ); + + createIndex(SessionsTable.TABLE_NAME, "plan_session_join_address_index", + SessionsTable.JOIN_ADDRESS_ID); } private void createIndex(String tableName, String indexName, String... indexedColumns) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java index c43a713ef..e80be4e03 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java @@ -17,6 +17,7 @@ package com.djrapitops.plan.storage.database.transactions.init; import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.events.StoreJoinAddressTransaction; /** * Transaction that creates the table schema of Plan database. @@ -36,6 +37,8 @@ public class CreateTablesTransaction extends OperationCriticalTransaction { execute(UserInfoTable.createTableSQL(dbType)); execute(GeoInfoTable.createTableSQL(dbType)); execute(NicknamesTable.createTableSQL(dbType)); + execute(JoinAddressTable.createTableSQL(dbType)); + executeOther(new StoreJoinAddressTransaction(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP)); execute(SessionsTable.createTableSQL(dbType)); execute(KillsTable.createTableSQL(dbType)); execute(PingTable.createTableSQL(dbType)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionJoinAddressPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionJoinAddressPatch.java new file mode 100644 index 000000000..83960b425 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionJoinAddressPatch.java @@ -0,0 +1,149 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.storage.database.transactions.patches; + +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.building.Sql; +import com.djrapitops.plan.storage.database.sql.tables.JoinAddressTable; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import static com.djrapitops.plan.storage.database.sql.building.Sql.*; + +/** + * Adds join_address_id to plan_sessions, and populates latest session rows with value from plan_user_info. + * + * @author AuroraLS3 + */ +public class SessionJoinAddressPatch extends Patch { + + public static Query> uniqueJoinAddressesOld() { + String sql = SELECT + DISTINCT + "LOWER(COALESCE(" + UserInfoTable.JOIN_ADDRESS + ", ?)) as address" + + FROM + UserInfoTable.TABLE_NAME + + ORDER_BY + "address ASC"; + return new QueryStatement>(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List joinAddresses = new ArrayList<>(); + while (set.next()) joinAddresses.add(set.getString("address")); + return joinAddresses; + } + }; + } + + @Override + public boolean hasBeenApplied() { + return hasColumn(SessionsTable.TABLE_NAME, SessionsTable.JOIN_ADDRESS_ID); + } + + @Override + protected void applyPatch() { + Integer defaultAddressId = getDefaultAddressId(); + addColumn(SessionsTable.TABLE_NAME, SessionsTable.JOIN_ADDRESS_ID + ' ' + Sql.INT + " DEFAULT " + defaultAddressId); + + populateAddresses(); + populateLatestSessions(); + } + + private Integer getDefaultAddressId() { + return query(new QueryStatement(SELECT + ID + + FROM + JoinAddressTable.TABLE_NAME + + WHERE + JoinAddressTable.JOIN_ADDRESS + "=LOWER(?)") { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt(JoinAddressTable.ID) : 1; + } + }); + } + + private void populateAddresses() { + List joinAddresses = query(uniqueJoinAddressesOld()); + joinAddresses.remove(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + execute(new ExecBatchStatement(JoinAddressTable.INSERT_STATEMENT) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + for (String joinAddress : joinAddresses) { + statement.setString(1, joinAddress.toLowerCase()); + statement.addBatch(); + } + } + }); + } + + private void populateLatestSessions() { + String sql = SELECT + + "MAX(s." + SessionsTable.ID + ") as session_id," + + "j." + JoinAddressTable.ID + " as join_address_id" + + FROM + UserInfoTable.TABLE_NAME + " u" + + INNER_JOIN + SessionsTable.TABLE_NAME + " s on s." + SessionsTable.USER_ID + "=u." + UserInfoTable.USER_ID + + AND + "s." + SessionsTable.SERVER_ID + "=u." + UserInfoTable.SERVER_ID + + INNER_JOIN + JoinAddressTable.TABLE_NAME + " j on j." + JoinAddressTable.JOIN_ADDRESS + "=u." + UserInfoTable.JOIN_ADDRESS + + GROUP_BY + "u." + UserInfoTable.USER_ID + ",u." + UserInfoTable.SERVER_ID; + + Map joinAddressIdsBySessionId = query(new QueryAllStatement>(sql) { + @Override + public Map processResults(ResultSet set) throws SQLException { + Map joinAddressBySessionId = new TreeMap<>(); + while (set.next()) { + joinAddressBySessionId.put( + set.getInt("session_id"), + set.getInt("join_address_id") + ); + } + return joinAddressBySessionId; + } + }); + + String updateSql = "UPDATE " + SessionsTable.TABLE_NAME + " SET " + SessionsTable.JOIN_ADDRESS_ID + "=?" + + WHERE + SessionsTable.ID + "=?"; + + execute(new ExecBatchStatement(updateSql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + for (Map.Entry sessionIdAndJoinAddressId : joinAddressIdsBySessionId.entrySet()) { + Integer sessionId = sessionIdAndJoinAddressId.getKey(); + Integer joinAddressId = sessionIdAndJoinAddressId.getValue(); + statement.setInt(1, joinAddressId); + statement.setInt(2, sessionId); + statement.addBatch(); + } + } + }); + } + +} diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/DatabaseTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/DatabaseTest.java index 05aecf85b..b51dca612 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/DatabaseTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/DatabaseTest.java @@ -30,7 +30,10 @@ import com.djrapitops.plan.query.QuerySvc; import com.djrapitops.plan.settings.config.Config; import com.djrapitops.plan.settings.config.PlanConfig; import com.djrapitops.plan.settings.locale.Locale; -import com.djrapitops.plan.storage.database.queries.*; +import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.ServerAggregateQueries; import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries; import com.djrapitops.plan.storage.database.queries.objects.*; import com.djrapitops.plan.storage.database.queries.objects.playertable.NetworkTablePlayersQuery; @@ -101,7 +104,7 @@ public interface DatabaseTest extends DatabaseTestPreparer { FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); db().executeTransaction(new NicknameStoreTransaction(playerUUID, new Nickname("TestNick", RandomData.randomTime(), serverUUID()), (uuid, name) -> false /* Not cached */)); db().executeTransaction(new GeoInfoStoreTransaction(playerUUID, new GeoInfo("TestLoc", RandomData.randomTime()))); @@ -131,7 +134,7 @@ public interface DatabaseTest extends DatabaseTestPreparer { long sessionStart = System.currentTimeMillis(); ActiveSession session = new ActiveSession(playerUUID, serverUUID(), sessionStart, worlds[0], "SURVIVAL"); - execute(DataStoreQueries.storeSession(session.toFinishedSession(sessionStart + 22345L))); + db().executeTransaction(new SessionEndTransaction(session.toFinishedSession(sessionStart + 22345L))); TestPluginLogger logger = new TestPluginLogger(); new DBCleanTask( @@ -154,7 +157,7 @@ public interface DatabaseTest extends DatabaseTestPreparer { saveUserTwo(); saveTwoWorlds(); FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); db().executeTransaction(new NicknameStoreTransaction(playerUUID, RandomData.randomNickname(serverUUID()), (uuid, name) -> false /* Not cached */)); saveGeoInfo(playerUUID, new GeoInfo("TestLoc", RandomData.randomTime())); assertTrue(db().query(PlayerFetchQueries.isPlayerRegistered(playerUUID))); diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ActivityIndexQueriesTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ActivityIndexQueriesTest.java index 22664bbb9..dd4a79f77 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ActivityIndexQueriesTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ActivityIndexQueriesTest.java @@ -28,6 +28,7 @@ import com.djrapitops.plan.storage.database.queries.objects.playertable.ServerTa import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; import com.djrapitops.plan.storage.database.sql.tables.UsersTable; import com.djrapitops.plan.storage.database.transactions.events.PlayerServerRegisterTransaction; +import com.djrapitops.plan.storage.database.transactions.events.SessionEndTransaction; import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; @@ -59,7 +60,7 @@ public interface ActivityIndexQueriesTest extends DatabaseTestPreparer { } for (FinishedSession session : RandomData.randomSessions(serverUUID(), worlds, playerUUID, player2UUID)) { - if (save.test(session)) execute(DataStoreQueries.storeSession(session)); + if (save.test(session)) db().executeTransaction(new SessionEndTransaction(session)); } } diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/DatabaseBackupTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/DatabaseBackupTest.java index dcfd3bf79..ce5484126 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/DatabaseBackupTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/DatabaseBackupTest.java @@ -52,7 +52,7 @@ public interface DatabaseBackupTest extends DatabaseTestPreparer { TestConstants.PLAYER_TWO_NAME, serverUUID(), TestConstants.GET_PLAYER_HOSTNAME)); FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); db().executeTransaction( new NicknameStoreTransaction(playerUUID, RandomData.randomNickname(serverUUID()), (uuid, name) -> false /* Not cached */) diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ExtensionsDatabaseTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ExtensionsDatabaseTest.java index f9fa92a62..ecaefebd7 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ExtensionsDatabaseTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/ExtensionsDatabaseTest.java @@ -38,6 +38,7 @@ import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.DatabaseTestPreparer; import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction; import com.djrapitops.plan.storage.database.transactions.events.PlayerRegisterTransaction; +import com.djrapitops.plan.storage.database.transactions.events.SessionEndTransaction; import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -121,7 +122,7 @@ public interface ExtensionsDatabaseTest extends DatabaseTestPreparer { // Store a session to check against issue https://github.com/plan-player-analytics/Plan/issues/1039 ActiveSession session = new ActiveSession(playerUUID, serverUUID(), 32345L, worlds[0], "SURVIVAL"); session.getExtraData().put(WorldTimes.class, RandomData.randomWorldTimes(worlds)); - execute(DataStoreQueries.storeSession(session.toFinishedSession(42345L))); + db().executeTransaction(new SessionEndTransaction(session.toFinishedSession(42345L))); Map result = db().query(new ExtensionServerTableDataQuery(serverUUID(), 50)); assertEquals(1, result.size()); diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/JoinAddressQueriesTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/JoinAddressQueriesTest.java new file mode 100644 index 000000000..639110519 --- /dev/null +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/JoinAddressQueriesTest.java @@ -0,0 +1,210 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries; + +import com.djrapitops.plan.gathering.domain.FinishedSession; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; +import com.djrapitops.plan.storage.database.DatabaseTestPreparer; +import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries; +import com.djrapitops.plan.storage.database.queries.objects.JoinAddressQueries; +import com.djrapitops.plan.storage.database.sql.tables.JoinAddressTable; +import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction; +import com.djrapitops.plan.storage.database.transactions.events.PlayerRegisterTransaction; +import com.djrapitops.plan.storage.database.transactions.events.SessionEndTransaction; +import com.djrapitops.plan.storage.database.transactions.events.StoreJoinAddressTransaction; +import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction; +import org.junit.jupiter.api.Test; +import utilities.RandomData; +import utilities.TestConstants; +import utilities.TestData; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public interface JoinAddressQueriesTest extends DatabaseTestPreparer { + + @Test + default void removeEverythingRemovesJoinAddresses() { + String joinAddress = TestConstants.GET_PLAYER_HOSTNAME.get(); + executeTransactions(new StoreJoinAddressTransaction(joinAddress)); + + List expected = List.of(joinAddress, JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + List result = db().query(JoinAddressQueries.uniqueJoinAddresses()); + assertEquals(expected, result); + + executeTransactions(new RemoveEverythingTransaction()); + + expected = List.of(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + result = db().query(JoinAddressQueries.uniqueJoinAddresses()); + assertEquals(expected, result); + } + + @Test + default void unknownJoinAddressIsStoredInDatabaseDuringCreation() { + List expected = List.of(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + List result = db().query(JoinAddressQueries.allJoinAddresses()); + assertEquals(expected, result); + } + + @Test + default void joinAddressCanBeUnknown() { + db().executeTransaction(new WorldNameStoreTransaction(serverUUID(), worlds[0])); + db().executeTransaction(new WorldNameStoreTransaction(serverUUID(), worlds[1])); + db().executeTransaction(new PlayerRegisterTransaction(playerUUID, System::currentTimeMillis, TestConstants.PLAYER_ONE_NAME)); + + FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); + session.getExtraData().remove(JoinAddress.class); + db().executeTransaction(new SessionEndTransaction(session)); + + Set expected = Set.of(db().query(BaseUserQueries.fetchUserId(playerUUID))); + Set result = db().query(JoinAddressQueries.userIdsOfPlayersWithJoinAddresses(List.of(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP))); + + assertEquals(expected, result); + + Map expectedAddressCounts = Map.of(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP, 1); + Map resultAddressCounts = db().query(JoinAddressQueries.latestJoinAddresses()); + + assertEquals(expectedAddressCounts, resultAddressCounts); + } + + @Test + default void latestJoinAddressIsUpdatedUponSecondSession() { + joinAddressCanBeUnknown(); + + FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); + String expectedAddress = TestConstants.GET_PLAYER_HOSTNAME.get(); + session.getExtraData().put(JoinAddress.class, new JoinAddress(expectedAddress)); + db().executeTransaction(new SessionEndTransaction(session)); + + Map expected = Map.of(expectedAddress, 1); + Map result = db().query(JoinAddressQueries.latestJoinAddresses()); + + assertEquals(expected, result); + } + + @Test + default void joinAddressUpdateIsUniquePerServer() { + joinAddressCanBeUnknown(); + + db().executeTransaction(TestData.storeServers()); + + db().executeTransaction(new WorldNameStoreTransaction(TestConstants.SERVER_TWO_UUID, worlds[0])); + db().executeTransaction(new WorldNameStoreTransaction(TestConstants.SERVER_TWO_UUID, worlds[1])); + FinishedSession session = RandomData.randomSession(TestConstants.SERVER_TWO_UUID, worlds, playerUUID, player2UUID); + String expectedAddress = TestConstants.GET_PLAYER_HOSTNAME.get(); + session.getExtraData().put(JoinAddress.class, new JoinAddress(expectedAddress)); + db().executeTransaction(new SessionEndTransaction(session)); + + Map expected1 = Map.of(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP, 1); + Map result1 = db().query(JoinAddressQueries.latestJoinAddresses(serverUUID())); + + assertEquals(expected1, result1); + + Map expected2 = Map.of(expectedAddress, 1); + Map result2 = db().query(JoinAddressQueries.latestJoinAddresses(TestConstants.SERVER_TWO_UUID)); + + assertEquals(expected2, result2); + } + + @Test + default void joinAddressQueryHasNoNullValues() { + joinAddressCanBeUnknown(); + + Map expected = Collections.singletonMap(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP, 1); + Map result = db().query(JoinAddressQueries.latestJoinAddresses()); + assertEquals(expected, result); + } + + @Test + default void serverJoinAddressQueryHasNoNullValues() { + joinAddressCanBeUnknown(); + + Map expected = Collections.singletonMap(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP, 1); + Map result = db().query(JoinAddressQueries.latestJoinAddresses(serverUUID())); + assertEquals(expected, result); + } + + @Test + default void joinAddressQueryHasDistinctPlayers() { + joinAddressCanBeUnknown(); + + db().executeTransaction(TestData.storeServers()); + + FinishedSession session = RandomData.randomSession(serverUUID(), worlds, player2UUID, playerUUID); + String expectedAddress = TestConstants.GET_PLAYER_HOSTNAME.get(); + session.getExtraData().put(JoinAddress.class, new JoinAddress(expectedAddress)); + db().executeTransaction(new SessionEndTransaction(session)); + + Map expected = Map.of(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP, 1, expectedAddress, 1); + Map result = db().query(JoinAddressQueries.latestJoinAddresses()); + + assertEquals(expected, result); + } + + @Test + default void joinAddressFilterOptionsAreFetched() { + executeTransactions( + new StoreJoinAddressTransaction(TestConstants.GET_PLAYER_HOSTNAME.get()) + ); + + List expected = List.of(TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase(), JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP); + List result = db().query(JoinAddressQueries.uniqueJoinAddresses()); + assertEquals(expected, result); + } + + @Test + default void joinAddressFilterOptionsAreFetchedWhenThereAreMultiple() { + executeTransactions( + new StoreJoinAddressTransaction(TestConstants.GET_PLAYER_HOSTNAME.get()), + new StoreJoinAddressTransaction(TestConstants.GET_PLAYER_HOSTNAME.get() + "_a"), + new StoreJoinAddressTransaction(TestConstants.GET_PLAYER_HOSTNAME.get() + "_b") + ); + + List expected = Arrays.asList( + TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase(), + TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase() + "_a", + TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase() + "_b", + JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP + ); + List result = db().query(JoinAddressQueries.uniqueJoinAddresses()); + + assertEquals(expected, result); + } + + @Test + default void joinAddressFilterUUIDsAreFetched() { + latestJoinAddressIsUpdatedUponSecondSession(); + + Set expected = Set.of(db().query(BaseUserQueries.fetchUserId(playerUUID))); + Set result = db().query(JoinAddressQueries.userIdsOfPlayersWithJoinAddresses( + List.of(TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase())) + ); + assertEquals(expected, result); + } + + @Test + default void joinAddressFilterUUIDsAreFetchedWhenUnknown() { + joinAddressCanBeUnknown(); + + Set expected = Set.of(db().query(BaseUserQueries.fetchUserId(playerUUID))); + Set result = db().query(JoinAddressQueries.userIdsOfPlayersWithJoinAddresses( + List.of(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP)) + ); + assertEquals(expected, result); + } +} diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/QueriesTestAggregate.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/QueriesTestAggregate.java index 26830b521..8d4d0565f 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/QueriesTestAggregate.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/QueriesTestAggregate.java @@ -31,6 +31,7 @@ public interface QueriesTestAggregate extends TPSQueriesTest, UserInfoQueriesTest, WebUserQueriesTest, - FilterQueryTest { + FilterQueryTest, + JoinAddressQueriesTest { /* Collects all query tests together so its easier to implement database tests */ } diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/SessionQueriesTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/SessionQueriesTest.java index 4b8bec460..9d0ba4c83 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/SessionQueriesTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/SessionQueriesTest.java @@ -141,7 +141,7 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { long expectedLength = session.getLength(); long sessionEnd = session.getEnd(); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); forcePersistenceCheck(); @@ -170,7 +170,7 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { default void sessionsAreStoredWithAllData() { prepareForSessionSave(); FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); forcePersistenceCheck(); @@ -187,7 +187,7 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { default void mostRecentSessionsCanBeQueried() { prepareForSessionSave(); FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); List expected = Collections.singletonList(session); List result = db().query(SessionQueries.fetchLatestSessionsOfServer(serverUUID(), 1)); @@ -294,7 +294,7 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { prepareForSessionSave(); FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); forcePersistenceCheck(); @@ -310,7 +310,7 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); List expected = session.getExtraData(PlayerKills.class).map(PlayerKills::asList) .orElseThrow(AssertionError::new); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); forcePersistenceCheck(); @@ -330,7 +330,7 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); WorldTimes expected = session.getExtraData(WorldTimes.class).orElseThrow(AssertionError::new); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); // Fetch the session Map> sessions = db().query(SessionQueries.fetchSessionsOfPlayer(playerUUID)); @@ -377,8 +377,8 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { prepareForSessionSave(); List player1Sessions = RandomData.randomSessions(serverUUID(), worlds, playerUUID, player2UUID); List player2Sessions = RandomData.randomSessions(serverUUID(), worlds, player2UUID, playerUUID); - player1Sessions.forEach(session -> execute(DataStoreQueries.storeSession(session))); - player2Sessions.forEach(session -> execute(DataStoreQueries.storeSession(session))); + player1Sessions.forEach(session -> db().executeTransaction(new SessionEndTransaction(session))); + player2Sessions.forEach(session -> db().executeTransaction(new SessionEndTransaction(session))); long playtimeThreshold = RandomData.randomLong(TimeUnit.HOURS.toMillis(1L), TimeUnit.DAYS.toMillis(2L)); @@ -397,8 +397,8 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { prepareForSessionSave(); List player1Sessions = RandomData.randomSessions(serverUUID(), worlds, playerUUID, player2UUID); List player2Sessions = RandomData.randomSessions(serverUUID(), worlds, player2UUID, playerUUID); - player1Sessions.forEach(session -> execute(DataStoreQueries.storeSession(session))); - player2Sessions.forEach(session -> execute(DataStoreQueries.storeSession(session))); + player1Sessions.forEach(session -> db().executeTransaction(new SessionEndTransaction(session))); + player2Sessions.forEach(session -> db().executeTransaction(new SessionEndTransaction(session))); long time = System.currentTimeMillis(); long playtimeThreshold = RandomData.randomLong(TimeUnit.HOURS.toMillis(1L), TimeUnit.DAYS.toMillis(2L)); @@ -433,14 +433,14 @@ public interface SessionQueriesTest extends DatabaseTestPreparer { default void serverPreferencePieValuesAreCorrect() { prepareForSessionSave(); List server1Sessions = RandomData.randomSessions(serverUUID(), worlds, playerUUID, player2UUID); - server1Sessions.forEach(session -> execute(DataStoreQueries.storeSession(session))); + server1Sessions.forEach(session -> db().executeTransaction(new SessionEndTransaction(session))); ServerUUID serverTwoUuid = TestConstants.SERVER_TWO_UUID; executeTransactions(new StoreServerInformationTransaction(new Server(serverTwoUuid, TestConstants.SERVER_TWO_NAME, "", TestConstants.VERSION))); db().executeTransaction(new WorldNameStoreTransaction(serverTwoUuid, worlds[0])); db().executeTransaction(new WorldNameStoreTransaction(serverTwoUuid, worlds[1])); List server2Sessions = RandomData.randomSessions(serverTwoUuid, worlds, playerUUID, player2UUID); - server2Sessions.forEach(session -> execute(DataStoreQueries.storeSession(session))); + server2Sessions.forEach(session -> db().executeTransaction(new SessionEndTransaction(session))); Map expected = Maps.builder(String.class, Long.class) .put(TestConstants.SERVER_NAME, new SessionsMutator(server1Sessions).toPlaytime()) diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/UserInfoQueriesTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/UserInfoQueriesTest.java index 6c1fab3e7..624d0665e 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/UserInfoQueriesTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/UserInfoQueriesTest.java @@ -18,12 +18,10 @@ package com.djrapitops.plan.storage.database.queries; import com.djrapitops.plan.gathering.domain.BaseUser; import com.djrapitops.plan.gathering.domain.UserInfo; -import com.djrapitops.plan.identification.Server; import com.djrapitops.plan.storage.database.DatabaseTestPreparer; import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries; import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries; -import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction; import com.djrapitops.plan.storage.database.transactions.Transaction; import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction; import com.djrapitops.plan.storage.database.transactions.events.*; @@ -51,45 +49,6 @@ public interface UserInfoQueriesTest extends DatabaseTestPreparer { assertEquals(expected, userInfo); } - @Test - default void joinAddressCanBeNull() { - assertFalse(db().query(BaseUserQueries.fetchBaseUserOfPlayer(playerUUID)).isPresent()); - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, serverUUID(), () -> null)); - - Set userInfo = db().query(UserInfoQueries.fetchUserInformationOfUser(playerUUID)); - Set expected = Collections.singleton(new UserInfo(playerUUID, serverUUID(), TestConstants.REGISTER_TIME, false, null, false)); - - assertEquals(expected, userInfo); - } - - @Test - default void joinAddressIsUpdatedUponSecondLogin() { - assertFalse(db().query(BaseUserQueries.fetchBaseUserOfPlayer(playerUUID)).isPresent()); - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, serverUUID(), () -> null)); - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, serverUUID(), TestConstants.GET_PLAYER_HOSTNAME)); - - Set userInfo = db().query(UserInfoQueries.fetchUserInformationOfUser(playerUUID)); - Set expected = Collections.singleton(new UserInfo(playerUUID, serverUUID(), TestConstants.REGISTER_TIME, false, TestConstants.GET_PLAYER_HOSTNAME.get(), false)); - - assertEquals(expected, userInfo); - } - - @Test - default void joinAddressUpdateIsUniquePerServer() { - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, serverUUID(), () -> null)); - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, serverUUID(), TestConstants.GET_PLAYER_HOSTNAME)); - - db().executeTransaction(new StoreServerInformationTransaction(new Server(TestConstants.SERVER_TWO_UUID, TestConstants.SERVER_TWO_NAME, "", TestConstants.VERSION))); - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, TestConstants.SERVER_TWO_UUID, () -> "example.join.address")); - - Set userInfo = db().query(UserInfoQueries.fetchUserInformationOfUser(playerUUID)); - Set expected = new HashSet<>(Arrays.asList( - new UserInfo(playerUUID, serverUUID(), TestConstants.REGISTER_TIME, false, TestConstants.GET_PLAYER_HOSTNAME.get(), false), - new UserInfo(playerUUID, TestConstants.SERVER_TWO_UUID, TestConstants.REGISTER_TIME, false, "example.join.address", false) - )); - - assertEquals(expected, userInfo); - } @Test default void userInfoTableUpdatesBanStatus() { @@ -267,81 +226,4 @@ public interface UserInfoQueriesTest extends DatabaseTestPreparer { default void noMinimumRegisterDateIsFetchedWithNoData() { assertFalse(db().query(BaseUserQueries.minimumRegisterDate()).isPresent()); } - - @Test - default void joinAddressQueryHasNoNullValues() { - joinAddressCanBeNull(); - - Map expected = Collections.singletonMap("unknown", 1); - Map result = db().query(UserInfoQueries.joinAddresses()); - assertEquals(expected, result); - } - - @Test - default void serverJoinAddressQueryHasNoNullValues() { - joinAddressCanBeNull(); - - Map expected = Collections.singletonMap("unknown", 1); - Map result = db().query(UserInfoQueries.joinAddresses(serverUUID())); - assertEquals(expected, result); - } - - @Test - default void joinAddressQueryHasDistinctPlayers() { - db().executeTransaction(new StoreServerInformationTransaction(new Server(TestConstants.SERVER_TWO_UUID, TestConstants.SERVER_TWO_NAME, "", TestConstants.VERSION))); - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, serverUUID(), TestConstants.GET_PLAYER_HOSTNAME)); - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, TestConstants.SERVER_TWO_UUID, TestConstants.GET_PLAYER_HOSTNAME)); - - Map expected = Collections.singletonMap(TestConstants.GET_PLAYER_HOSTNAME.get(), 1); - Map result = db().query(UserInfoQueries.joinAddresses()); - assertEquals(expected, result); - } - - @Test - default void joinAddressFilterOptionsAreFetched() { - joinAddressIsUpdatedUponSecondLogin(); - - List expected = Collections.singletonList(TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase()); - List result = db().query(UserInfoQueries.uniqueJoinAddresses()); - assertEquals(expected, result); - } - - @Test - default void joinAddressFilterOptionsAreFetchedWhenThereAreMultiple() { - joinAddressIsUpdatedUponSecondLogin(); - db().executeTransaction(new StoreServerInformationTransaction(new Server(TestConstants.SERVER_TWO_UUID, TestConstants.SERVER_TWO_NAME, "", TestConstants.VERSION))); - - db().executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, serverUUID(), () -> TestConstants.GET_PLAYER_HOSTNAME.get() + "_b")); - db().executeTransaction(new PlayerServerRegisterTransaction(player2UUID, () -> TestConstants.REGISTER_TIME, TestConstants.PLAYER_ONE_NAME, TestConstants.SERVER_TWO_UUID, () -> TestConstants.GET_PLAYER_HOSTNAME.get() + "_a")); - - List expected = Arrays.asList( - TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase() + "_a", - TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase() + "_b" - ); - List result = db().query(UserInfoQueries.uniqueJoinAddresses()); - - assertEquals(expected, result); - } - - @Test - default void joinAddressFilterUUIDsAreFetched() { - joinAddressIsUpdatedUponSecondLogin(); - - Set expected = Set.of(db().query(BaseUserQueries.fetchUserId(playerUUID))); - Set result = db().query(UserInfoQueries.userIdsOfPlayersWithJoinAddresses( - Collections.singletonList(TestConstants.GET_PLAYER_HOSTNAME.get().toLowerCase())) - ); - assertEquals(expected, result); - } - - @Test - default void joinAddressFilterUUIDsAreFetchedWhenUnknown() { - joinAddressCanBeNull(); - - Set expected = Set.of(db().query(BaseUserQueries.fetchUserId(playerUUID))); - Set result = db().query(UserInfoQueries.userIdsOfPlayersWithJoinAddresses( - Collections.singletonList("unknown")) - ); - assertEquals(expected, result); - } } diff --git a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/analysis/TopListQueriesTest.java b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/analysis/TopListQueriesTest.java index 702651f1d..34437b8f2 100644 --- a/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/analysis/TopListQueriesTest.java +++ b/Plan/common/src/test/java/com/djrapitops/plan/storage/database/queries/analysis/TopListQueriesTest.java @@ -18,8 +18,8 @@ package com.djrapitops.plan.storage.database.queries.analysis; import com.djrapitops.plan.gathering.domain.FinishedSession; import com.djrapitops.plan.storage.database.DatabaseTestPreparer; -import com.djrapitops.plan.storage.database.queries.DataStoreQueries; import com.djrapitops.plan.storage.database.transactions.events.PlayerServerRegisterTransaction; +import com.djrapitops.plan.storage.database.transactions.events.SessionEndTransaction; import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction; import org.junit.jupiter.api.Test; import utilities.RandomData; @@ -37,7 +37,7 @@ public interface TopListQueriesTest extends DatabaseTestPreparer { db().executeTransaction(new PlayerServerRegisterTransaction(player2UUID, RandomData::randomTime, TestConstants.PLAYER_TWO_NAME, serverUUID(), TestConstants.GET_PLAYER_HOSTNAME)); FinishedSession session = RandomData.randomSession(serverUUID(), worlds, playerUUID, player2UUID); - execute(DataStoreQueries.storeSession(session)); + db().executeTransaction(new SessionEndTransaction(session)); } @Test diff --git a/Plan/common/src/test/java/utilities/RandomData.java b/Plan/common/src/test/java/utilities/RandomData.java index 30a95d268..d7847926b 100644 --- a/Plan/common/src/test/java/utilities/RandomData.java +++ b/Plan/common/src/test/java/utilities/RandomData.java @@ -20,6 +20,7 @@ import com.djrapitops.plan.delivery.domain.DateObj; import com.djrapitops.plan.delivery.domain.Nickname; import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.sql.tables.KillsTable; import org.apache.commons.lang3.RandomStringUtils; @@ -28,6 +29,8 @@ import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public class RandomData { @@ -37,6 +40,9 @@ public class RandomData { private static final Random r = new Random(); + private static final int JOIN_ADDRESS_COUNT = 50; + private static final List JOIN_ADDRESSES = generateJoinAddresses(JOIN_ADDRESS_COUNT); + public static int randomInt(int rangeStart, int rangeEnd) { return ThreadLocalRandom.current().nextInt(rangeStart, rangeEnd); } @@ -117,6 +123,7 @@ public class RandomData { } extraData.put(MobKillCounter.class, new MobKillCounter()); extraData.put(DeathCounter.class, new DeathCounter()); + extraData.put(JoinAddress.class, JOIN_ADDRESSES.get(randomInt(0, JOIN_ADDRESS_COUNT))); return new FinishedSession( uuids[0], serverUUID, start, end, RandomData.randomLong(0, end - start), @@ -197,4 +204,10 @@ public class RandomData { public static double randomDouble() { return ThreadLocalRandom.current().nextDouble(); } + + public static List generateJoinAddresses(int n) { + return IntStream.range(0, n).mapToObj(i -> "join_address_" + i) + .map(JoinAddress::new) + .collect(Collectors.toList()); + } } diff --git a/Plan/fabric/src/main/java/net/playeranalytics/plan/gathering/listeners/fabric/PlayerOnlineListener.java b/Plan/fabric/src/main/java/net/playeranalytics/plan/gathering/listeners/fabric/PlayerOnlineListener.java index 49bc7712f..cd03926cd 100644 --- a/Plan/fabric/src/main/java/net/playeranalytics/plan/gathering/listeners/fabric/PlayerOnlineListener.java +++ b/Plan/fabric/src/main/java/net/playeranalytics/plan/gathering/listeners/fabric/PlayerOnlineListener.java @@ -25,6 +25,7 @@ import com.djrapitops.plan.extension.ExtensionSvc; import com.djrapitops.plan.gathering.cache.NicknameCache; import com.djrapitops.plan.gathering.cache.SessionCache; import com.djrapitops.plan.gathering.domain.ActiveSession; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.gathering.geolocation.GeolocationCache; import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.identification.ServerUUID; @@ -139,7 +140,9 @@ public class PlayerOnlineListener implements FabricListener { ServerUUID serverUUID = serverInfo.getServerUUID(); String joinAddress = address.toString(); if (!joinAddress.isEmpty()) { - joinAddresses.put(playerUUID, joinAddress.substring(0, joinAddress.lastIndexOf(':'))); + joinAddress = joinAddress.substring(0, joinAddress.lastIndexOf(':')); + joinAddresses.put(playerUUID, joinAddress); + dbSystem.getDatabase().executeTransaction(new StoreJoinAddressTransaction(joinAddress)); } dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> banned)); } catch (Exception e) { @@ -201,6 +204,7 @@ public class PlayerOnlineListener implements FabricListener { ActiveSession session = new ActiveSession(playerUUID, serverUUID, time, world, gm); session.getExtraData().put(PlayerName.class, new PlayerName(playerName)); session.getExtraData().put(ServerName.class, new ServerName(serverInfo.getServer().getIdentifiableName())); + session.getExtraData().put(JoinAddress.class, new JoinAddress(getHostName)); sessionCache.cacheSession(playerUUID, session) .ifPresent(previousSession -> database.executeTransaction(new SessionEndTransaction(previousSession))); diff --git a/Plan/nukkit/src/main/java/com/djrapitops/plan/gathering/listeners/nukkit/PlayerOnlineListener.java b/Plan/nukkit/src/main/java/com/djrapitops/plan/gathering/listeners/nukkit/PlayerOnlineListener.java index f59165c1f..9f0778707 100644 --- a/Plan/nukkit/src/main/java/com/djrapitops/plan/gathering/listeners/nukkit/PlayerOnlineListener.java +++ b/Plan/nukkit/src/main/java/com/djrapitops/plan/gathering/listeners/nukkit/PlayerOnlineListener.java @@ -184,7 +184,8 @@ public class PlayerOnlineListener implements Listener { session.getExtraData().put(PlayerName.class, new PlayerName(playerName)); session.getExtraData().put(ServerName.class, new ServerName(serverInfo.getServer().getIdentifiableName())); sessionCache.cacheSession(playerUUID, session) - .ifPresent(previousSession -> database.executeTransaction(new SessionEndTransaction(previousSession))); + .map(SessionEndTransaction::new) + .ifPresent(database::executeTransaction); database.executeTransaction(new NicknameStoreTransaction( playerUUID, new Nickname(displayName, time, serverUUID), diff --git a/Plan/sponge/src/main/java/com/djrapitops/plan/gathering/listeners/sponge/PlayerOnlineListener.java b/Plan/sponge/src/main/java/com/djrapitops/plan/gathering/listeners/sponge/PlayerOnlineListener.java index acd08447a..f95f5bc17 100644 --- a/Plan/sponge/src/main/java/com/djrapitops/plan/gathering/listeners/sponge/PlayerOnlineListener.java +++ b/Plan/sponge/src/main/java/com/djrapitops/plan/gathering/listeners/sponge/PlayerOnlineListener.java @@ -25,6 +25,7 @@ import com.djrapitops.plan.extension.ExtensionSvc; import com.djrapitops.plan.gathering.cache.NicknameCache; import com.djrapitops.plan.gathering.cache.SessionCache; import com.djrapitops.plan.gathering.domain.ActiveSession; +import com.djrapitops.plan.gathering.domain.event.JoinAddress; import com.djrapitops.plan.gathering.geolocation.GeolocationCache; import com.djrapitops.plan.gathering.listeners.Status; import com.djrapitops.plan.identification.ServerInfo; @@ -179,12 +180,15 @@ public class PlayerOnlineListener { new GeoInfoStoreTransaction(playerUUID, address, time, geolocationCache::getCountry) ); } + database.executeTransaction(new StoreJoinAddressTransaction(getHostName)); ActiveSession session = new ActiveSession(playerUUID, serverUUID, time, world, gm); session.getExtraData().put(PlayerName.class, new PlayerName(playerName)); session.getExtraData().put(ServerName.class, new ServerName(serverInfo.getServer().getIdentifiableName())); + session.getExtraData().put(JoinAddress.class, new JoinAddress(getHostName)); sessionCache.cacheSession(playerUUID, session) - .ifPresent(previousSession -> database.executeTransaction(new SessionEndTransaction(previousSession))); + .map(SessionEndTransaction::new) + .ifPresent(database::executeTransaction); database.executeTransaction(new NicknameStoreTransaction( playerUUID, new Nickname(displayName, time, serverUUID),