Skip to content

Commit 6ebaf9b

Browse files
refactor(punishment): move business logic to service layer (DIP)
Move duplicate-punishment check from BanCommand into PunishPlayerEndpoint with ALREADY_PUNISHED error code. Create UnpunishPlayerProtocolObject and UnpunishPlayerEndpoint so UnBanCommand goes through the service layer instead of calling PunishmentRedis directly. Move proxy message framing into ProxyRedis.publishToProxy().
1 parent 7ec5460 commit 6ebaf9b

6 files changed

Lines changed: 171 additions & 25 deletions

File tree

commons/src/main/java/net/swofty/commons/protocol/objects/punishment/PunishPlayerProtocolObject.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public enum ErrorCode {
115115
INVALID_TYPE,
116116
DATABASE_ERROR,
117117
INVALID_EXPIRY,
118+
ALREADY_PUNISHED,
118119
UNKNOWN_ERROR
119120
}
120121
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package net.swofty.commons.protocol.objects.punishment;
2+
3+
import net.swofty.commons.protocol.ProtocolObject;
4+
import net.swofty.commons.protocol.Serializer;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
import org.json.JSONObject;
8+
9+
import java.util.UUID;
10+
11+
public class UnpunishPlayerProtocolObject
12+
extends ProtocolObject<UnpunishPlayerProtocolObject.UnpunishPlayerMessage,
13+
UnpunishPlayerProtocolObject.UnpunishPlayerResponse> {
14+
15+
@Override
16+
public Serializer<UnpunishPlayerMessage> getSerializer() {
17+
return new Serializer<>() {
18+
@Override
19+
public String serialize(UnpunishPlayerMessage value) {
20+
JSONObject json = new JSONObject();
21+
json.put("target", value.target().toString());
22+
json.put("staff", value.staff().toString());
23+
return json.toString();
24+
}
25+
26+
@Override
27+
public UnpunishPlayerMessage deserialize(String json) {
28+
JSONObject obj = new JSONObject(json);
29+
return new UnpunishPlayerMessage(
30+
UUID.fromString(obj.getString("target")),
31+
UUID.fromString(obj.getString("staff"))
32+
);
33+
}
34+
35+
@Override
36+
public UnpunishPlayerMessage clone(UnpunishPlayerMessage value) {
37+
return value;
38+
}
39+
};
40+
}
41+
42+
@Override
43+
public Serializer<UnpunishPlayerResponse> getReturnSerializer() {
44+
return new Serializer<>() {
45+
@Override
46+
public String serialize(UnpunishPlayerResponse value) {
47+
JSONObject json = new JSONObject();
48+
json.put("success", value.success());
49+
json.put("errorMessage", value.errorMessage());
50+
return json.toString();
51+
}
52+
53+
@Override
54+
public UnpunishPlayerResponse deserialize(String json) {
55+
JSONObject obj = new JSONObject(json);
56+
return new UnpunishPlayerResponse(
57+
obj.getBoolean("success"),
58+
obj.optString("errorMessage", null)
59+
);
60+
}
61+
62+
@Override
63+
public UnpunishPlayerResponse clone(UnpunishPlayerResponse value) {
64+
return value;
65+
}
66+
};
67+
}
68+
69+
public record UnpunishPlayerMessage(
70+
@NotNull UUID target,
71+
@NotNull UUID staff
72+
) {}
73+
74+
public record UnpunishPlayerResponse(
75+
boolean success,
76+
@Nullable String errorMessage
77+
) {}
78+
}

service.punishment/src/main/java/net/swofty/service/punishment/ProxyRedis.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
import redis.clients.jedis.JedisPool;
55
import redis.clients.jedis.JedisPoolConfig;
66

7+
import net.swofty.commons.proxy.ToProxyChannels;
8+
import org.json.JSONObject;
9+
710
import java.net.URI;
811
import java.time.Duration;
12+
import java.util.UUID;
913
import java.util.concurrent.CompletableFuture;
1014

1115
public class ProxyRedis {
@@ -59,6 +63,12 @@ public static CompletableFuture<Void> publishMessage(String filterId, String cha
5963
});
6064
}
6165

66+
public static void publishToProxy(ToProxyChannels channel, JSONObject message) {
67+
UUID uuid = UUID.randomUUID();
68+
publishMessage("proxy", channel.getChannelName(),
69+
message.toString() + "}=-=-={" + uuid + "}=-=-={" + uuid);
70+
}
71+
6272
public static boolean isInitialized() {
6373
return initialized && jedisPool != null && !jedisPool.isClosed();
6474
}

service.punishment/src/main/java/net/swofty/service/punishment/endpoints/PunishPlayerEndpoint.java

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import org.tinylog.Logger;
1212

1313
import java.time.Instant;
14-
import java.util.UUID;
14+
import java.util.Optional;
1515

1616
public class PunishPlayerEndpoint implements ServiceEndpoint
1717
<PunishPlayerProtocolObject.PunishPlayerMessage,
@@ -24,30 +24,42 @@ public ProtocolObject<PunishPlayerProtocolObject.PunishPlayerMessage, PunishPlay
2424

2525
@Override
2626
public PunishPlayerProtocolObject.PunishPlayerResponse onMessage(ServiceProxyRequest message, PunishPlayerProtocolObject.PunishPlayerMessage messageObject) {
27-
Logger.info("PunishPlayerEndpoint onMessage");
27+
PunishmentType punishmentType;
2828
try {
29-
// Validate punishment type
30-
PunishmentType.valueOf(messageObject.type());
29+
punishmentType = PunishmentType.valueOf(messageObject.type());
3130
} catch (IllegalArgumentException e) {
3231
return new PunishPlayerProtocolObject.PunishPlayerResponse(false, null, PunishPlayerProtocolObject.ErrorCode.INVALID_TYPE, "The punishment type provided is invalid.");
3332
}
3433

35-
PunishmentReason reason = messageObject.reason();
36-
PunishmentId id = PunishmentId.generateId();
37-
3834
Instant now = Instant.now();
3935
if (messageObject.expiresAt() > 0 && Instant.ofEpochMilli(messageObject.expiresAt()).isBefore(now)) {
4036
return new PunishPlayerProtocolObject.PunishPlayerResponse(false, null, PunishPlayerProtocolObject.ErrorCode.INVALID_EXPIRY, "The expiration time provided is invalid.");
4137
}
4238

39+
boolean hasOverwriteTag = messageObject.tags() != null && messageObject.tags().contains(PunishmentTag.OVERWRITE);
40+
if (!hasOverwriteTag) {
41+
Optional<PunishmentRedis.ActivePunishment> existing = PunishmentRedis.getActive(messageObject.target());
42+
if (existing.isPresent()) {
43+
PunishmentRedis.ActivePunishment active = existing.get();
44+
PunishmentType existingType = PunishmentType.valueOf(active.type());
45+
if (existingType == punishmentType) {
46+
return new PunishPlayerProtocolObject.PunishPlayerResponse(false, null,
47+
PunishPlayerProtocolObject.ErrorCode.ALREADY_PUNISHED, active.banId());
48+
}
49+
}
50+
}
51+
52+
PunishmentReason reason = messageObject.reason();
53+
PunishmentId id = PunishmentId.generateId();
54+
4355
PunishmentRedis.saveActivePunishment(
4456
messageObject.target(),
4557
messageObject.type(),
4658
id.id(),
4759
reason,
4860
messageObject.expiresAt()
4961
);
50-
sendMessageToProxy(ToProxyChannels.PUNISH_PLAYER, new JSONObject()
62+
ProxyRedis.publishToProxy(ToProxyChannels.PUNISH_PLAYER, new JSONObject()
5163
.put("target", messageObject.target())
5264
.put("type", messageObject.type())
5365
.put("id", id.id())
@@ -65,12 +77,4 @@ public PunishPlayerProtocolObject.PunishPlayerResponse onMessage(ServiceProxyReq
6577
);
6678
return new PunishPlayerProtocolObject.PunishPlayerResponse(true, id.id(), null, null);
6779
}
68-
69-
public static void sendMessageToProxy(ToProxyChannels channel, JSONObject message) {
70-
UUID uuid = UUID.randomUUID();
71-
72-
ProxyRedis.publishMessage("proxy",
73-
channel.getChannelName(),
74-
message.toString() + "}=-=-={" + uuid + "}=-=-={" + uuid);
75-
}
7680
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package net.swofty.service.punishment.endpoints;
2+
3+
import net.swofty.commons.impl.ServiceProxyRequest;
4+
import net.swofty.commons.protocol.ProtocolObject;
5+
import net.swofty.commons.protocol.objects.punishment.UnpunishPlayerProtocolObject;
6+
import net.swofty.commons.punishment.PunishmentRedis;
7+
import net.swofty.service.generic.redis.ServiceEndpoint;
8+
import org.tinylog.Logger;
9+
10+
import java.util.Optional;
11+
12+
public class UnpunishPlayerEndpoint implements ServiceEndpoint
13+
<UnpunishPlayerProtocolObject.UnpunishPlayerMessage,
14+
UnpunishPlayerProtocolObject.UnpunishPlayerResponse> {
15+
16+
@Override
17+
public ProtocolObject<UnpunishPlayerProtocolObject.UnpunishPlayerMessage, UnpunishPlayerProtocolObject.UnpunishPlayerResponse> associatedProtocolObject() {
18+
return new UnpunishPlayerProtocolObject();
19+
}
20+
21+
@Override
22+
public UnpunishPlayerProtocolObject.UnpunishPlayerResponse onMessage(ServiceProxyRequest message, UnpunishPlayerProtocolObject.UnpunishPlayerMessage messageObject) {
23+
Optional<PunishmentRedis.ActivePunishment> existing = PunishmentRedis.getActive(messageObject.target());
24+
if (existing.isEmpty()) {
25+
return new UnpunishPlayerProtocolObject.UnpunishPlayerResponse(false, "No active punishment found for this player.");
26+
}
27+
28+
PunishmentRedis.revoke(messageObject.target()).join();
29+
Logger.info("Revoked punishment for {} by staff {}",
30+
messageObject.target(), messageObject.staff());
31+
return new UnpunishPlayerProtocolObject.UnpunishPlayerResponse(true, null);
32+
}
33+
}

type.generic/src/main/java/net/swofty/type/generic/command/commands/UnBanCommand.java

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
import net.minestom.server.command.builder.arguments.ArgumentType;
55
import net.minestom.server.command.builder.suggestion.SuggestionEntry;
66
import net.minestom.server.utils.mojang.MojangUtils;
7+
import net.swofty.commons.ServiceType;
8+
import net.swofty.commons.protocol.objects.punishment.UnpunishPlayerProtocolObject;
79
import net.swofty.commons.punishment.PunishmentRedis;
10+
import net.swofty.proxyapi.ProxyService;
811
import net.swofty.type.generic.command.CommandParameters;
912
import net.swofty.type.generic.command.HypixelCommand;
1013
import net.swofty.type.generic.user.categories.Rank;
1114

1215
import java.io.IOException;
1316
import java.util.Set;
14-
import java.util.UUID;
17+
import java.util.concurrent.CompletableFuture;
18+
import java.util.concurrent.TimeUnit;
1519

1620
@CommandParameters(
1721
description = "Unban a player from the server.",
@@ -35,14 +39,30 @@ public void registerUsage(MinestomCommand command) {
3539
command.addSyntax((sender, context) -> {
3640
String playerName = context.get(argument);
3741

38-
sender.sendMessage("§8Processing unban for player " + playerName + "...");
39-
try {
40-
PunishmentRedis.revoke(MojangUtils.getUUID(playerName)).thenRun(() -> {
41-
sender.sendMessage("§aSuccessfully unbanned player: " + playerName);
42-
});
43-
} catch (IOException e) {
44-
sender.sendMessage("§cCould not find player: " + playerName);
45-
}
42+
CompletableFuture.runAsync(() -> {
43+
try {
44+
var targetUuid = resolvePlayerUuid(sender, playerName, "unban");
45+
ProxyService punishmentService = new ProxyService(ServiceType.PUNISHMENT);
46+
var message = new UnpunishPlayerProtocolObject.UnpunishPlayerMessage(
47+
targetUuid, senderUuid(sender)
48+
);
49+
50+
punishmentService.handleRequest(message).thenAccept(result -> {
51+
if (result instanceof UnpunishPlayerProtocolObject.UnpunishPlayerResponse response) {
52+
if (response.success()) {
53+
sender.sendMessage("§aSuccessfully unbanned player: " + playerName);
54+
} else {
55+
sender.sendMessage("§c" + response.errorMessage());
56+
}
57+
}
58+
}).orTimeout(5, TimeUnit.SECONDS).exceptionally(_ -> {
59+
sender.sendMessage("§cCould not unban this player at this time. The punishment service may be offline.");
60+
return null;
61+
});
62+
} catch (IOException e) {
63+
sender.sendMessage("§cCould not find player: " + playerName);
64+
}
65+
});
4666
}, argument);
4767
}
4868
}

0 commit comments

Comments
 (0)