Add allowlist bounce endpoint
This commit is contained in:
parent
3ebe7a66a1
commit
d32760b34d
|
@ -28,7 +28,7 @@ import com.djrapitops.plan.identification.ServerUUID;
|
|||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.BanStatusTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.KickStoreTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreWhitelistBounceTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreAllowlistBounceTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
@ -86,7 +86,7 @@ public class PlayerOnlineListener implements Listener {
|
|||
boolean notWhitelisted = PlayerLoginEvent.Result.KICK_WHITELIST == event.getResult();
|
||||
|
||||
if (notWhitelisted) {
|
||||
dbSystem.getDatabase().executeTransaction(new StoreWhitelistBounceTransaction(playerUUID, event.getPlayer().getName(), serverUUID, System.currentTimeMillis()));
|
||||
dbSystem.getDatabase().executeTransaction(new StoreAllowlistBounceTransaction(playerUUID, event.getPlayer().getName(), serverUUID, System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
String address = event.getHostname();
|
||||
|
|
|
@ -93,6 +93,7 @@ public enum WebPermission implements Supplier<String>, Lang {
|
|||
PAGE_SERVER_PERFORMANCE_OVERVIEW("See Performance numbers"),
|
||||
PAGE_SERVER_PLUGIN_HISTORY("See Plugin History"),
|
||||
PAGE_SERVER_PLUGINS("See Plugins -tabs of servers"),
|
||||
PAGE_SERVER_ALLOWLIST_BOUNCE("See list of Game allowlist bounces"),
|
||||
|
||||
PAGE_PLAYER("See all of player page"),
|
||||
PAGE_PLAYER_OVERVIEW("See Player Overview -tab"),
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.datatransfer;
|
||||
|
||||
import com.djrapitops.plan.utilities.dev.Untrusted;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents an event where player bounced off the whitelist.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class AllowlistBounce {
|
||||
|
||||
private final UUID playerUUID;
|
||||
@Untrusted
|
||||
private final String playerName;
|
||||
private final int count;
|
||||
private final long lastTime;
|
||||
|
||||
public AllowlistBounce(UUID playerUUID, String playerName, int count, long lastTime) {
|
||||
this.playerUUID = playerUUID;
|
||||
this.playerName = playerName;
|
||||
this.count = count;
|
||||
this.lastTime = lastTime;
|
||||
}
|
||||
|
||||
public UUID getPlayerUUID() {
|
||||
return playerUUID;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public long getLastTime() {
|
||||
return lastTime;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.resolver.json;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.auth.WebPermission;
|
||||
import com.djrapitops.plan.delivery.formatting.Formatter;
|
||||
import com.djrapitops.plan.delivery.web.resolver.MimeType;
|
||||
import com.djrapitops.plan.delivery.web.resolver.Response;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.Request;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.WebUser;
|
||||
import com.djrapitops.plan.delivery.webserver.cache.AsyncJSONResolverService;
|
||||
import com.djrapitops.plan.delivery.webserver.cache.DataID;
|
||||
import com.djrapitops.plan.delivery.webserver.cache.JSONStorage;
|
||||
import com.djrapitops.plan.identification.Identifiers;
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.Database;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.AllowlistQueries;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
|
||||
import com.djrapitops.plan.utilities.dev.Untrusted;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Response resolver to get game allowlist bounces.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
@Singleton
|
||||
@Path("/v1/gameAllowlistBounces")
|
||||
public class AllowlistJSONResolver extends JSONResolver {
|
||||
|
||||
private final DBSystem dbSystem;
|
||||
private final Identifiers identifiers;
|
||||
private final AsyncJSONResolverService jsonResolverService;
|
||||
|
||||
@Inject
|
||||
public AllowlistJSONResolver(DBSystem dbSystem, Identifiers identifiers, AsyncJSONResolverService jsonResolverService) {
|
||||
this.dbSystem = dbSystem;
|
||||
this.identifiers = identifiers;
|
||||
this.jsonResolverService = jsonResolverService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Formatter<Long> getHttpLastModifiedFormatter() {return jsonResolverService.getHttpLastModifiedFormatter();}
|
||||
|
||||
@Override
|
||||
public boolean canAccess(@Untrusted Request request) {
|
||||
WebUser user = request.getUser().orElse(new WebUser(""));
|
||||
|
||||
return user.hasPermission(WebPermission.PAGE_SERVER_ALLOWLIST_BOUNCE);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Operation(
|
||||
description = "Get allowlist bounce data for server",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", content = @Content(mediaType = MimeType.JSON)),
|
||||
@ApiResponse(responseCode = "400", description = "If 'server' parameter is not an existing server")
|
||||
},
|
||||
parameters = @Parameter(in = ParameterIn.QUERY, name = "server", description = "Server identifier to get data for (optional)", examples = {
|
||||
@ExampleObject("Server 1"),
|
||||
@ExampleObject("1"),
|
||||
@ExampleObject("1fb39d2a-eb82-4868-b245-1fad17d823b3"),
|
||||
}),
|
||||
requestBody = @RequestBody(content = @Content(examples = @ExampleObject()))
|
||||
)
|
||||
@Override
|
||||
public Optional<Response> resolve(@Untrusted Request request) {
|
||||
return Optional.of(getResponse(request));
|
||||
}
|
||||
|
||||
private Response getResponse(@Untrusted Request request) {
|
||||
JSONStorage.StoredJSON result = getStoredJSON(request);
|
||||
return getCachedOrNewResponse(request, result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JSONStorage.StoredJSON getStoredJSON(Request request) {
|
||||
Optional<Long> timestamp = Identifiers.getTimestamp(request);
|
||||
|
||||
ServerUUID serverUUID = identifiers.getServerUUID(request);
|
||||
Database database = dbSystem.getDatabase();
|
||||
return jsonResolverService.resolve(timestamp, DataID.PLAYER_RETENTION, serverUUID,
|
||||
theUUID -> Map.of(
|
||||
"allowlist_bounces", database.query(AllowlistQueries.getBounces(serverUUID)),
|
||||
"last_seen_by_uuid", database.query(SessionQueries.getLastSeen(serverUUID))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -90,6 +90,7 @@ public class RootJSONResolver {
|
|||
RetentionJSONResolver retentionJSONResolver,
|
||||
PlayerJoinAddressJSONResolver playerJoinAddressJSONResolver,
|
||||
PluginHistoryJSONResolver pluginHistoryJSONResolver,
|
||||
AllowlistJSONResolver allowlistJSONResolver,
|
||||
|
||||
PreferencesJSONResolver preferencesJSONResolver,
|
||||
StorePreferencesJSONResolver storePreferencesJSONResolver,
|
||||
|
@ -129,7 +130,8 @@ public class RootJSONResolver {
|
|||
.add("extensionData", extensionJSONResolver)
|
||||
.add("retention", retentionJSONResolver)
|
||||
.add("joinAddresses", playerJoinAddressJSONResolver)
|
||||
.add("preferences", preferencesJSONResolver);
|
||||
.add("preferences", preferencesJSONResolver)
|
||||
.add("gameAllowlistBounces", allowlistJSONResolver);
|
||||
|
||||
this.webServer = webServer;
|
||||
// These endpoints require authentication to be enabled.
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.datatransfer.AllowlistBounce;
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.queries.Query;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.AllowlistBounceTable;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.ServerTable;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
|
||||
|
||||
/**
|
||||
* Query against {@link AllowlistBounceTable}.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class AllowlistQueries {
|
||||
|
||||
private AllowlistQueries() {
|
||||
/* Static method class */
|
||||
}
|
||||
|
||||
public static Query<List<AllowlistBounce>> getBounces(ServerUUID serverUUID) {
|
||||
@Language("SQL") String sql = SELECT +
|
||||
AllowlistBounceTable.UUID + ',' +
|
||||
AllowlistBounceTable.USER_NAME + ',' +
|
||||
AllowlistBounceTable.TIMES + ',' +
|
||||
AllowlistBounceTable.LAST_BOUNCE +
|
||||
FROM + AllowlistBounceTable.TABLE_NAME +
|
||||
WHERE + AllowlistBounceTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID;
|
||||
return db -> db.queryList(sql, AllowlistQueries::extract, serverUUID);
|
||||
}
|
||||
|
||||
private static AllowlistBounce extract(ResultSet set) throws SQLException {
|
||||
return new AllowlistBounce(
|
||||
UUID.fromString(set.getString(AllowlistBounceTable.UUID)),
|
||||
set.getString(AllowlistBounceTable.USER_NAME),
|
||||
set.getInt(AllowlistBounceTable.TIMES),
|
||||
set.getLong(AllowlistBounceTable.LAST_BOUNCE)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1014,4 +1014,16 @@ public class SessionQueries {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Map<UUID, Long>> getLastSeen(ServerUUID serverUUID) {
|
||||
String sql = SELECT + UsersTable.USER_UUID + ", MAX(" + SessionsTable.SESSION_END + ") as last_seen" +
|
||||
FROM + SessionsTable.TABLE_NAME + " s" +
|
||||
INNER_JOIN + UsersTable.TABLE_NAME + " u ON u." + UsersTable.ID + "=s." + SessionsTable.USER_ID +
|
||||
WHERE + SessionsTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID +
|
||||
GROUP_BY + UsersTable.USER_UUID;
|
||||
return db -> db.queryMap(sql, (set, to) -> to.put(
|
||||
UUID.fromString(set.getString(UsersTable.USER_UUID)),
|
||||
set.getLong("last_seen")
|
||||
), serverUUID);
|
||||
}
|
||||
}
|
|
@ -22,13 +22,13 @@ import com.djrapitops.plan.storage.database.sql.building.Sql;
|
|||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
/**
|
||||
* Represents plan_whitelist_bounce table.
|
||||
* Represents plan_allowlist_bounce table.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class WhitelistBounceTable {
|
||||
public class AllowlistBounceTable {
|
||||
|
||||
public static final String TABLE_NAME = "plan_whitelist_bounce";
|
||||
public static final String TABLE_NAME = "plan_allowlist_bounce";
|
||||
|
||||
public static final String ID = "id";
|
||||
public static final String UUID = "uuid";
|
||||
|
@ -52,7 +52,7 @@ public class WhitelistBounceTable {
|
|||
" WHERE " + UUID + "=?" +
|
||||
" AND " + SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID;
|
||||
|
||||
private WhitelistBounceTable() {
|
||||
private AllowlistBounceTable() {
|
||||
/* Static information class */
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
package com.djrapitops.plan.storage.database.transactions.events;
|
||||
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.WhitelistBounceTable;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.AllowlistBounceTable;
|
||||
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
|
||||
import com.djrapitops.plan.storage.database.transactions.Transaction;
|
||||
import com.djrapitops.plan.utilities.dev.Untrusted;
|
||||
|
@ -27,11 +27,11 @@ import java.sql.SQLException;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Stores a bounced whitelist login.
|
||||
* Stores a bounced allowlist login.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class StoreWhitelistBounceTransaction extends Transaction {
|
||||
public class StoreAllowlistBounceTransaction extends Transaction {
|
||||
|
||||
private final UUID playerUUID;
|
||||
@Untrusted
|
||||
|
@ -39,7 +39,7 @@ public class StoreWhitelistBounceTransaction extends Transaction {
|
|||
private final ServerUUID serverUUID;
|
||||
private final long time;
|
||||
|
||||
public StoreWhitelistBounceTransaction(UUID playerUUID, @Untrusted String playerName, ServerUUID serverUUID, long time) {
|
||||
public StoreAllowlistBounceTransaction(UUID playerUUID, @Untrusted String playerName, ServerUUID serverUUID, long time) {
|
||||
this.playerUUID = playerUUID;
|
||||
this.playerName = playerName;
|
||||
this.serverUUID = serverUUID;
|
||||
|
@ -48,7 +48,7 @@ public class StoreWhitelistBounceTransaction extends Transaction {
|
|||
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
boolean updated = execute(new ExecStatement(WhitelistBounceTable.INCREMENT_TIMES_STATEMENT) {
|
||||
boolean updated = execute(new ExecStatement(AllowlistBounceTable.INCREMENT_TIMES_STATEMENT) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setLong(1, time);
|
||||
|
@ -57,7 +57,7 @@ public class StoreWhitelistBounceTransaction extends Transaction {
|
|||
}
|
||||
});
|
||||
if (!updated) {
|
||||
execute(new ExecStatement(WhitelistBounceTable.INSERT_STATEMENT) {
|
||||
execute(new ExecStatement(AllowlistBounceTable.INSERT_STATEMENT) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setString(1, playerUUID.toString());
|
|
@ -59,7 +59,7 @@ public class CreateTablesTransaction extends OperationCriticalTransaction {
|
|||
executeOther(new SecurityTableIdPatch());
|
||||
execute(WebUserPreferencesTable.createTableSQL(dbType));
|
||||
execute(PluginVersionTable.createTableSQL(dbType));
|
||||
execute(WhitelistBounceTable.createTableSQL(dbType));
|
||||
execute(AllowlistBounceTable.createTableSQL(dbType));
|
||||
|
||||
// DataExtension tables
|
||||
execute(ExtensionIconTable.createTableSQL(dbType));
|
||||
|
|
|
@ -163,7 +163,8 @@ class AccessControlTest {
|
|||
Arguments.of("/v1/preferences", WebPermission.ACCESS, 200, 200),
|
||||
Arguments.of("/v1/storePreferences", WebPermission.ACCESS, 400, 400),
|
||||
Arguments.of("/v1/pluginHistory?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_NETWORK_PLUGIN_HISTORY, 200, 403),
|
||||
Arguments.of("/v1/pluginHistory?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_SERVER_PLUGIN_HISTORY, 200, 403)
|
||||
Arguments.of("/v1/pluginHistory?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_SERVER_PLUGIN_HISTORY, 200, 403),
|
||||
Arguments.of("/v1/gameAllowlistBounces?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_SERVER_ALLOWLIST_BOUNCE, 200, 403)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import com.djrapitops.plan.identification.ServerUUID;
|
|||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.BanStatusTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.KickStoreTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreWhitelistBounceTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreAllowlistBounceTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
|
||||
|
@ -97,7 +97,7 @@ public class PlayerOnlineListener implements Listener {
|
|||
public void onPlayerKick(PlayerKickEvent event) {
|
||||
try {
|
||||
if (event.getReasonEnum() == PlayerKickEvent.Reason.NOT_WHITELISTED) {
|
||||
dbSystem.getDatabase().executeTransaction(new StoreWhitelistBounceTransaction(
|
||||
dbSystem.getDatabase().executeTransaction(new StoreAllowlistBounceTransaction(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getName(),
|
||||
serverInfo.getServerUUID(), System.currentTimeMillis())
|
||||
|
|
|
@ -27,7 +27,7 @@ import com.djrapitops.plan.identification.ServerUUID;
|
|||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.BanStatusTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.KickStoreTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreWhitelistBounceTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreAllowlistBounceTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import org.spongepowered.api.Game;
|
||||
|
@ -96,8 +96,8 @@ public class PlayerOnlineListener {
|
|||
if (game.server().isWhitelistEnabled()) {
|
||||
game.server().serviceProvider().whitelistService().isWhitelisted(profile)
|
||||
.thenAccept(whitelisted -> {
|
||||
if (!whitelisted) {
|
||||
dbSystem.getDatabase().executeTransaction(new StoreWhitelistBounceTransaction(
|
||||
if (Boolean.FALSE.equals(whitelisted)) {
|
||||
dbSystem.getDatabase().executeTransaction(new StoreAllowlistBounceTransaction(
|
||||
playerUUID,
|
||||
event.profile().name().orElse(event.user().uniqueId().toString()),
|
||||
serverUUID,
|
||||
|
|
Loading…
Reference in New Issue