Skip to content

Commit ae2f10e

Browse files
committed
cross server shadowban sync
1 parent c71a390 commit ae2f10e

5 files changed

Lines changed: 149 additions & 4 deletions

File tree

common/src/main/java/io/wdsj/asw/common/sync/VelocitySyncProtocol.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ public final class VelocitySyncProtocol {
1919
public static final String TYPE_VL_RESET_REQUEST = "vl-reset-request";
2020
public static final String TYPE_VL_RESET = "vl-reset";
2121
public static final String TYPE_VL_RESET_ALL = "vl-reset-all";
22+
public static final String TYPE_SHADOW_SET = "shadow-set";
23+
public static final String TYPE_SHADOW_CLEAR = "shadow-clear";
24+
public static final String TYPE_SHADOW_SYNC = "shadow-sync";
25+
public static final String TYPE_SHADOW_QUERY = "shadow-query";
2226
public static final String TYPE_PING = "ping";
2327
public static final String TYPE_PONG = "pong";
2428

paper/src/main/java/io/wdsj/asw/bukkit/manage/punish/PlayerShadowController.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import org.bukkit.entity.Player;
44

5+
import io.wdsj.asw.bukkit.AdvancedSensitiveWords;
6+
import io.wdsj.asw.bukkit.proxy.velocity.sync.VelocitySyncClient;
7+
58
import java.time.Duration;
69
import java.util.Map;
710
import java.util.Objects;
@@ -34,10 +37,14 @@ public static void shadowPlayer(Player player, Duration duration) {
3437
* @param duration shadow duration
3538
*/
3639
public static void shadowPlayer(UUID uuid, Duration duration) {
40+
shadowPlayer(uuid, duration, true);
41+
}
42+
43+
public static void shadowPlayer(UUID uuid, Duration duration, boolean notifyProxy) {
3744
Objects.requireNonNull(uuid, "uuid");
3845
Objects.requireNonNull(duration, "duration");
3946
if (duration.isZero() || duration.isNegative()) {
40-
unshadowPlayer(uuid);
47+
unshadowPlayer(uuid, notifyProxy);
4148
return;
4249
}
4350

@@ -47,7 +54,22 @@ public static void shadowPlayer(UUID uuid, Duration duration) {
4754
} catch (ArithmeticException ignored) {
4855
expiresAtMillis = Long.MAX_VALUE;
4956
}
57+
shadowPlayerUntil(uuid, expiresAtMillis, notifyProxy);
58+
}
59+
60+
public static void shadowPlayerUntil(UUID uuid, long expiresAtMillis, boolean notifyProxy) {
61+
Objects.requireNonNull(uuid, "uuid");
62+
if (expiresAtMillis <= System.currentTimeMillis()) {
63+
unshadowPlayer(uuid, notifyProxy);
64+
return;
65+
}
5066
SHADOWED_PLAYERS.put(uuid, new ShadowBan(expiresAtMillis));
67+
if (notifyProxy) {
68+
VelocitySyncClient client = AdvancedSensitiveWords.getInstance().getVelocitySyncClient();
69+
if (client != null) {
70+
client.sendShadowSet(uuid, expiresAtMillis);
71+
}
72+
}
5173
}
5274

5375
/**
@@ -56,16 +78,26 @@ public static void shadowPlayer(UUID uuid, Duration duration) {
5678
*/
5779
public static void unshadowPlayer(Player player) {
5880
Objects.requireNonNull(player, "player");
59-
SHADOWED_PLAYERS.remove(player.getUniqueId());
81+
unshadowPlayer(player.getUniqueId());
6082
}
6183

6284
/**
6385
* Remove player from shadowed players
6486
* @param uuid player uuid
6587
*/
6688
public static void unshadowPlayer(UUID uuid) {
89+
unshadowPlayer(uuid, true);
90+
}
91+
92+
public static void unshadowPlayer(UUID uuid, boolean notifyProxy) {
6793
Objects.requireNonNull(uuid, "uuid");
6894
SHADOWED_PLAYERS.remove(uuid);
95+
if (notifyProxy) {
96+
VelocitySyncClient client = AdvancedSensitiveWords.getInstance().getVelocitySyncClient();
97+
if (client != null) {
98+
client.sendShadowClear(uuid);
99+
}
100+
}
69101
}
70102

71103
/**

paper/src/main/java/io/wdsj/asw/bukkit/proxy/velocity/sync/VelocitySyncClient.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.gson.JsonParser;
66
import io.wdsj.asw.bukkit.AdvancedSensitiveWords;
77
import io.wdsj.asw.bukkit.manage.notice.Notifier;
8+
import io.wdsj.asw.bukkit.manage.punish.PlayerShadowController;
89
import io.wdsj.asw.bukkit.manage.punish.ViolationCounter;
910
import io.wdsj.asw.bukkit.setting.PaperConfigurationService;
1011
import io.wdsj.asw.bukkit.setting.PluginMessages;
@@ -104,6 +105,32 @@ public void query(Player player) {
104105
message.addProperty("playerUuid", player.getUniqueId().toString());
105106
message.addProperty("playerName", player.getName());
106107
send(message);
108+
109+
JsonObject shadowQuery = base(VelocitySyncProtocol.TYPE_SHADOW_QUERY);
110+
shadowQuery.addProperty("playerUuid", player.getUniqueId().toString());
111+
shadowQuery.addProperty("playerName", player.getName());
112+
send(shadowQuery);
113+
}
114+
115+
public void sendShadowSet(UUID playerId, long expiresAtMillis) {
116+
if (!authenticated()) {
117+
return;
118+
}
119+
JsonObject message = base(VelocitySyncProtocol.TYPE_SHADOW_SET);
120+
message.addProperty("requestId", UUID.randomUUID().toString());
121+
message.addProperty("playerUuid", playerId.toString());
122+
message.addProperty("expiresAtMillis", expiresAtMillis);
123+
send(message);
124+
}
125+
126+
public void sendShadowClear(UUID playerId) {
127+
if (!authenticated()) {
128+
return;
129+
}
130+
JsonObject message = base(VelocitySyncProtocol.TYPE_SHADOW_CLEAR);
131+
message.addProperty("requestId", UUID.randomUUID().toString());
132+
message.addProperty("playerUuid", playerId.toString());
133+
send(message);
107134
}
108135

109136
@EventHandler
@@ -215,6 +242,7 @@ private void handleMessage(String raw) {
215242
case VelocitySyncProtocol.TYPE_VL_SYNC -> handleSync(message);
216243
case VelocitySyncProtocol.TYPE_VL_RESET -> handleReset(message);
217244
case VelocitySyncProtocol.TYPE_VL_RESET_ALL -> handleResetAll();
245+
case VelocitySyncProtocol.TYPE_SHADOW_SYNC -> handleShadowSync(message);
218246
case VelocitySyncProtocol.TYPE_PING -> send(base(VelocitySyncProtocol.TYPE_PONG));
219247
default -> {
220248
}
@@ -259,6 +287,19 @@ private void handleResetAll() {
259287
Notifier.normalNotice(MessageUtils.retrieveMessage(PluginMessages.MESSAGE_ON_VIOLATION_RESET));
260288
}
261289

290+
private void handleShadowSync(JsonObject message) {
291+
UUID playerId = uuid(message, "playerUuid");
292+
if (playerId == null) {
293+
return;
294+
}
295+
long expiresAtMillis = longValue(message, "expiresAtMillis", 0L);
296+
if (expiresAtMillis <= System.currentTimeMillis()) {
297+
PlayerShadowController.unshadowPlayer(playerId, false);
298+
return;
299+
}
300+
PlayerShadowController.shadowPlayerUntil(playerId, expiresAtMillis, false);
301+
}
302+
262303
private static JsonObject object(JsonObject object, String key) {
263304
JsonElement element = object.get(key);
264305
return element != null && element.isJsonObject() ? element.getAsJsonObject() : null;
@@ -278,6 +319,15 @@ private static String string(JsonObject object, String key) {
278319
return element == null || element.isJsonNull() ? null : element.getAsString();
279320
}
280321

322+
private static long longValue(JsonObject object, String key, long fallback) {
323+
try {
324+
JsonElement element = object.get(key);
325+
return element == null || element.isJsonNull() ? fallback : element.getAsLong();
326+
} catch (Exception exception) {
327+
return fallback;
328+
}
329+
}
330+
281331
private final class BackendClient extends WebSocketClient {
282332
private BackendClient(URI serverUri) {
283333
super(serverUri);

paper/src/main/kotlin/io/wdsj/asw/bukkit/listener/QuitDataCleaner.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.wdsj.asw.bukkit.listener
22

33
import io.wdsj.asw.bukkit.service.chat.antispam.ChatAntiSpamService
4-
import io.wdsj.asw.bukkit.manage.punish.PlayerShadowController
54
import io.wdsj.asw.bukkit.util.context.ChatContext
65
import io.wdsj.asw.bukkit.util.context.SignContext
76
import org.bukkit.entity.Player
@@ -28,6 +27,5 @@ class QuitDataCleaner : Listener {
2827
ChatContext.clearPlayerContext(player)
2928
SignContext.clearPlayerContext(player)
3029
ChatAntiSpamService.clear(player.uniqueId)
31-
PlayerShadowController.unshadowPlayer(player)
3230
}
3331
}

velocity/src/main/java/io/wdsj/asw/velocity/sync/VelocitySyncWebSocketServer.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public final class VelocitySyncWebSocketServer implements AutoCloseable {
3333
private final Map<WebSocket, Session> sessions = new ConcurrentHashMap<>();
3434
private final Map<String, Long> nonceHistory = new ConcurrentHashMap<>();
3535
private final Map<UUID, Map<ModuleType, AtomicLong>> counts = new ConcurrentHashMap<>();
36+
private final Map<UUID, Long> shadowExpiresAt = new ConcurrentHashMap<>();
3637

3738
public VelocitySyncWebSocketServer(AdvancedSensitiveWords plugin, Logger logger, Config config) {
3839
this.plugin = plugin;
@@ -69,6 +70,7 @@ public void close() {
6970
sessions.clear();
7071
nonceHistory.clear();
7172
counts.clear();
73+
shadowExpiresAt.clear();
7274
}
7375

7476
private void handle(WebSocket socket, String raw) {
@@ -95,6 +97,9 @@ private void handle(WebSocket socket, String raw) {
9597
case VelocitySyncProtocol.TYPE_VL_INCREMENT -> handleIncrement(socket, message);
9698
case VelocitySyncProtocol.TYPE_VL_QUERY -> sendSync(socket, uuid(message, "playerUuid"));
9799
case VelocitySyncProtocol.TYPE_VL_RESET_REQUEST -> handleResetRequest(message);
100+
case VelocitySyncProtocol.TYPE_SHADOW_SET -> handleShadowSet(message);
101+
case VelocitySyncProtocol.TYPE_SHADOW_CLEAR -> handleShadowClear(message);
102+
case VelocitySyncProtocol.TYPE_SHADOW_QUERY -> sendShadowSync(socket, uuid(message, "playerUuid"));
98103
case VelocitySyncProtocol.TYPE_PING -> socket.send(base(VelocitySyncProtocol.TYPE_PONG).toString());
99104
default -> {
100105
}
@@ -171,6 +176,61 @@ private void resetAll() {
171176
broadcast(base(VelocitySyncProtocol.TYPE_VL_RESET_ALL));
172177
}
173178

179+
private void handleShadowSet(JsonObject message) {
180+
UUID playerId = uuid(message, "playerUuid");
181+
long expiresAtMillis = longValue(message, "expiresAtMillis", 0L);
182+
if (playerId == null) {
183+
return;
184+
}
185+
if (expiresAtMillis <= Instant.now().toEpochMilli()) {
186+
shadowExpiresAt.remove(playerId);
187+
broadcastShadowSync(playerId, 0L);
188+
return;
189+
}
190+
shadowExpiresAt.put(playerId, expiresAtMillis);
191+
broadcastShadowSync(playerId, expiresAtMillis);
192+
}
193+
194+
private void handleShadowClear(JsonObject message) {
195+
UUID playerId = uuid(message, "playerUuid");
196+
if (playerId == null) {
197+
return;
198+
}
199+
shadowExpiresAt.remove(playerId);
200+
broadcastShadowSync(playerId, 0L);
201+
}
202+
203+
private void sendShadowSync(WebSocket socket, UUID playerId) {
204+
if (playerId == null) {
205+
return;
206+
}
207+
long expiresAtMillis = activeShadowExpiresAt(playerId);
208+
socket.send(shadowSyncMessage(playerId, expiresAtMillis).toString());
209+
}
210+
211+
private void broadcastShadowSync(UUID playerId, long expiresAtMillis) {
212+
broadcast(shadowSyncMessage(playerId, expiresAtMillis));
213+
}
214+
215+
private JsonObject shadowSyncMessage(UUID playerId, long expiresAtMillis) {
216+
JsonObject message = base(VelocitySyncProtocol.TYPE_SHADOW_SYNC);
217+
message.addProperty("playerUuid", playerId.toString());
218+
message.addProperty("expiresAtMillis", expiresAtMillis);
219+
return message;
220+
}
221+
222+
private long activeShadowExpiresAt(UUID playerId) {
223+
Long expiresAtMillis = shadowExpiresAt.get(playerId);
224+
if (expiresAtMillis == null) {
225+
return 0L;
226+
}
227+
if (expiresAtMillis <= Instant.now().toEpochMilli()) {
228+
shadowExpiresAt.remove(playerId, expiresAtMillis);
229+
return 0L;
230+
}
231+
return expiresAtMillis;
232+
}
233+
174234
private void broadcastSync(UUID playerId) {
175235
JsonObject message = syncMessage(playerId);
176236
if (message != null) {
@@ -235,6 +295,7 @@ private void closeStaleSessions() {
235295
socket.close(1001, "Heartbeat timeout");
236296
}
237297
});
298+
shadowExpiresAt.entrySet().removeIf(entry -> entry.getValue() <= now);
238299
}
239300

240301
private static ModuleType parseModule(String module) {

0 commit comments

Comments
 (0)