Skip to content

Commit ae9b528

Browse files
committed
Implement ban status upkeep task
The task processes 50 users ban status once every 2 minutes. Affects issues: - Close #1964
1 parent ec96db2 commit ae9b528

File tree

19 files changed

+462
-20
lines changed

19 files changed

+462
-20
lines changed

Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/BukkitSensor.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.djrapitops.plan.gathering.domain.PluginMetadata;
2020
import org.bukkit.Bukkit;
21+
import org.bukkit.OfflinePlayer;
2122
import org.bukkit.Server;
2223
import org.bukkit.World;
2324
import org.bukkit.entity.Player;
@@ -27,6 +28,7 @@
2728
import javax.inject.Singleton;
2829
import java.util.Arrays;
2930
import java.util.List;
31+
import java.util.UUID;
3032
import java.util.stream.Collectors;
3133

3234
@Singleton
@@ -140,4 +142,17 @@ public List<PluginMetadata> getInstalledPlugins() {
140142
.map(description -> new PluginMetadata(description.getName(), description.getVersion()))
141143
.collect(Collectors.toList());
142144
}
145+
146+
@Override
147+
public boolean supportsBans() {
148+
return true;
149+
}
150+
151+
@Override
152+
public boolean isBanned(UUID playerUUID) {
153+
OfflinePlayer player = server.getOfflinePlayer(playerUUID);
154+
if (player == null) {return false;}
155+
return player.isBanned();
156+
}
157+
143158
}

Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitTaskModule.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@
2525
import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
2626
import com.djrapitops.plan.gathering.ShutdownDataPreservation;
2727
import com.djrapitops.plan.gathering.ShutdownHook;
28-
import com.djrapitops.plan.gathering.timed.BukkitPingCounter;
29-
import com.djrapitops.plan.gathering.timed.InstalledPluginGatheringTask;
30-
import com.djrapitops.plan.gathering.timed.ServerTPSCounter;
31-
import com.djrapitops.plan.gathering.timed.SystemUsageBuffer;
28+
import com.djrapitops.plan.gathering.timed.*;
3229
import com.djrapitops.plan.settings.upkeep.ConfigStoreTask;
3330
import com.djrapitops.plan.storage.upkeep.DBCleanTask;
3431
import com.djrapitops.plan.storage.upkeep.ExtensionDisableOnGameServerTask;
@@ -113,4 +110,8 @@ public interface BukkitTaskModule {
113110
@Binds
114111
@IntoSet
115112
TaskSystem.Task bindInstalledPluginGatheringTask(InstalledPluginGatheringTask installedPluginGatheringTask);
113+
114+
@Binds
115+
@IntoSet
116+
TaskSystem.Task bindBanStatusUpkeepTask(BanStatusUpkeepTask banStatusUpkeepTask);
116117
}

Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.djrapitops.plan.delivery.formatting.Formatters;
2323
import com.djrapitops.plan.delivery.webserver.NonProxyWebserverDisableChecker;
2424
import com.djrapitops.plan.delivery.webserver.WebServerSystem;
25+
import com.djrapitops.plan.gathering.GatheringUtilities;
2526
import com.djrapitops.plan.gathering.cache.CacheSystem;
2627
import com.djrapitops.plan.gathering.importing.ImportSystem;
2728
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
@@ -69,6 +70,7 @@ public class PlanSystem implements SubSystem {
6970
private final ImportSystem importSystem;
7071
private final ExportSystem exportSystem;
7172
private final DeliveryUtilities deliveryUtilities;
73+
private final GatheringUtilities gatheringUtilities;
7274
private final ApiServices apiServices;
7375
private final PluginLogger logger;
7476
private final ErrorLogger errorLogger;
@@ -92,7 +94,7 @@ public PlanSystem(
9294
PluginLogger logger,
9395
ErrorLogger errorLogger,
9496
ApiServices apiServices, // API v5
95-
@SuppressWarnings("deprecation") PlanAPI.PlanAPIHolder apiHolder // Deprecated PlanAPI, backwards compatibility
97+
@SuppressWarnings("deprecation") PlanAPI.PlanAPIHolder apiHolder, GatheringUtilities gatheringUtilities // Deprecated PlanAPI, backwards compatibility
9698
) {
9799
this.files = files;
98100
this.configSystem = configSystem;
@@ -108,6 +110,7 @@ public PlanSystem(
108110
this.importSystem = importSystem;
109111
this.exportSystem = exportSystem;
110112
this.deliveryUtilities = deliveryUtilities;
113+
this.gatheringUtilities = gatheringUtilities;
111114
this.logger = logger;
112115
this.errorLogger = errorLogger;
113116
this.apiServices = apiServices;
@@ -276,6 +279,10 @@ public DeliveryUtilities getDeliveryUtilities() {
276279
return deliveryUtilities;
277280
}
278281

282+
public GatheringUtilities getGatheringUtilities() {
283+
return gatheringUtilities;
284+
}
285+
279286
public boolean isEnabled() {
280287
return enabled;
281288
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* This file is part of Player Analytics (Plan).
3+
*
4+
* Plan is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License v3 as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Plan is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package com.djrapitops.plan.gathering;
18+
19+
import dagger.Lazy;
20+
21+
import javax.inject.Inject;
22+
import javax.inject.Singleton;
23+
24+
/**
25+
* Dagger object for accessing some gathering utilities.
26+
*
27+
* @author AuroraLS3
28+
*/
29+
@Singleton
30+
public class GatheringUtilities {
31+
32+
private final Lazy<ServerSensor<?>> serverSensor;
33+
34+
@Inject
35+
public GatheringUtilities(Lazy<ServerSensor<?>> serverSensor) {
36+
this.serverSensor = serverSensor;
37+
}
38+
39+
public ServerSensor<?> getServerSensor() {
40+
return serverSensor.get();
41+
}
42+
}

Plan/common/src/main/java/com/djrapitops/plan/gathering/ServerSensor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import java.util.Collections;
2222
import java.util.List;
23+
import java.util.UUID;
2324

2425
/**
2526
* Allows sensing values from different server platforms.
@@ -70,4 +71,8 @@ default boolean usingRedisBungee() {
7071
default List<PluginMetadata> getInstalledPlugins() {
7172
return List.of();
7273
}
74+
75+
default boolean supportsBans() {return false;}
76+
77+
default boolean isBanned(UUID playerUUID) {return false;}
7378
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* This file is part of Player Analytics (Plan).
3+
*
4+
* Plan is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License v3 as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Plan is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package com.djrapitops.plan.gathering.timed;
18+
19+
import com.djrapitops.plan.TaskSystem;
20+
import com.djrapitops.plan.gathering.ServerSensor;
21+
import com.djrapitops.plan.identification.ServerInfo;
22+
import com.djrapitops.plan.identification.ServerUUID;
23+
import com.djrapitops.plan.storage.database.DBSystem;
24+
import com.djrapitops.plan.storage.database.Database;
25+
import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries;
26+
import com.djrapitops.plan.storage.database.transactions.events.BatchBanStatusTransaction;
27+
import net.playeranalytics.plugin.scheduling.RunnableFactory;
28+
29+
import javax.inject.Inject;
30+
import javax.inject.Singleton;
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
import java.util.UUID;
34+
import java.util.concurrent.CompletableFuture;
35+
import java.util.concurrent.ThreadLocalRandom;
36+
import java.util.concurrent.TimeUnit;
37+
38+
/**
39+
* @author AuroraLS3
40+
*/
41+
@Singleton
42+
public class BanStatusUpkeepTask extends TaskSystem.Task {
43+
44+
private final DBSystem dbSystem;
45+
private final ServerInfo serverInfo;
46+
private final ServerSensor<?> serverSensor;
47+
private Integer currentId;
48+
49+
@Inject
50+
public BanStatusUpkeepTask(DBSystem dbSystem, ServerInfo serverInfo, ServerSensor<?> serverSensor) {
51+
this.dbSystem = dbSystem;
52+
this.serverInfo = serverInfo;
53+
this.serverSensor = serverSensor;
54+
}
55+
56+
@Override
57+
public void register(RunnableFactory runnableFactory) {
58+
if (!serverSensor.supportsBans()) return; // Don't register this task if server doesn't have bans.
59+
60+
int randomStartDelay = ThreadLocalRandom.current().nextInt(30);
61+
runnableFactory.create(this)
62+
.runTaskTimerAsynchronously(randomStartDelay, 120, TimeUnit.SECONDS);
63+
}
64+
65+
@Override
66+
public void run() {
67+
updateBanStatus();
68+
}
69+
70+
CompletableFuture<?> updateBanStatus() {
71+
ServerUUID serverUUID = serverInfo.getServerUUID();
72+
Database database = dbSystem.getDatabase();
73+
Integer maxId = database.query(UserIdentifierQueries.fetchMaxUserId(serverUUID));
74+
if (currentId == null) {
75+
currentId = ThreadLocalRandom.current().nextInt(maxId);
76+
}
77+
78+
List<UUID> toProcess = database.query(UserIdentifierQueries.fetchUUIDsStartingFromId(currentId, serverUUID, 51));
79+
if (!toProcess.isEmpty()) {
80+
UUID lastUUID = toProcess.get(toProcess.size() - 1);
81+
currentId = database.query(UserIdentifierQueries.fetchUserId(lastUUID)).orElse(0);
82+
if (currentId >= maxId) {
83+
currentId = 0;
84+
}
85+
} else {
86+
currentId = 0;
87+
}
88+
89+
List<UUID> bannedPlayers = new ArrayList<>();
90+
List<UUID> unbannedPlayers = new ArrayList<>();
91+
92+
int i = 0;
93+
for (UUID playerUUID : toProcess) {
94+
if (i > 50) break; // Skip the UUID next batch starts with
95+
boolean banned = serverSensor.isBanned(playerUUID);
96+
if (banned) {
97+
bannedPlayers.add(playerUUID);
98+
} else {
99+
unbannedPlayers.add(playerUUID);
100+
}
101+
i++;
102+
}
103+
104+
if (bannedPlayers.isEmpty() && unbannedPlayers.isEmpty()) return null;
105+
return database.executeTransaction(new BatchBanStatusTransaction(bannedPlayers, unbannedPlayers, serverUUID));
106+
}
107+
}

Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserIdentifierQueries.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,19 @@ public Optional<Integer> processResults(ResultSet set) throws SQLException {
238238
}
239239
};
240240
}
241+
242+
public static Query<Integer> fetchMaxUserId(ServerUUID serverUUID) {
243+
String sql = SELECT + "MAX(" + UserInfoTable.USER_ID + ")" + FROM + UserInfoTable.TABLE_NAME + WHERE + UserInfoTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID;
244+
return db -> db.queryOptional(sql, row -> row.getInt(1), serverUUID).orElse(0);
245+
}
246+
247+
public static Query<List<UUID>> fetchUUIDsStartingFromId(Integer userId, ServerUUID serverUUID, int limit) {
248+
String sql = SELECT + UsersTable.USER_UUID +
249+
FROM + UserInfoTable.TABLE_NAME + " ui" +
250+
LEFT_JOIN + UsersTable.TABLE_NAME + " u ON u." + UsersTable.ID + "=ui." + UserInfoTable.USER_ID +
251+
WHERE + UserInfoTable.USER_ID + ">=" + userId +
252+
AND + UserInfoTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID +
253+
LIMIT + limit;
254+
return db -> db.queryList(sql, row -> UUID.fromString(row.getString(UsersTable.USER_UUID)), serverUUID);
255+
}
241256
}

Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/BanStatusTransaction.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,14 @@ public class BanStatusTransaction extends Transaction {
3939

4040
private final UUID playerUUID;
4141
private final ServerUUID serverUUID;
42-
private final BooleanSupplier banStatus;
4342

44-
public BanStatusTransaction(UUID playerUUID, ServerUUID serverUUID, boolean banStatus) {
45-
this(playerUUID, serverUUID, () -> banStatus);
43+
private Boolean banned;
44+
private BooleanSupplier banStatus;
45+
46+
public BanStatusTransaction(UUID playerUUID, ServerUUID serverUUID, boolean banned) {
47+
this.playerUUID = playerUUID;
48+
this.serverUUID = serverUUID;
49+
this.banned = banned;
4650
}
4751

4852
public BanStatusTransaction(UUID playerUUID, ServerUUID serverUUID, BooleanSupplier banStatus) {
@@ -65,7 +69,7 @@ private Executable updateBanStatus() {
6569
return new ExecStatement(sql) {
6670
@Override
6771
public void prepare(PreparedStatement statement) throws SQLException {
68-
statement.setBoolean(1, banStatus.getAsBoolean());
72+
statement.setBoolean(1, banned != null ? banned : banStatus.getAsBoolean());
6973
statement.setString(2, playerUUID.toString());
7074
statement.setString(3, serverUUID.toString());
7175
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* This file is part of Player Analytics (Plan).
3+
*
4+
* Plan is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License v3 as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Plan is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package com.djrapitops.plan.storage.database.transactions.events;
18+
19+
import com.djrapitops.plan.identification.ServerUUID;
20+
import com.djrapitops.plan.storage.database.sql.building.Update;
21+
import com.djrapitops.plan.storage.database.sql.tables.ServerTable;
22+
import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable;
23+
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
24+
import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement;
25+
import com.djrapitops.plan.storage.database.transactions.Executable;
26+
import com.djrapitops.plan.storage.database.transactions.Transaction;
27+
28+
import java.sql.PreparedStatement;
29+
import java.sql.SQLException;
30+
import java.util.List;
31+
import java.util.UUID;
32+
33+
/**
34+
* Transaction to update a player's ban status.
35+
*
36+
* @author AuroraLS3
37+
*/
38+
public class BatchBanStatusTransaction extends Transaction {
39+
40+
private final List<UUID> bannedPlayerUUIDs;
41+
private final List<UUID> unbannedPlayerUUIDs;
42+
private final ServerUUID serverUUID;
43+
44+
public BatchBanStatusTransaction(List<UUID> bannedPlayerUUIDs, List<UUID> unbannedPlayerUUIDs, ServerUUID serverUUID) {
45+
this.bannedPlayerUUIDs = bannedPlayerUUIDs;
46+
this.unbannedPlayerUUIDs = unbannedPlayerUUIDs;
47+
this.serverUUID = serverUUID;
48+
}
49+
50+
@Override
51+
protected void performOperations() {
52+
execute(updateBanStatus());
53+
}
54+
55+
private Executable updateBanStatus() {
56+
String sql = Update.values(UserInfoTable.TABLE_NAME, UserInfoTable.BANNED)
57+
.where(UserInfoTable.USER_ID + "=" + UsersTable.SELECT_USER_ID)
58+
.and(UserInfoTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID)
59+
.toString();
60+
61+
return new ExecBatchStatement(sql) {
62+
@Override
63+
public void prepare(PreparedStatement statement) throws SQLException {
64+
for (UUID bannedPlayerUUID : bannedPlayerUUIDs) {
65+
statement.setBoolean(1, true);
66+
statement.setString(2, bannedPlayerUUID.toString());
67+
statement.setString(3, serverUUID.toString());
68+
statement.addBatch();
69+
}
70+
for (UUID unbannedPlayerUUID : unbannedPlayerUUIDs) {
71+
statement.setBoolean(1, false);
72+
statement.setString(2, unbannedPlayerUUID.toString());
73+
statement.setString(3, serverUUID.toString());
74+
statement.addBatch();
75+
}
76+
}
77+
};
78+
}
79+
}

0 commit comments

Comments
 (0)