diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/TablePlayer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/TablePlayer.java index 900af8b26..468815617 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/TablePlayer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/TablePlayer.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.delivery.domain; import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; import com.djrapitops.plan.gathering.domain.BaseUser; +import com.djrapitops.plan.gathering.domain.Ping; import java.util.Objects; import java.util.Optional; @@ -38,6 +39,7 @@ public class TablePlayer implements Comparable { private Long registered; private Long lastSeen; private String geolocation; + private Ping ping; private boolean banned = false; @@ -87,6 +89,10 @@ public class TablePlayer implements Comparable { return Optional.ofNullable(geolocation); } + public Ping getPing() { + return ping; + } + public boolean isBanned() { return banned; } @@ -111,12 +117,13 @@ public class TablePlayer implements Comparable { lastSeen.equals(that.lastSeen) && name.equals(that.name) && activityIndex.equals(that.activityIndex) && - geolocation.equals(that.geolocation); + geolocation.equals(that.geolocation) && + ping.equals(that.ping); } @Override public int hashCode() { - return Objects.hash(name, activityIndex, activePlaytime, sessionCount, registered, lastSeen, geolocation); + return Objects.hash(name, activityIndex, activePlaytime, sessionCount, registered, lastSeen, geolocation, ping); } @Override @@ -130,6 +137,7 @@ public class TablePlayer implements Comparable { ", registered=" + registered + ", lastSeen=" + lastSeen + ", geolocation='" + geolocation + '\'' + + ", ping='" + ping + '\'' + ", banned=" + banned + '}'; } @@ -190,6 +198,11 @@ public class TablePlayer implements Comparable { return this; } + public Builder ping(Ping ping) { + player.ping = ping; + return this; + } + public TablePlayer build() { return player; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/TablePlayerDto.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/TablePlayerDto.java index 891f7ce86..ec4477940 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/TablePlayerDto.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/TablePlayerDto.java @@ -17,6 +17,7 @@ package com.djrapitops.plan.delivery.domain.datatransfer; import com.djrapitops.plan.delivery.domain.datatransfer.extension.ExtensionValueDataDto; +import com.djrapitops.plan.gathering.domain.Ping; import java.util.Map; import java.util.UUID; @@ -36,6 +37,9 @@ public class TablePlayerDto { private Long lastSeen; private Long registered; private String country; + private Double pingAverage; + private Integer pingMax; + private Integer pingMin; private Map extensionValues; @@ -119,6 +123,30 @@ public class TablePlayerDto { this.playerUUID = playerUUID; } + public Double getPingAverage() { + return pingAverage; + } + + public void setPingAverage(Double pingAverage) { + this.pingAverage = pingAverage; + } + + public Integer getPingMax() { + return pingMax; + } + + public void setPingMax(Integer pingMax) { + this.pingMax = pingMax; + } + + public Integer getPingMin() { + return pingMin; + } + + public void setPingMin(Integer pingMin) { + this.pingMin = pingMin; + } + public static final class TablePlayerDtoBuilder { private final TablePlayerDto tablePlayerDto; @@ -169,6 +197,15 @@ public class TablePlayerDto { return this; } + public TablePlayerDtoBuilder withPing(Ping ping) { + if (ping != null) { + tablePlayerDto.setPingAverage(ping.getAverage()); + tablePlayerDto.setPingMax(ping.getMax()); + tablePlayerDto.setPingMin(ping.getMin()); + } + return this; + } + public TablePlayerDto build() {return tablePlayerDto;} } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java index eecef90fb..a545217d7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONCreator.java @@ -144,6 +144,7 @@ public class PlayersTableJSONCreator { .withRegistered(player.getRegistered().orElse(null)) .withCountry(player.getGeolocation().orElse(null)) .withExtensionValues(mapToExtensionValues(extensionData.get(player.getPlayerUUID()))) + .withPing(player.getPing()) .build() ).collect(Collectors.toList()); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/NetworkTablePlayersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/NetworkTablePlayersQuery.java index e0c67e12f..3d387dc6f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/NetworkTablePlayersQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/NetworkTablePlayersQuery.java @@ -18,14 +18,12 @@ package com.djrapitops.plan.storage.database.queries.objects.playertable; import com.djrapitops.plan.delivery.domain.TablePlayer; import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.gathering.domain.Ping; 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.NetworkActivityIndexQueries; -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 com.djrapitops.plan.storage.database.sql.tables.*; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -72,6 +70,14 @@ public class NetworkTablePlayersQuery implements Query> { FROM + SessionsTable.TABLE_NAME + " s" + GROUP_BY + "s." + SessionsTable.USER_ID; + String selectPingData = SELECT + + "p." + PingTable.USER_ID + ',' + + "AVG(p." + PingTable.AVG_PING + ") as " + PingTable.AVG_PING + "," + + "MAX(p." + PingTable.MAX_PING + ") as " + PingTable.MAX_PING + "," + + "MIN(p." + PingTable.MIN_PING + ") as " + PingTable.MIN_PING + + FROM + PingTable.TABLE_NAME + " p" + + GROUP_BY + "p." + PingTable.USER_ID; + String selectBanned = SELECT + DISTINCT + "ub." + UserInfoTable.USER_ID + FROM + UserInfoTable.TABLE_NAME + " ub" + WHERE + UserInfoTable.BANNED + "=?"; @@ -85,12 +91,16 @@ public class NetworkTablePlayersQuery implements Query> { "ses.last_seen," + "ses.count," + "ses.active_playtime," + - "act.activity_index" + + "act.activity_index," + + "pi.min_ping," + + "pi.max_ping," + + "pi.avg_ping" + FROM + UsersTable.TABLE_NAME + " u" + LEFT_JOIN + '(' + selectBanned + ") ban on ban." + UserInfoTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + selectLatestGeolocations + ") geo on geo." + GeoInfoTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + selectSessionData + ") ses on ses." + SessionsTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + NetworkActivityIndexQueries.selectActivityIndexSQL() + ") act on u." + UsersTable.ID + "=act." + UserInfoTable.USER_ID + + LEFT_JOIN + '(' + selectPingData + ") pi on pi." + PingTable.USER_ID + "=u." + UsersTable.ID + ORDER_BY + "ses.last_seen DESC LIMIT ?"; return db.query(new QueryStatement<>(selectBaseUsers, 1000) { @@ -113,7 +123,11 @@ public class NetworkTablePlayersQuery implements Query> { .lastSeen(set.getLong("last_seen")) .sessionCount(set.getInt("count")) .activePlaytime(set.getLong("active_playtime")) - .activityIndex(new ActivityIndex(set.getDouble("activity_index"), date)); + .activityIndex(new ActivityIndex(set.getDouble("activity_index"), date)) + .ping(new Ping(0L, null, + set.getInt(PingTable.MIN_PING), + set.getInt(PingTable.MAX_PING), + set.getDouble(PingTable.AVG_PING))); if (set.getString("banned") != null) { player.banned(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/QueryTablePlayersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/QueryTablePlayersQuery.java index cfc05342b..47b0f3cbb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/QueryTablePlayersQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/QueryTablePlayersQuery.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database.queries.objects.playertable; import com.djrapitops.plan.delivery.domain.TablePlayer; import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.gathering.domain.Ping; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.SQLDB; import com.djrapitops.plan.storage.database.queries.Query; @@ -100,6 +101,15 @@ public class QueryTablePlayersQuery implements Query> { AND + "ub." + UserInfoTable.USER_ID + userIdsInSet + (serverUUIDs.isEmpty() ? "" : AND + "ub." + UserInfoTable.SERVER_ID + " IN (" + selectServerIds + ")"); + String selectPingData = SELECT + "p." + PingTable.USER_ID + ',' + + "AVG(p." + PingTable.AVG_PING + ") as " + PingTable.AVG_PING + "," + + "MAX(p." + PingTable.MAX_PING + ") as " + PingTable.MAX_PING + "," + + "MIN(p." + PingTable.MIN_PING + ") as " + PingTable.MIN_PING + + FROM + PingTable.TABLE_NAME + " p" + + WHERE + "p." + PingTable.USER_ID + userIdsInSet + + (serverUUIDs.isEmpty() ? "" : AND + "p." + PingTable.SERVER_ID + " IN (" + selectServerIds + ")") + + GROUP_BY + "p." + PingTable.USER_ID; + String selectBaseUsers = SELECT + "u." + UsersTable.USER_UUID + ',' + "u." + UsersTable.USER_NAME + ',' + @@ -109,12 +119,16 @@ public class QueryTablePlayersQuery implements Query> { "ses.last_seen," + "ses.count," + "ses.active_playtime," + - "act.activity_index" + + "act.activity_index," + + "pi.min_ping," + + "pi.max_ping," + + "pi.avg_ping" + FROM + UsersTable.TABLE_NAME + " u" + LEFT_JOIN + '(' + selectBanned + ") ban on ban." + UserInfoTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + selectLatestGeolocations + ") geo on geo." + GeoInfoTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + selectSessionData + ") ses on ses." + SessionsTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + NetworkActivityIndexQueries.selectActivityIndexSQL() + ") act on u." + UsersTable.ID + "=act." + UserInfoTable.USER_ID + + LEFT_JOIN + '(' + selectPingData + ") pi on pi." + PingTable.USER_ID + "=u." + UsersTable.ID + WHERE + "u." + UsersTable.ID + userIdsInSet + ORDER_BY + "ses.last_seen DESC"; @@ -139,7 +153,11 @@ public class QueryTablePlayersQuery implements Query> { .lastSeen(set.getLong("last_seen")) .sessionCount(set.getInt("count")) .activePlaytime(set.getLong("active_playtime")) - .activityIndex(new ActivityIndex(set.getDouble("activity_index"), beforeDate)); + .activityIndex(new ActivityIndex(set.getDouble("activity_index"), beforeDate)) + .ping(new Ping(0L, null, + set.getInt(PingTable.MIN_PING), + set.getInt(PingTable.MAX_PING), + set.getDouble(PingTable.AVG_PING))); if (set.getString("banned") != null) { player.banned(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/ServerTablePlayersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/ServerTablePlayersQuery.java index 5dee74c61..bf1649dd0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/ServerTablePlayersQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/playertable/ServerTablePlayersQuery.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database.queries.objects.playertable; import com.djrapitops.plan.delivery.domain.TablePlayer; import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.gathering.domain.Ping; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.SQLDB; import com.djrapitops.plan.storage.database.queries.Query; @@ -81,6 +82,15 @@ public class ServerTablePlayersQuery implements Query> { WHERE + "s." + SessionsTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + GROUP_BY + "s." + SessionsTable.USER_ID; + String selectPingData = SELECT + + "p." + PingTable.USER_ID + ',' + + "AVG(p." + PingTable.AVG_PING + ") as " + PingTable.AVG_PING + "," + + "MAX(p." + PingTable.MAX_PING + ") as " + PingTable.MAX_PING + "," + + "MIN(p." + PingTable.MIN_PING + ") as " + PingTable.MIN_PING + + FROM + PingTable.TABLE_NAME + " p" + + WHERE + "p." + PingTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + + GROUP_BY + "p." + PingTable.USER_ID; + String selectBaseUsers = SELECT + "u." + UsersTable.USER_UUID + ',' + "u." + UsersTable.USER_NAME + ',' + @@ -90,12 +100,16 @@ public class ServerTablePlayersQuery implements Query> { "ses.last_seen," + "ses.count," + "ses.active_playtime," + - "act.activity_index" + + "act.activity_index," + + "pi.min_ping," + + "pi.max_ping," + + "pi.avg_ping" + FROM + UsersTable.TABLE_NAME + " u" + INNER_JOIN + UserInfoTable.TABLE_NAME + " on u." + UsersTable.ID + "=" + UserInfoTable.TABLE_NAME + '.' + UserInfoTable.USER_ID + LEFT_JOIN + '(' + selectLatestGeolocations + ") geo on geo." + GeoInfoTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + selectSessionData + ") ses on ses." + SessionsTable.USER_ID + "=u." + UsersTable.ID + LEFT_JOIN + '(' + ActivityIndexQueries.selectActivityIndexSQL() + ") act on u." + UsersTable.ID + "=act." + UserInfoTable.USER_ID + + LEFT_JOIN + '(' + selectPingData + ") pi on pi." + PingTable.USER_ID + "=u." + UsersTable.ID + WHERE + UserInfoTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + ORDER_BY + "ses.last_seen DESC LIMIT ?"; @@ -105,7 +119,8 @@ public class ServerTablePlayersQuery implements Query> { 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); + statement.setString(14, serverUUID.toString()); // Ping query + statement.setInt(15, xMostRecentPlayers); } @Override @@ -120,7 +135,11 @@ public class ServerTablePlayersQuery implements Query> { .lastSeen(set.getLong("last_seen")) .sessionCount(set.getInt("count")) .activePlaytime(set.getLong("active_playtime")) - .activityIndex(new ActivityIndex(set.getDouble("activity_index"), date)); + .activityIndex(new ActivityIndex(set.getDouble("activity_index"), date)) + .ping(new Ping(0L, serverUUID, + set.getInt(PingTable.MIN_PING), + set.getInt(PingTable.MAX_PING), + set.getDouble(PingTable.AVG_PING))); if (set.getBoolean(UserInfoTable.BANNED)) { player.banned(); } diff --git a/Plan/react/dashboard/src/components/cards/common/PlayerListCard.js b/Plan/react/dashboard/src/components/cards/common/PlayerListCard.js index 8c70b14f9..34eeea8e0 100644 --- a/Plan/react/dashboard/src/components/cards/common/PlayerListCard.js +++ b/Plan/react/dashboard/src/components/cards/common/PlayerListCard.js @@ -2,7 +2,7 @@ import {useTranslation} from "react-i18next"; import {Card} from "react-bootstrap"; import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome"; import React, {useCallback, useEffect, useState} from "react"; -import {faCheck, faGlobe, faUser, faUserPlus, faUsers} from "@fortawesome/free-solid-svg-icons"; +import {faCheck, faGlobe, faSignal, faUser, faUserPlus, faUsers} from "@fortawesome/free-solid-svg-icons"; import DataTablesTable from "../../table/DataTablesTable"; import {CardLoader} from "../../navigation/Loader"; import {Link} from "react-router-dom"; @@ -62,6 +62,15 @@ const PlayerListCard = ({data, title, justList, orderBy}) => { }, { title: <> {t('html.label.country')}, data: "country" + }, { + title: <> {t('html.label.averagePing')}, + data: {_: "pingAverage", display: "pingAverageFormatted"} + }, { + title: <> {t('html.label.bestPing')}, + data: {_: "pingMin", display: "pingMinFormatted"} + }, { + title: <> {t('html.label.worstPing')}, + data: {_: "pingMax", display: "pingMaxFormatted"} }]; columns.push(...data.extensionDescriptors.map(descriptor => { @@ -85,7 +94,13 @@ const PlayerListCard = ({data, title, justList, orderBy}) => { registeredFormatted: , lastSeen: player.lastSeen, lastSeenFormatted: , - country: player.country + country: player.country, + pingAverage: player.pingAverage, + pingAverageFormatted: formatDecimals(player.pingAverage, decimalFormat) + "ms", + pingMax: player.pingMax, + pingMaxFormatted: player.pingMax + "ms", + pingMin: player.pingMin, + pingMinFormatted: player.pingMin + "ms" }; data.extensionDescriptors.forEach(descriptor => { row[descriptor.name] = ;