Skip to content

Commit 1a54367

Browse files
fix(punishment): per-type Redis keys, init guards, persist for permanent punishments
- Key schema changed to punish:active:{uuid}:{type} so bans and mutes coexist - Added isInitialized() guard on all public methods to prevent NPE - Added jedis.persist() for permanent punishments to clear stale TTLs - revoke() now takes a type parameter and is synchronous - Fixed banMessage computing timeLeft unconditionally for permanent bans
1 parent d52b19c commit 1a54367

2 files changed

Lines changed: 47 additions & 32 deletions

File tree

commons/src/main/java/net/swofty/commons/punishment/PunishmentMessages.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ public static Component banMessage(ActivePunishment punishment) {
1111
PunishmentReason reason = punishment.reason();
1212
String banId = punishment.banId();
1313

14-
long timeLeft = expiresAt - System.currentTimeMillis();
15-
String prettyTimeLeft = StringUtility.formatTimeLeft(timeLeft);
16-
1714
String header;
1815
if (expiresAt <= 0) {
1916
header = "§cYou are permanently banned from this server!\n";
2017
} else {
18+
long timeLeft = expiresAt - System.currentTimeMillis();
19+
String prettyTimeLeft = StringUtility.formatTimeLeft(timeLeft);
2120
header = "§cYou are temporarily banned for §f" + prettyTimeLeft + " §cfrom this server!\n";
2221
}
2322

commons/src/main/java/net/swofty/commons/punishment/PunishmentRedis.java

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88

99
import java.net.URI;
1010
import java.time.Duration;
11-
import java.util.Map;
12-
import java.util.Optional;
13-
import java.util.UUID;
11+
import java.util.*;
1412
import java.util.concurrent.CompletableFuture;
1513

1614
public class PunishmentRedis {
17-
private static final String PREFIX = "punish:";
15+
private static final String PREFIX = "punish:active:";
16+
private static final Gson GSON = new Gson();
1817
private static JedisPool jedisPool;
1918
private static volatile boolean initialized = false;
2019
private static volatile boolean connecting = false;
@@ -40,7 +39,6 @@ private static synchronized void connectSync(String redisUri) {
4039
URI uri = URI.create(redisUri);
4140
jedisPool = new JedisPool(poolConfig, uri);
4241

43-
// Test connection
4442
try (Jedis jedis = jedisPool.getResource()) {
4543
jedis.ping();
4644
}
@@ -60,66 +58,84 @@ public static boolean isInitialized() {
6058
return initialized && jedisPool != null && !jedisPool.isClosed();
6159
}
6260

63-
public static void saveActivePunishment(UUID playerId, String type, String id, PunishmentReason reason, long expiresAt, java.util.List<PunishmentTag> tags) {
61+
private static String key(UUID playerId, String type) {
62+
return PREFIX + playerId + ":" + type;
63+
}
64+
65+
public static void saveActivePunishment(UUID playerId, String type, String id,
66+
PunishmentReason reason, long expiresAt,
67+
List<PunishmentTag> tags) {
68+
if (!isInitialized()) throw new IllegalStateException("PunishmentRedis not initialized");
69+
6470
try (Jedis jedis = jedisPool.getResource()) {
65-
String key = PREFIX + "active:" + playerId;
71+
String k = key(playerId, type);
6672

67-
Gson gson = new Gson();
68-
java.util.HashMap<String, String> data = new java.util.HashMap<>(Map.of(
73+
HashMap<String, String> data = new HashMap<>(Map.of(
6974
"type", type,
7075
"banId", id,
71-
"reason", gson.toJson(reason),
76+
"reason", GSON.toJson(reason),
7277
"expiresAt", String.valueOf(expiresAt)
7378
));
7479
if (tags != null && !tags.isEmpty()) {
75-
data.put("tags", gson.toJson(tags));
80+
data.put("tags", GSON.toJson(tags));
7681
}
7782

78-
jedis.hset(key, data);
83+
jedis.hset(k, data);
7984
if (expiresAt > 0) {
8085
long ttlSeconds = (expiresAt - System.currentTimeMillis()) / 1000;
8186
if (ttlSeconds > 0) {
82-
jedis.expire(key, (int) ttlSeconds);
87+
jedis.expire(k, (int) ttlSeconds);
8388
}
89+
} else {
90+
jedis.persist(k);
8491
}
8592
}
8693
}
8794

88-
public static Optional<ActivePunishment> getActive(UUID playerId) {
95+
public static Optional<ActivePunishment> getActive(UUID playerId, String type) {
96+
if (!isInitialized()) return Optional.empty();
97+
8998
try (Jedis jedis = jedisPool.getResource()) {
90-
String key = PREFIX + "active:" + playerId;
91-
Map<String, String> data = jedis.hgetAll(key);
99+
String k = key(playerId, type);
100+
Map<String, String> data = jedis.hgetAll(k);
92101

93102
if (data.isEmpty()) return Optional.empty();
94103

95-
String type = data.get("type");
96104
String banId = data.get("banId");
97105
long expiresAt = Long.parseLong(data.getOrDefault("expiresAt", "-1"));
98106

99107
if (expiresAt > 0 && System.currentTimeMillis() > expiresAt) {
100-
jedis.del(key); // clean up expired
108+
jedis.del(k);
101109
return Optional.empty();
102110
}
103111

104-
Gson gson = new Gson();
105-
PunishmentReason reason = gson.fromJson(data.get("reason"), PunishmentReason.class);
112+
PunishmentReason reason = GSON.fromJson(data.get("reason"), PunishmentReason.class);
106113

107-
java.util.List<PunishmentTag> tags = java.util.List.of();
114+
List<PunishmentTag> tags = List.of();
108115
String tagsJson = data.get("tags");
109116
if (tagsJson != null && !tagsJson.isBlank()) {
110-
tags = java.util.List.of(gson.fromJson(tagsJson, PunishmentTag[].class));
117+
tags = List.of(GSON.fromJson(tagsJson, PunishmentTag[].class));
111118
}
112119

113120
return Optional.of(new ActivePunishment(type, banId, reason, expiresAt, tags));
114121
}
115122
}
116123

117-
public static CompletableFuture<Long> revoke(UUID playerId) {
118-
return CompletableFuture.supplyAsync(() -> {
119-
try (Jedis jedis = jedisPool.getResource()) {
120-
String key = PREFIX + "active:" + playerId;
121-
return jedis.del(key);
122-
}
123-
});
124+
public static List<ActivePunishment> getAllActive(UUID playerId) {
125+
if (!isInitialized()) return List.of();
126+
127+
List<ActivePunishment> result = new ArrayList<>();
128+
for (PunishmentType pt : PunishmentType.values()) {
129+
getActive(playerId, pt.name()).ifPresent(result::add);
130+
}
131+
return result;
132+
}
133+
134+
public static void revoke(UUID playerId, String type) {
135+
if (!isInitialized()) throw new IllegalStateException("PunishmentRedis not initialized");
136+
137+
try (Jedis jedis = jedisPool.getResource()) {
138+
jedis.del(key(playerId, type));
139+
}
124140
}
125141
}

0 commit comments

Comments
 (0)