mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-21 15:41:24 +01:00
Optimized (memory) /server players table query
This commit is contained in:
parent
54f98c9fab
commit
2412f5b9d5
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Player Analytics (Plan).
|
||||||
|
*
|
||||||
|
* Plan is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Plan is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.djrapitops.plan.delivery.domain;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
|
||||||
|
import com.djrapitops.plan.gathering.domain.BaseUser;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a player displayed on a player table on players tab or /players page.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class TablePlayer implements Comparable<TablePlayer> {
|
||||||
|
|
||||||
|
private UUID uuid;
|
||||||
|
private String name;
|
||||||
|
private ActivityIndex activityIndex;
|
||||||
|
private Long playtime;
|
||||||
|
private Integer sessionCount;
|
||||||
|
private Long registered;
|
||||||
|
private Long lastSeen;
|
||||||
|
private String geolocation;
|
||||||
|
|
||||||
|
private boolean banned = false;
|
||||||
|
|
||||||
|
private TablePlayer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TablePlayer.Builder builder() {
|
||||||
|
return new TablePlayer.Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TablePlayer.Builder builderFromBaseUser(BaseUser baseUser) {
|
||||||
|
return new TablePlayer.Builder()
|
||||||
|
.uuid(baseUser.getUuid())
|
||||||
|
.name(baseUser.getName())
|
||||||
|
.registered(baseUser.getRegistered());
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getPlayerUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getName() {
|
||||||
|
return Optional.ofNullable(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ActivityIndex> getCurrentActivityIndex() {
|
||||||
|
return Optional.ofNullable(activityIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getPlaytime() {
|
||||||
|
return Optional.ofNullable(playtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getSessionCount() {
|
||||||
|
return Optional.ofNullable(sessionCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getRegistered() {
|
||||||
|
return Optional.ofNullable(registered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getLastSeen() {
|
||||||
|
return Optional.ofNullable(lastSeen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getGeolocation() {
|
||||||
|
return Optional.ofNullable(geolocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBanned() {
|
||||||
|
return banned;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(TablePlayer other) {
|
||||||
|
// Most recent first
|
||||||
|
return Long.compare(
|
||||||
|
other.lastSeen != null ? other.lastSeen : 0L,
|
||||||
|
this.lastSeen != null ? this.lastSeen : 0L
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof TablePlayer)) return false;
|
||||||
|
TablePlayer that = (TablePlayer) o;
|
||||||
|
return playtime == that.playtime &&
|
||||||
|
sessionCount == that.sessionCount &&
|
||||||
|
registered == that.registered &&
|
||||||
|
lastSeen == that.lastSeen &&
|
||||||
|
name.equals(that.name) &&
|
||||||
|
activityIndex.equals(that.activityIndex) &&
|
||||||
|
geolocation.equals(that.geolocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(name, activityIndex, playtime, sessionCount, registered, lastSeen, geolocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private final TablePlayer player;
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
player = new TablePlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getPlayerUUID() {
|
||||||
|
return player.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder uuid(UUID playerUUID) {
|
||||||
|
player.uuid = playerUUID;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder name(String name) {
|
||||||
|
player.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder banned() {
|
||||||
|
player.banned = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder activityIndex(ActivityIndex activityIndex) {
|
||||||
|
player.activityIndex = activityIndex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder playtime(long playtime) {
|
||||||
|
player.playtime = playtime;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder sessionCount(int count) {
|
||||||
|
player.sessionCount = count;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder registered(long registered) {
|
||||||
|
player.registered = registered;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder lastSeen(long lastSeen) {
|
||||||
|
player.lastSeen = lastSeen;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder geolocation(String geolocation) {
|
||||||
|
player.geolocation = geolocation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TablePlayer build() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,8 +37,6 @@ import com.djrapitops.plan.settings.config.paths.TimeSettings;
|
|||||||
import com.djrapitops.plan.storage.database.DBSystem;
|
import com.djrapitops.plan.storage.database.DBSystem;
|
||||||
import com.djrapitops.plan.storage.database.Database;
|
import com.djrapitops.plan.storage.database.Database;
|
||||||
import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries;
|
import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries;
|
||||||
import com.djrapitops.plan.storage.database.queries.containers.AllPlayerContainersQuery;
|
|
||||||
import com.djrapitops.plan.storage.database.queries.containers.ServerPlayersTableContainersQuery;
|
|
||||||
import com.djrapitops.plan.storage.database.queries.objects.*;
|
import com.djrapitops.plan.storage.database.queries.objects.*;
|
||||||
import com.djrapitops.plan.utilities.comparators.SessionStartComparator;
|
import com.djrapitops.plan.utilities.comparators.SessionStartComparator;
|
||||||
|
|
||||||
@ -84,9 +82,9 @@ public class JSONFactory {
|
|||||||
Database database = dbSystem.getDatabase();
|
Database database = dbSystem.getDatabase();
|
||||||
|
|
||||||
return new PlayersTableJSONParser(
|
return new PlayersTableJSONParser(
|
||||||
database.query(new ServerPlayersTableContainersQuery(serverUUID)),
|
database.query(new ServerTablePlayersQuery(serverUUID, System.currentTimeMillis(), playtimeThreshold, xMostRecentPlayers)),
|
||||||
database.query(new ExtensionServerPlayerDataTableQuery(serverUUID, xMostRecentPlayers)),
|
database.query(new ExtensionServerPlayerDataTableQuery(serverUUID, xMostRecentPlayers)),
|
||||||
xMostRecentPlayers, playtimeThreshold, openPlayerLinksInNewTab,
|
openPlayerLinksInNewTab,
|
||||||
formatters
|
formatters
|
||||||
).toJSONString();
|
).toJSONString();
|
||||||
}
|
}
|
||||||
@ -99,9 +97,9 @@ public class JSONFactory {
|
|||||||
Database database = dbSystem.getDatabase();
|
Database database = dbSystem.getDatabase();
|
||||||
|
|
||||||
return new PlayersTableJSONParser(
|
return new PlayersTableJSONParser(
|
||||||
database.query(new AllPlayerContainersQuery()), // TODO Optimize the heck out of this
|
Collections.emptyList(),// TODO Replace with new query
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
xMostRecentPlayers, playtimeThreshold, openPlayerLinksInNewTab,
|
openPlayerLinksInNewTab,
|
||||||
formatters
|
formatters
|
||||||
).toJSONString();
|
).toJSONString();
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.delivery.rendering.json;
|
package com.djrapitops.plan.delivery.rendering.json;
|
||||||
|
|
||||||
import com.djrapitops.plan.delivery.domain.container.PlayerContainer;
|
import com.djrapitops.plan.delivery.domain.TablePlayer;
|
||||||
import com.djrapitops.plan.delivery.domain.keys.PlayerKeys;
|
|
||||||
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
|
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
|
||||||
import com.djrapitops.plan.delivery.domain.mutators.GeoInfoMutator;
|
|
||||||
import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator;
|
|
||||||
import com.djrapitops.plan.delivery.formatting.Formatter;
|
import com.djrapitops.plan.delivery.formatting.Formatter;
|
||||||
import com.djrapitops.plan.delivery.formatting.Formatters;
|
import com.djrapitops.plan.delivery.formatting.Formatters;
|
||||||
import com.djrapitops.plan.delivery.rendering.html.Html;
|
import com.djrapitops.plan.delivery.rendering.html.Html;
|
||||||
@ -29,8 +26,6 @@ import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
|
|||||||
import com.djrapitops.plan.extension.FormatType;
|
import com.djrapitops.plan.extension.FormatType;
|
||||||
import com.djrapitops.plan.extension.icon.Color;
|
import com.djrapitops.plan.extension.icon.Color;
|
||||||
import com.djrapitops.plan.extension.implementation.results.*;
|
import com.djrapitops.plan.extension.implementation.results.*;
|
||||||
import com.djrapitops.plan.gathering.domain.GeoInfo;
|
|
||||||
import com.djrapitops.plan.utilities.comparators.PlayerContainerLastPlayedComparator;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -43,12 +38,10 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class PlayersTableJSONParser {
|
public class PlayersTableJSONParser {
|
||||||
|
|
||||||
private final List<PlayerContainer> players;
|
private final List<TablePlayer> players;
|
||||||
private final List<ExtensionDescriptive> extensionDescriptives;
|
private final List<ExtensionDescriptive> extensionDescriptives;
|
||||||
private final Map<UUID, ExtensionTabData> extensionData;
|
private final Map<UUID, ExtensionTabData> extensionData;
|
||||||
|
|
||||||
private final int maxPlayers;
|
|
||||||
private final long activeMsThreshold;
|
|
||||||
private final boolean openPlayerPageInNewTab;
|
private final boolean openPlayerPageInNewTab;
|
||||||
|
|
||||||
private Map<FormatType, Formatter<Long>> numberFormatters;
|
private Map<FormatType, Formatter<Long>> numberFormatters;
|
||||||
@ -57,10 +50,10 @@ public class PlayersTableJSONParser {
|
|||||||
|
|
||||||
public PlayersTableJSONParser(
|
public PlayersTableJSONParser(
|
||||||
// Data
|
// Data
|
||||||
List<PlayerContainer> players,
|
List<TablePlayer> players,
|
||||||
Map<UUID, ExtensionTabData> extensionData,
|
Map<UUID, ExtensionTabData> extensionData,
|
||||||
// Settings
|
// Settings
|
||||||
int maxPlayers, long activeMsThreshold, boolean openPlayerPageInNewTab,
|
boolean openPlayerPageInNewTab,
|
||||||
// Formatters
|
// Formatters
|
||||||
Formatters formatters
|
Formatters formatters
|
||||||
) {
|
) {
|
||||||
@ -73,8 +66,6 @@ public class PlayersTableJSONParser {
|
|||||||
extensionDescriptives.sort((one, two) -> String.CASE_INSENSITIVE_ORDER.compare(one.getName(), two.getName()));
|
extensionDescriptives.sort((one, two) -> String.CASE_INSENSITIVE_ORDER.compare(one.getName(), two.getName()));
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
this.maxPlayers = maxPlayers;
|
|
||||||
this.activeMsThreshold = activeMsThreshold;
|
|
||||||
this.openPlayerPageInNewTab = openPlayerPageInNewTab;
|
this.openPlayerPageInNewTab = openPlayerPageInNewTab;
|
||||||
// Formatters
|
// Formatters
|
||||||
numberFormatters = new EnumMap<>(FormatType.class);
|
numberFormatters = new EnumMap<>(FormatType.class);
|
||||||
@ -107,15 +98,9 @@ public class PlayersTableJSONParser {
|
|||||||
private String parseData() {
|
private String parseData() {
|
||||||
StringBuilder dataJSON = new StringBuilder("[");
|
StringBuilder dataJSON = new StringBuilder("[");
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
players.sort(new PlayerContainerLastPlayedComparator());
|
|
||||||
|
|
||||||
int currentPlayerNumber = 0;
|
int currentPlayerNumber = 0;
|
||||||
for (PlayerContainer player : players) {
|
for (TablePlayer player : players) {
|
||||||
if (currentPlayerNumber >= maxPlayers) {
|
UUID playerUUID = player.getPlayerUUID();
|
||||||
break;
|
|
||||||
}
|
|
||||||
UUID playerUUID = player.getValue(PlayerKeys.UUID).orElse(null);
|
|
||||||
if (playerUUID == null) {
|
if (playerUUID == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -125,7 +110,7 @@ public class PlayersTableJSONParser {
|
|||||||
}
|
}
|
||||||
dataJSON.append('{'); // Start new item
|
dataJSON.append('{'); // Start new item
|
||||||
|
|
||||||
appendPlayerData(dataJSON, now, player);
|
appendPlayerData(dataJSON, player);
|
||||||
appendExtensionData(dataJSON, extensionData.getOrDefault(playerUUID, new ExtensionTabData.Factory(null).build()));
|
appendExtensionData(dataJSON, extensionData.getOrDefault(playerUUID, new ExtensionTabData.Factory(null).build()));
|
||||||
|
|
||||||
dataJSON.append('}'); // Close new item
|
dataJSON.append('}'); // Close new item
|
||||||
@ -135,22 +120,21 @@ public class PlayersTableJSONParser {
|
|||||||
return dataJSON.append(']').toString();
|
return dataJSON.append(']').toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendPlayerData(StringBuilder dataJSON, long now, PlayerContainer player) {
|
private void appendPlayerData(StringBuilder dataJSON, TablePlayer player) {
|
||||||
String name = player.getValue(PlayerKeys.NAME).orElse("Unknown");
|
String name = player.getName().orElse(player.getPlayerUUID().toString());
|
||||||
String url = "../player/" + name;
|
String url = "../player/" + name;
|
||||||
|
|
||||||
SessionsMutator sessionsMutator = SessionsMutator.forContainer(player);
|
int loginTimes = player.getSessionCount().orElse(0);
|
||||||
int loginTimes = sessionsMutator.count();
|
long playtime = player.getPlaytime().orElse(-1L);
|
||||||
long playtime = sessionsMutator.toPlaytime();
|
long registered = player.getRegistered().orElse(-1L);
|
||||||
long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L);
|
long lastSeen = player.getLastSeen().orElse(-1L);
|
||||||
long lastSeen = sessionsMutator.toLastSeen();
|
|
||||||
|
|
||||||
ActivityIndex activityIndex = player.getActivityIndex(now, activeMsThreshold);
|
ActivityIndex activityIndex = player.getCurrentActivityIndex().orElseGet(() -> new ActivityIndex(0.0, 0));
|
||||||
boolean isBanned = player.getValue(PlayerKeys.BANNED).orElse(false);
|
boolean isBanned = player.isBanned();
|
||||||
String activityString = activityIndex.getFormattedValue(decimalFormatter)
|
String activityString = activityIndex.getFormattedValue(decimalFormatter)
|
||||||
+ (isBanned ? " (<b>Banned</b>)" : " (" + activityIndex.getGroup() + ")");
|
+ (isBanned ? " (<b>Banned</b>)" : " (" + activityIndex.getGroup() + ")");
|
||||||
|
|
||||||
String geolocation = GeoInfoMutator.forContainer(player).mostRecent().map(GeoInfo::getGeolocation).orElse("-");
|
String geolocation = player.getGeolocation().orElse("-");
|
||||||
|
|
||||||
Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK;
|
Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK;
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ public class WorldAliasSettings {
|
|||||||
String worldName = entry.getKey();
|
String worldName = entry.getKey();
|
||||||
long playtime = entry.getValue();
|
long playtime = entry.getValue();
|
||||||
|
|
||||||
if (!aliases.contains(worldName)) {
|
if (worldName != null && !aliases.contains(worldName)) {
|
||||||
addWorld(worldName);
|
addWorld(worldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*;
|
import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*;
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ public class ActivityIndexQueries {
|
|||||||
return fetchActivityGroupCount(date, serverUUID, playtimeThreshold, ActivityIndex.REGULAR, 5.1);
|
return fetchActivityGroupCount(date, serverUUID, playtimeThreshold, ActivityIndex.REGULAR, 5.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String selectActivityIndexSQL() {
|
public static String selectActivityIndexSQL() {
|
||||||
String selectActivePlaytimeSQL = SELECT +
|
String selectActivePlaytimeSQL = SELECT +
|
||||||
SessionsTable.USER_UUID +
|
SessionsTable.USER_UUID +
|
||||||
",SUM(" +
|
",SUM(" +
|
||||||
@ -93,7 +94,7 @@ public class ActivityIndexQueries {
|
|||||||
GROUP_BY + "q1." + SessionsTable.USER_UUID;
|
GROUP_BY + "q1." + SessionsTable.USER_UUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setSelectActivityIndexSQLParameters(PreparedStatement statement, int index, long playtimeThreshold, UUID serverUUID, long date) throws SQLException {
|
public static void setSelectActivityIndexSQLParameters(PreparedStatement statement, int index, long playtimeThreshold, UUID serverUUID, long date) throws SQLException {
|
||||||
statement.setDouble(index, Math.PI);
|
statement.setDouble(index, Math.PI);
|
||||||
statement.setLong(index + 1, playtimeThreshold);
|
statement.setLong(index + 1, playtimeThreshold);
|
||||||
|
|
||||||
@ -477,4 +478,28 @@ public class ActivityIndexQueries {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Query<Object> activityIndexOnServerToMap(UUID serverUUID, long date, long threshold, BiConsumer<UUID, Double> consumer) {
|
||||||
|
String sql = SELECT + "activity_index,n." + UserInfoTable.USER_UUID +
|
||||||
|
FROM + UserInfoTable.TABLE_NAME + " n" +
|
||||||
|
LEFT_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + UserInfoTable.USER_UUID +
|
||||||
|
WHERE + UserInfoTable.SERVER_UUID + "=?";
|
||||||
|
|
||||||
|
return new QueryStatement<Object>(sql, 1000) {
|
||||||
|
@Override
|
||||||
|
public void prepare(PreparedStatement statement) throws SQLException {
|
||||||
|
setSelectActivityIndexSQLParameters(statement, 1, threshold, serverUUID, date);
|
||||||
|
statement.setString(2, serverUUID.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object processResults(ResultSet set) throws SQLException {
|
||||||
|
while (set.next()) {
|
||||||
|
double activityIndex = set.getDouble("activity_index"); // Returns 0.0 if missing
|
||||||
|
consumer.accept(UUID.fromString(set.getString(UserInfoTable.USER_UUID)), activityIndex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Player Analytics (Plan).
|
|
||||||
*
|
|
||||||
* Plan is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Plan is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.djrapitops.plan.storage.database.queries.containers;
|
|
||||||
|
|
||||||
import com.djrapitops.plan.delivery.domain.container.PlayerContainer;
|
|
||||||
import com.djrapitops.plan.delivery.domain.keys.PlayerKeys;
|
|
||||||
import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator;
|
|
||||||
import com.djrapitops.plan.gathering.domain.BaseUser;
|
|
||||||
import com.djrapitops.plan.gathering.domain.GeoInfo;
|
|
||||||
import com.djrapitops.plan.gathering.domain.Session;
|
|
||||||
import com.djrapitops.plan.storage.database.SQLDB;
|
|
||||||
import com.djrapitops.plan.storage.database.queries.Query;
|
|
||||||
import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries;
|
|
||||||
import com.djrapitops.plan.storage.database.queries.objects.GeoInfoQueries;
|
|
||||||
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
|
|
||||||
import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optimized version of {@link ServerPlayerContainersQuery} for /server page Players table.
|
|
||||||
*
|
|
||||||
* @author Rsl1122
|
|
||||||
* @see com.djrapitops.plan.delivery.rendering.json.PlayersTableJSONParser For what needs to be included.
|
|
||||||
*/
|
|
||||||
public class ServerPlayersTableContainersQuery implements Query<List<PlayerContainer>> {
|
|
||||||
|
|
||||||
private final UUID serverUUID;
|
|
||||||
|
|
||||||
public ServerPlayersTableContainersQuery(UUID serverUUID) {
|
|
||||||
this.serverUUID = serverUUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<PlayerContainer> executeQuery(SQLDB db) {
|
|
||||||
List<PlayerContainer> containers = new ArrayList<>();
|
|
||||||
|
|
||||||
Collection<BaseUser> baseUsers = db.query(BaseUserQueries.fetchServerBaseUsers(serverUUID));
|
|
||||||
|
|
||||||
Map<UUID, List<GeoInfo>> geoInformation = db.query(GeoInfoQueries.fetchServerGeoInformation(serverUUID));
|
|
||||||
// TODO Optimize the heck out of this
|
|
||||||
Map<UUID, List<Session>> sessions = db.query(SessionQueries.fetchSessionsOfServer(serverUUID));
|
|
||||||
Set<UUID> bannedUsers = db.query(UserInfoQueries.fetchBannedUUIDsOfServer(serverUUID));
|
|
||||||
|
|
||||||
for (BaseUser user : baseUsers) {
|
|
||||||
PlayerContainer container = new PlayerContainer();
|
|
||||||
|
|
||||||
// BaseUser
|
|
||||||
UUID uuid = user.getUuid();
|
|
||||||
container.putRawData(PlayerKeys.UUID, uuid);
|
|
||||||
container.putRawData(PlayerKeys.NAME, user.getName());
|
|
||||||
container.putRawData(PlayerKeys.REGISTERED, user.getRegistered());
|
|
||||||
container.putRawData(PlayerKeys.BANNED, bannedUsers.contains(uuid));
|
|
||||||
|
|
||||||
// GeoInfo
|
|
||||||
container.putRawData(PlayerKeys.GEO_INFO, geoInformation.getOrDefault(uuid, new ArrayList<>()));
|
|
||||||
|
|
||||||
container.putCachingSupplier(PlayerKeys.SESSIONS, () -> {
|
|
||||||
List<Session> playerSessions = sessions.getOrDefault(uuid, new ArrayList<>());
|
|
||||||
container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add);
|
|
||||||
return playerSessions;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
container.putCachingSupplier(PlayerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen());
|
|
||||||
|
|
||||||
containers.add(container);
|
|
||||||
}
|
|
||||||
return containers;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Player Analytics (Plan).
|
||||||
|
*
|
||||||
|
* Plan is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Plan is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.djrapitops.plan.storage.database.queries.objects;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.delivery.domain.TablePlayer;
|
||||||
|
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
|
||||||
|
import com.djrapitops.plan.storage.database.SQLDB;
|
||||||
|
import com.djrapitops.plan.storage.database.queries.Query;
|
||||||
|
import com.djrapitops.plan.storage.database.queries.QueryStatement;
|
||||||
|
import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQueries;
|
||||||
|
import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable;
|
||||||
|
import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
|
||||||
|
import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable;
|
||||||
|
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for displaying players on /server page players tab.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class ServerTablePlayersQuery implements Query<List<TablePlayer>> {
|
||||||
|
|
||||||
|
private final UUID serverUUID;
|
||||||
|
private final long date;
|
||||||
|
private final long activeMsThreshold;
|
||||||
|
private final int xMostRecentPlayers;
|
||||||
|
|
||||||
|
public ServerTablePlayersQuery(UUID serverUUID, long date, long activeMsThreshold, int xMostRecentPlayers) {
|
||||||
|
this.serverUUID = serverUUID;
|
||||||
|
this.date = date;
|
||||||
|
this.activeMsThreshold = activeMsThreshold;
|
||||||
|
this.xMostRecentPlayers = xMostRecentPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TablePlayer> executeQuery(SQLDB db) {
|
||||||
|
String selectGeolocations = SELECT +
|
||||||
|
GeoInfoTable.USER_UUID + ", " +
|
||||||
|
GeoInfoTable.GEOLOCATION + ", " +
|
||||||
|
GeoInfoTable.LAST_USED +
|
||||||
|
FROM + GeoInfoTable.TABLE_NAME;
|
||||||
|
String selectLatestGeolocationDate = SELECT +
|
||||||
|
GeoInfoTable.USER_UUID + ", " +
|
||||||
|
"MAX(" + GeoInfoTable.LAST_USED + ") as last_used_g" +
|
||||||
|
FROM + GeoInfoTable.TABLE_NAME +
|
||||||
|
GROUP_BY + GeoInfoTable.USER_UUID;
|
||||||
|
String selectLatestGeolocations = SELECT +
|
||||||
|
"g1." + GeoInfoTable.GEOLOCATION + ',' +
|
||||||
|
"g1." + GeoInfoTable.USER_UUID +
|
||||||
|
FROM + "(" + selectGeolocations + ") AS g1" +
|
||||||
|
INNER_JOIN + "(" + selectLatestGeolocationDate + ") AS g2 ON g1.uuid = g2.uuid" +
|
||||||
|
WHERE + GeoInfoTable.LAST_USED + "=last_used_g";
|
||||||
|
|
||||||
|
String selectSessionData = SELECT + "s." + SessionsTable.USER_UUID + ',' +
|
||||||
|
"MAX(" + SessionsTable.SESSION_END + ") as last_seen," +
|
||||||
|
"COUNT(1) as count," +
|
||||||
|
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
|
||||||
|
FROM + SessionsTable.TABLE_NAME + " s" +
|
||||||
|
WHERE + "s." + SessionsTable.SERVER_UUID + "=?" +
|
||||||
|
GROUP_BY + "s." + SessionsTable.USER_UUID;
|
||||||
|
|
||||||
|
String selectBaseUsers = SELECT +
|
||||||
|
"u." + UsersTable.USER_UUID + ',' +
|
||||||
|
"u." + UsersTable.USER_NAME + ',' +
|
||||||
|
"u." + UsersTable.REGISTERED + ',' +
|
||||||
|
UserInfoTable.BANNED + ',' +
|
||||||
|
"geoloc." + GeoInfoTable.GEOLOCATION + ',' +
|
||||||
|
"ses.last_seen," +
|
||||||
|
"ses.count," +
|
||||||
|
"ses.playtime," +
|
||||||
|
"act.activity_index" +
|
||||||
|
FROM + UsersTable.TABLE_NAME + " u" +
|
||||||
|
INNER_JOIN + UserInfoTable.TABLE_NAME + " on u." + UsersTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + '.' + UserInfoTable.USER_UUID +
|
||||||
|
LEFT_JOIN + '(' + selectLatestGeolocations + ") geoloc on geoloc." + GeoInfoTable.USER_UUID + "=u." + UsersTable.USER_UUID +
|
||||||
|
LEFT_JOIN + '(' + selectSessionData + ") ses on ses." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID +
|
||||||
|
LEFT_JOIN + '(' + ActivityIndexQueries.selectActivityIndexSQL() + ") act on u." + SessionsTable.USER_UUID + "=act." + UserInfoTable.USER_UUID +
|
||||||
|
WHERE + UserInfoTable.SERVER_UUID + "=?" +
|
||||||
|
ORDER_BY + "ses.last_seen DESC LIMIT ?";
|
||||||
|
|
||||||
|
return db.query(new QueryStatement<List<TablePlayer>>(selectBaseUsers, 1000) {
|
||||||
|
@Override
|
||||||
|
public void prepare(PreparedStatement statement) throws SQLException {
|
||||||
|
statement.setString(1, serverUUID.toString()); // Session query
|
||||||
|
ActivityIndexQueries.setSelectActivityIndexSQLParameters(statement, 2, activeMsThreshold, serverUUID, date);
|
||||||
|
statement.setString(13, serverUUID.toString()); // Session query
|
||||||
|
statement.setInt(14, xMostRecentPlayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TablePlayer> processResults(ResultSet set) throws SQLException {
|
||||||
|
List<TablePlayer> players = new ArrayList<>();
|
||||||
|
while (set.next()) {
|
||||||
|
TablePlayer.Builder player = TablePlayer.builder()
|
||||||
|
.uuid(UUID.fromString(set.getString(UsersTable.USER_UUID)))
|
||||||
|
.name(set.getString(UsersTable.USER_NAME))
|
||||||
|
.geolocation(set.getString(GeoInfoTable.GEOLOCATION))
|
||||||
|
.registered(set.getLong(UsersTable.REGISTERED))
|
||||||
|
.lastSeen(set.getLong("last_seen"))
|
||||||
|
.sessionCount(set.getInt("count"))
|
||||||
|
.playtime(set.getLong("playtime"))
|
||||||
|
.activityIndex(new ActivityIndex(set.getDouble("activity_index"), date));
|
||||||
|
if (set.getBoolean(UserInfoTable.BANNED)) {
|
||||||
|
player.banned();
|
||||||
|
}
|
||||||
|
players.add(player.build());
|
||||||
|
}
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,6 @@ import com.djrapitops.plan.storage.database.queries.QueryStatement;
|
|||||||
import com.djrapitops.plan.storage.database.sql.parsing.Sql;
|
import com.djrapitops.plan.storage.database.sql.parsing.Sql;
|
||||||
import com.djrapitops.plan.storage.database.sql.tables.*;
|
import com.djrapitops.plan.storage.database.sql.tables.*;
|
||||||
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
|
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
|
||||||
import com.djrapitops.plan.utilities.comparators.SessionStartComparator;
|
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
@ -263,7 +262,7 @@ public class SessionQueries {
|
|||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.map(SortedMap::values)
|
.map(SortedMap::values)
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.sorted(new SessionStartComparator()) // Disorder arises
|
.sorted(dateColderRecentComparator) // Disorder arises
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import com.djrapitops.plan.PlanSystem;
|
|||||||
import com.djrapitops.plan.data.element.TableContainer;
|
import com.djrapitops.plan.data.element.TableContainer;
|
||||||
import com.djrapitops.plan.delivery.domain.DateObj;
|
import com.djrapitops.plan.delivery.domain.DateObj;
|
||||||
import com.djrapitops.plan.delivery.domain.Nickname;
|
import com.djrapitops.plan.delivery.domain.Nickname;
|
||||||
|
import com.djrapitops.plan.delivery.domain.TablePlayer;
|
||||||
import com.djrapitops.plan.delivery.domain.WebUser;
|
import com.djrapitops.plan.delivery.domain.WebUser;
|
||||||
import com.djrapitops.plan.delivery.domain.container.PlayerContainer;
|
import com.djrapitops.plan.delivery.domain.container.PlayerContainer;
|
||||||
import com.djrapitops.plan.delivery.domain.container.ServerContainer;
|
import com.djrapitops.plan.delivery.domain.container.ServerContainer;
|
||||||
@ -1406,7 +1407,7 @@ public interface DatabaseTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
default void activeTunredInactiveQueryHasAllParametersSet() {
|
default void activeTurnedInactiveQueryHasAllParametersSet() {
|
||||||
Integer result = db().query(ActivityIndexQueries.countRegularPlayersTurnedInactive(
|
Integer result = db().query(ActivityIndexQueries.countRegularPlayersTurnedInactive(
|
||||||
0, System.currentTimeMillis(), serverUUID(),
|
0, System.currentTimeMillis(), serverUUID(),
|
||||||
TimeUnit.HOURS.toMillis(2L)
|
TimeUnit.HOURS.toMillis(2L)
|
||||||
@ -1414,6 +1415,14 @@ public interface DatabaseTest {
|
|||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void serverTablePlayersQueryQueriesAtLeastOnePlayer() {
|
||||||
|
sessionsAreStoredWithAllData();
|
||||||
|
|
||||||
|
List<TablePlayer> result = db().query(new ServerTablePlayersQuery(serverUUID(), System.currentTimeMillis(), 10L, 1));
|
||||||
|
assertNotEquals(Collections.emptyList(), result);
|
||||||
|
}
|
||||||
|
|
||||||
@PluginInfo(name = "ConditionalExtension")
|
@PluginInfo(name = "ConditionalExtension")
|
||||||
class ConditionalExtension implements DataExtension {
|
class ConditionalExtension implements DataExtension {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user