mirror of
https://github.com/Shiewk/SModeration.git
synced 2026-04-28 05:54:16 +02:00
Rework save format and punishment manager
- Punishments are now saved as JSON in files named after their targets - Each punishment type now has its own Java class - Each file contains a list of JSON objects that is updated every time something changes (e.g. player muted, banned or unbanned) - Punishments have a unique ID now; if something changes, the new version is added to the list and overwrites the old version - 'Undo' has been renamed to 'cancel' - You can no longer mute or ban players if they are already muted or banned
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import de.shiewk.smoderation.paper.inventory.CustomInventory;
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Ban extends TimedPunishment {
|
||||
|
||||
public Ban(UUID id, long timestamp, UUID issuer, UUID target, String reason, long duration, UUID cancelledBy) {
|
||||
super(id, "ban", timestamp, issuer, target, reason, duration, cancelledBy);
|
||||
}
|
||||
|
||||
public Ban(UUID id, long timestamp, UUID issuer, UUID target, String reason, long duration) {
|
||||
this(id, timestamp, issuer, target, reason, duration, null);
|
||||
}
|
||||
|
||||
public static class Factory implements PunishmentFactory<Ban> {
|
||||
|
||||
@Override
|
||||
public @NonNull Ban deserialize(SerializationHelper helper) {
|
||||
return new Ban(
|
||||
helper.getUUID("id"),
|
||||
helper.getLong("timestamp"),
|
||||
helper.getUUID("issuer"),
|
||||
helper.getUUID("target"),
|
||||
helper.getString("reason"),
|
||||
helper.getLong("duration"),
|
||||
helper.getUUID("cancelledBy", null)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processIssue() {
|
||||
super.processIssue();
|
||||
final Player player = Bukkit.getPlayer(getTargetID());
|
||||
if (player != null) {
|
||||
player.kick(CustomInventory.renderComponent(player, infoMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import de.shiewk.smoderation.paper.inventory.CustomInventory;
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Kick extends Punishment {
|
||||
|
||||
public Kick(UUID id, long timestamp, UUID issuer, UUID target, String reason) {
|
||||
super(id, "kick", timestamp, issuer, target, reason);
|
||||
}
|
||||
|
||||
public static class Factory implements PunishmentFactory<Kick> {
|
||||
|
||||
@Override
|
||||
public @NonNull Kick deserialize(SerializationHelper helper) {
|
||||
return new Kick(
|
||||
helper.getUUID("id"),
|
||||
helper.getLong("timestamp"),
|
||||
helper.getUUID("issuer"),
|
||||
helper.getUUID("target"),
|
||||
helper.getString("reason")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processIssue() {
|
||||
super.processIssue();
|
||||
final Player player = Bukkit.getPlayer(getTargetID());
|
||||
if (player != null) {
|
||||
player.kick(CustomInventory.renderComponent(player, infoMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Mute extends TimedPunishment {
|
||||
|
||||
public Mute(UUID id, long timestamp, UUID issuer, UUID target, String reason, long duration, UUID cancelledBy) {
|
||||
super(id, "mute", timestamp, issuer, target, reason, duration, cancelledBy);
|
||||
}
|
||||
|
||||
public Mute(UUID id, long timestamp, UUID issuer, UUID target, String reason, long duration) {
|
||||
this(id, timestamp, issuer, target, reason, duration, null);
|
||||
}
|
||||
|
||||
public static class Factory implements PunishmentFactory<Mute> {
|
||||
|
||||
@Override
|
||||
public @NonNull Mute deserialize(SerializationHelper helper) {
|
||||
return new Mute(
|
||||
helper.getUUID("id"),
|
||||
helper.getLong("timestamp"),
|
||||
helper.getUUID("issuer"),
|
||||
helper.getUUID("target"),
|
||||
helper.getString("reason"),
|
||||
helper.getLong("duration"),
|
||||
helper.getUUID("cancelledBy", null)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,205 +1,123 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import de.shiewk.smoderation.paper.event.PunishmentIssueEvent;
|
||||
import de.shiewk.smoderation.paper.storage.PunishmentContainer;
|
||||
import de.shiewk.smoderation.paper.util.ByteUtil;
|
||||
import de.shiewk.smoderation.paper.util.PlayerUtil;
|
||||
import de.shiewk.smoderation.paper.util.TimeUtil;
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import static de.shiewk.smoderation.paper.SModerationPaper.*;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public class Punishment {
|
||||
public abstract class Punishment {
|
||||
|
||||
public static final String DEFAULT_REASON = "No reason provided.";
|
||||
public final PunishmentType type;
|
||||
public final long time;
|
||||
public final long until;
|
||||
public final UUID by;
|
||||
public final UUID to;
|
||||
public final String reason;
|
||||
private UUID undoneBy;
|
||||
|
||||
public Punishment(PunishmentType type, long time, long until, UUID by, UUID to, String reason) {
|
||||
this(type, time, until, by, to, reason, null);
|
||||
}
|
||||
protected final UUID id;
|
||||
protected final String type;
|
||||
protected final long timestamp;
|
||||
protected final UUID issuer;
|
||||
protected final UUID target;
|
||||
protected final String reason;
|
||||
|
||||
private Punishment(PunishmentType type, long time, long until, UUID by, UUID to, String reason, UUID undoneBy) {
|
||||
protected Punishment(UUID id, String type, long timestamp, UUID issuer, UUID target, String reason) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.time = time;
|
||||
this.until = until;
|
||||
this.by = by;
|
||||
this.to = to;
|
||||
this.timestamp = timestamp;
|
||||
this.issuer = issuer;
|
||||
this.target = target;
|
||||
this.reason = reason;
|
||||
this.undoneBy = undoneBy;
|
||||
}
|
||||
|
||||
private static byte[] readStreamInternal(InputStream stream, int len) throws IOException {
|
||||
final byte[] bytes = stream.readNBytes(len);
|
||||
if (bytes.length != len){
|
||||
throw new EOFException("Stream has ended before enough bytes were read");
|
||||
}
|
||||
return bytes;
|
||||
public UUID getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static Punishment load(InputStream in) throws IOException {
|
||||
PunishmentType type = PunishmentType.values()[ByteUtil.bytesToInt(readStreamInternal(in, 4))];
|
||||
long time = ByteUtil.bytesToLong(readStreamInternal(in, 8));
|
||||
long until = ByteUtil.bytesToLong(readStreamInternal(in, 8));
|
||||
UUID by = ByteUtil.bytesToUuid(readStreamInternal(in, 16));
|
||||
UUID to = ByteUtil.bytesToUuid(readStreamInternal(in, 16));
|
||||
int reasonLen = ByteUtil.bytesToInt(readStreamInternal(in, 4));
|
||||
String reason = new String(readStreamInternal(in, reasonLen));
|
||||
UUID undoneBy = null;
|
||||
boolean undone = in.read() == 1;
|
||||
if (undone){
|
||||
undoneBy = ByteUtil.bytesToUuid(readStreamInternal(in, 16));
|
||||
}
|
||||
return new Punishment(type, time, until, by, to, reason, undoneBy);
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean wasUndone(){
|
||||
return undoneBy != null;
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public UUID undoneBy() {
|
||||
return undoneBy;
|
||||
public UUID getIssuerID() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public void undo(UUID undoneBy){
|
||||
if (this.undoneBy != null){
|
||||
throw new IllegalArgumentException("This punishment was already undone.");
|
||||
}
|
||||
this.undoneBy = undoneBy;
|
||||
public UUID getTargetID() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public boolean isActive(){
|
||||
return until > System.currentTimeMillis() && !wasUndone();
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public static Punishment mute(long time, long until, UUID by, UUID to, String reason){
|
||||
return new Punishment(PunishmentType.MUTE, time, until, by, to, reason);
|
||||
public void addSerializableProperties(SerializationHelper helper){
|
||||
helper.putUUID("id", id);
|
||||
helper.putString("type", type);
|
||||
helper.putLong("timestamp", timestamp);
|
||||
helper.putUUID("issuer", issuer);
|
||||
helper.putUUID("target", target);
|
||||
helper.putString("reason", reason);
|
||||
}
|
||||
|
||||
public static Punishment ban(long time, long until, UUID by, UUID to, String reason){
|
||||
return new Punishment(PunishmentType.BAN, time, until, by, to, reason);
|
||||
public boolean matchesSearchQuery(String query){
|
||||
if (query == null) return true;
|
||||
query = query.toLowerCase();
|
||||
return reason.toLowerCase().contains(query)
|
||||
|| issuer.toString().equalsIgnoreCase(query)
|
||||
|| target.toString().equalsIgnoreCase(query)
|
||||
|| PlayerUtil.offlinePlayerName(issuer).toLowerCase().contains(query)
|
||||
|| PlayerUtil.offlinePlayerName(target).toLowerCase().contains(query);
|
||||
}
|
||||
|
||||
public static Punishment kick(long time, UUID by, UUID to, String reason){
|
||||
return new Punishment(PunishmentType.KICK, time, time, by, to, reason);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream stream) throws IOException {
|
||||
stream.write(ByteUtil.intToBytes(type.ordinal()));
|
||||
stream.write(ByteUtil.longToBytes(time));
|
||||
stream.write(ByteUtil.longToBytes(until));
|
||||
stream.write(ByteUtil.uuidToBytes(by));
|
||||
stream.write(ByteUtil.uuidToBytes(to));
|
||||
final byte[] reasonBytes = reason.getBytes();
|
||||
stream.write(ByteUtil.intToBytes(reasonBytes.length));
|
||||
stream.write(reasonBytes);
|
||||
stream.write(wasUndone() ? 1 : 0);
|
||||
if (wasUndone()){
|
||||
stream.write(ByteUtil.uuidToBytes(undoneBy));
|
||||
}
|
||||
}
|
||||
|
||||
private Component undoMessage(){
|
||||
String key = "smod.punishment.undo." + type.name().toLowerCase();
|
||||
return translatable(key, text(PlayerUtil.offlinePlayerName(to)), text(PlayerUtil.offlinePlayerName(undoneBy)));
|
||||
}
|
||||
|
||||
public void broadcastUndo(PunishmentContainer container){
|
||||
for (CommandSender sender : container.collectBroadcastTargets()) {
|
||||
sender.sendMessage(undoMessage().colorIfAbsent(PRIMARY_COLOR));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Punishment{" +
|
||||
"type=" + type +
|
||||
", time=" + time +
|
||||
", until=" + until +
|
||||
", by=" + by +
|
||||
", to=" + to +
|
||||
", reason=" + reason +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static void issue(Punishment punishment, PunishmentContainer container){
|
||||
final PunishmentIssueEvent event = new PunishmentIssueEvent(punishment, container);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()){
|
||||
container.add(punishment);
|
||||
punishment.firstIssue(container);
|
||||
}
|
||||
}
|
||||
|
||||
private Component broadcastMessage(){
|
||||
String key = "smod.punishment.broadcast." + type.name().toLowerCase();
|
||||
public Component infoMessage(){
|
||||
return translatable(
|
||||
key,
|
||||
text(PlayerUtil.offlinePlayerName(to)),
|
||||
text(PlayerUtil.offlinePlayerName(by)),
|
||||
TimeUtil.formatTimeLong(this.until - this.time),
|
||||
"smod.punishment.playerMessage." + type,
|
||||
text(PlayerUtil.offlinePlayerName(this.issuer)),
|
||||
text(reason)
|
||||
);
|
||||
}
|
||||
|
||||
private void broadcastIssue(PunishmentContainer container){
|
||||
for (CommandSender sender : container.collectBroadcastTargets()) {
|
||||
sender.sendMessage(broadcastMessage().colorIfAbsent(PRIMARY_COLOR));
|
||||
}
|
||||
}
|
||||
|
||||
private void firstIssue(PunishmentContainer container){
|
||||
switch (type) {
|
||||
case MUTE, BAN -> {
|
||||
final CommandSender sender = PlayerUtil.senderByUUID(to);
|
||||
if (sender != null) {
|
||||
sender.sendMessage(playerMessage().colorIfAbsent(PRIMARY_COLOR));
|
||||
}
|
||||
}
|
||||
}
|
||||
broadcastIssue(container);
|
||||
}
|
||||
|
||||
public Component playerMessage(){
|
||||
String key = "smod.punishment.playerMessage." + type.name().toLowerCase();
|
||||
public Component adminMessage(){
|
||||
return translatable(
|
||||
key,
|
||||
text(PlayerUtil.offlinePlayerName(this.by)),
|
||||
text(reason),
|
||||
TimeUtil.formatTimeLong(this.until - System.currentTimeMillis())
|
||||
"smod.punishment.broadcast." + type,
|
||||
text(PlayerUtil.offlinePlayerName(target)),
|
||||
text(PlayerUtil.offlinePlayerName(issuer)),
|
||||
text(reason)
|
||||
);
|
||||
}
|
||||
|
||||
public boolean matchesSearchQuery(String searchQuery) {
|
||||
if (searchQuery == null) return true;
|
||||
searchQuery = searchQuery.toLowerCase();
|
||||
return reason.toLowerCase().contains(searchQuery)
|
||||
|| by.toString().equalsIgnoreCase(searchQuery)
|
||||
|| to.toString().equalsIgnoreCase(searchQuery)
|
||||
|| getPlayerName().toLowerCase().contains(searchQuery)
|
||||
|| getModeratorName().toLowerCase().contains(searchQuery);
|
||||
|
||||
public void processIssue() {
|
||||
CommandSender sender = PlayerUtil.senderByUUID(target);
|
||||
if (sender != null) {
|
||||
sender.sendMessage(infoMessage());
|
||||
}
|
||||
for (CommandSender target : getBroadcastTargets()) {
|
||||
target.sendMessage(adminMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getPlayerName() {
|
||||
return PlayerUtil.offlinePlayerName(to);
|
||||
public static List<CommandSender> getBroadcastTargets() {
|
||||
ObjectArrayList<CommandSender> senders = new ObjectArrayList<>();
|
||||
senders.add(Bukkit.getConsoleSender());
|
||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||
if (onlinePlayer.hasPermission("smod.notifications")){
|
||||
senders.add(onlinePlayer);
|
||||
}
|
||||
}
|
||||
return List.copyOf(senders);
|
||||
}
|
||||
|
||||
private String getModeratorName() {
|
||||
return PlayerUtil.offlinePlayerName(by);
|
||||
public static UUID generateUUID() {
|
||||
Random random = new Random();
|
||||
return new UUID(System.currentTimeMillis(), random.nextLong());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface PunishmentFactory<T extends Punishment> {
|
||||
|
||||
@NotNull T deserialize(SerializationHelper helper);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.Strictness;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import de.shiewk.smoderation.paper.SModerationPaper;
|
||||
import de.shiewk.smoderation.paper.event.PunishmentIssueEvent;
|
||||
import de.shiewk.smoderation.paper.util.PlayerUtil;
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static de.shiewk.smoderation.paper.SModerationPaper.LOGGER;
|
||||
|
||||
public final class PunishmentManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PunishmentManager.class);
|
||||
private final Object2ObjectArrayMap<String, PunishmentFactory<?>> typeRegistry = new Object2ObjectArrayMap<>(1);
|
||||
private final Object ioLock = new Object();
|
||||
private final Path dataDir;
|
||||
|
||||
public PunishmentManager(Path dataDir) {
|
||||
this.dataDir = dataDir;
|
||||
}
|
||||
|
||||
private Path getTargetFile(UUID targetUUID){
|
||||
return dataDir.resolve(targetUUID.toString().replace("-", ""));
|
||||
}
|
||||
|
||||
public boolean tryIssue(Punishment punishment) {
|
||||
try {
|
||||
PunishmentIssueEvent event = new PunishmentIssueEvent(punishment, this);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()){
|
||||
this.appendToSave(punishment);
|
||||
punishment.processIssue();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Punishment> byTargetUUID(UUID target) {
|
||||
synchronized (ioLock) {
|
||||
Path file = getTargetFile(target);
|
||||
if (!Files.exists(file)) {
|
||||
return List.of();
|
||||
}
|
||||
try (
|
||||
BufferedReader reader = Files.newBufferedReader(file);
|
||||
JsonReader json = new JsonReader(reader)
|
||||
) {
|
||||
json.setStrictness(Strictness.LENIENT);
|
||||
Object2ObjectArrayMap<UUID, Punishment> punishments = new Object2ObjectArrayMap<>(0);
|
||||
while (json.hasNext()){
|
||||
JsonObject obj = SModerationPaper.gson.fromJson(json, JsonObject.class);
|
||||
try {
|
||||
SerializationHelper helper = new SerializationHelper(obj);
|
||||
String type = helper.getString("type");
|
||||
PunishmentFactory<?> factory = typeRegistry.get(type);
|
||||
if (factory != null){
|
||||
Punishment punishment = factory.deserialize(helper);
|
||||
if (!punishment.getTargetID().equals(target)){
|
||||
LOGGER.warn("Punishment saved in file for {} has incorrect target UUID {}", target, punishment.getTargetID());
|
||||
} else {
|
||||
punishments.put(punishment.getID(), punishment);
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("Unknown punishment type '{}'! Can not load.", type);
|
||||
LOGGER.warn("Please check your configuration, or see file {} to remove corrupted data.", file);
|
||||
LOGGER.warn(obj.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Could not deserialize punishment!", e);
|
||||
LOGGER.warn("Please check file {} for corrupted data, or remove the corresponding line.", file);
|
||||
LOGGER.warn(obj.toString());
|
||||
}
|
||||
}
|
||||
return List.copyOf(punishments.values());
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException("Error while reading punishment file " + file, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Punishment> byTargetUUID(UUID target, Predicate<Punishment> filter) {
|
||||
return byTargetUUID(target).stream().filter(filter).toList();
|
||||
}
|
||||
|
||||
public <T extends Punishment> void registerType(String type, PunishmentFactory<T> factory){
|
||||
if (typeRegistry.containsKey(type)) {
|
||||
throw new IllegalStateException("Punishment type already registered: " + type);
|
||||
}
|
||||
typeRegistry.put(type, factory);
|
||||
}
|
||||
|
||||
public List<String> getRegisteredTypes(){
|
||||
return List.copyOf(typeRegistry.keySet());
|
||||
}
|
||||
|
||||
private void appendToSave(Punishment punishment) throws IOException {
|
||||
synchronized (ioLock) {
|
||||
Path file = getTargetFile(punishment.getTargetID());
|
||||
if (!Files.exists(file)) {
|
||||
Files.createDirectories(dataDir);
|
||||
Files.createFile(file);
|
||||
}
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardOpenOption.APPEND)) {
|
||||
JsonObject json = new JsonObject();
|
||||
punishment.addSerializableProperties(new SerializationHelper(json));
|
||||
SModerationPaper.gson.toJson(json, writer);
|
||||
writer.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @NotNull List<Punishment> getAll() throws IOException {
|
||||
ObjectArrayList<Punishment> punishments = new ObjectArrayList<>();
|
||||
synchronized (ioLock) {
|
||||
try (Stream<Path> stream = Files.list(dataDir)) {
|
||||
stream.forEach(file -> {
|
||||
try {
|
||||
String name = file.getFileName().toString();
|
||||
UUID targetUUID = PlayerUtil.uuidFromString(name);
|
||||
punishments.addAll(byTargetUUID(targetUUID));
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not read punishment file {}", file, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return List.copyOf(punishments);
|
||||
}
|
||||
|
||||
public List<Punishment> getAll(Predicate<Punishment> filter) throws IOException {
|
||||
return getAll().stream().filter(filter).toList();
|
||||
}
|
||||
|
||||
public void cancel(TimedPunishment punishment, UUID canceller) {
|
||||
if (!punishment.isActive()){
|
||||
throw new IllegalStateException("This punishment is not active");
|
||||
}
|
||||
punishment.cancel(canceller);
|
||||
try {
|
||||
appendToSave(punishment);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public enum PunishmentType {
|
||||
MUTE(translatable("smod.punishment.name.mute")),
|
||||
KICK(translatable("smod.punishment.name.kick")),
|
||||
BAN(translatable("smod.punishment.name.ban"));
|
||||
|
||||
public final Component name;
|
||||
|
||||
PunishmentType(Component name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package de.shiewk.smoderation.paper.punishments;
|
||||
|
||||
import de.shiewk.smoderation.paper.util.PlayerUtil;
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import de.shiewk.smoderation.paper.util.TimeUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public abstract class TimedPunishment extends Punishment {
|
||||
|
||||
protected final long duration;
|
||||
protected UUID cancelledBy;
|
||||
|
||||
protected TimedPunishment(UUID id, String type, long timestamp, UUID issuer, UUID target, String reason, long duration, UUID cancelledBy) {
|
||||
super(id, type, timestamp, issuer, target, reason);
|
||||
this.duration = duration;
|
||||
this.cancelledBy = cancelledBy;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public UUID getCancelledBy() {
|
||||
return cancelledBy;
|
||||
}
|
||||
|
||||
public boolean wasCancelled(){
|
||||
return cancelledBy != null;
|
||||
}
|
||||
|
||||
public boolean isActive(){
|
||||
return !wasCancelled() && System.currentTimeMillis() < timestamp + duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSerializableProperties(SerializationHelper helper) {
|
||||
super.addSerializableProperties(helper);
|
||||
helper.putLong("duration", duration);
|
||||
helper.putUUID("cancelledBy", cancelledBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSearchQuery(String query) {
|
||||
if (super.matchesSearchQuery(query)) return true;
|
||||
query = query.toLowerCase();
|
||||
return cancelledBy.toString().equalsIgnoreCase(query)
|
||||
|| PlayerUtil.offlinePlayerName(cancelledBy).toLowerCase().contains(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component infoMessage(){
|
||||
return translatable(
|
||||
"smod.punishment.playerMessage." + type,
|
||||
text(PlayerUtil.offlinePlayerName(this.issuer)),
|
||||
text(reason),
|
||||
TimeUtil.formatTimeLong(this.timestamp + this.duration - System.currentTimeMillis())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component adminMessage(){
|
||||
return translatable(
|
||||
"smod.punishment.broadcast." + type,
|
||||
text(PlayerUtil.offlinePlayerName(target)),
|
||||
text(PlayerUtil.offlinePlayerName(issuer)),
|
||||
text(reason),
|
||||
TimeUtil.formatTimeLong(this.duration)
|
||||
);
|
||||
}
|
||||
|
||||
public Component cancelMessage(){
|
||||
return translatable(
|
||||
"smod.punishment.cancel." + type,
|
||||
text(PlayerUtil.offlinePlayerName(target)),
|
||||
text(PlayerUtil.offlinePlayerName(cancelledBy))
|
||||
);
|
||||
}
|
||||
|
||||
public long getExpiry() {
|
||||
return getTimestamp() + getDuration();
|
||||
}
|
||||
|
||||
protected void cancel(UUID canceller) {
|
||||
if (this.cancelledBy != null){
|
||||
throw new IllegalArgumentException("This punishment was already cancelled.");
|
||||
}
|
||||
this.cancelledBy = canceller;
|
||||
for (CommandSender sender : getBroadcastTargets()) {
|
||||
sender.sendMessage(cancelMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user