mirror of
https://github.com/Shiewk/SModeration.git
synced 2026-04-28 05:54:16 +02:00
Compare commits
4 Commits
823093be35
...
2bccf45439
| Author | SHA1 | Date | |
|---|---|---|---|
|
2bccf45439
|
|||
|
24da79642a
|
|||
|
ea54f83909
|
|||
|
6c76066d8b
|
@@ -10,6 +10,7 @@ import de.shiewk.smoderation.paper.punishments.Kick;
|
|||||||
import de.shiewk.smoderation.paper.punishments.Mute;
|
import de.shiewk.smoderation.paper.punishments.Mute;
|
||||||
import de.shiewk.smoderation.paper.punishments.PunishmentManager;
|
import de.shiewk.smoderation.paper.punishments.PunishmentManager;
|
||||||
import de.shiewk.smoderation.paper.translation.TranslatorManager;
|
import de.shiewk.smoderation.paper.translation.TranslatorManager;
|
||||||
|
import de.shiewk.smoderation.paper.util.SModLegacy;
|
||||||
import de.shiewk.smoderation.paper.util.SchedulerUtil;
|
import de.shiewk.smoderation.paper.util.SchedulerUtil;
|
||||||
import io.papermc.paper.command.brigadier.Commands;
|
import io.papermc.paper.command.brigadier.Commands;
|
||||||
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
|
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
|
||||||
@@ -79,6 +80,12 @@ public final class SModerationPaper extends JavaPlugin {
|
|||||||
this.punishmentManager.registerType("mute", new Mute.Factory());
|
this.punishmentManager.registerType("mute", new Mute.Factory());
|
||||||
this.punishmentManager.registerType("ban", new Ban.Factory());
|
this.punishmentManager.registerType("ban", new Ban.Factory());
|
||||||
this.punishmentManager.registerType("kick", new Kick.Factory());
|
this.punishmentManager.registerType("kick", new Kick.Factory());
|
||||||
|
|
||||||
|
SModLegacy.migrateV1PunishmentsFile(
|
||||||
|
this.punishmentManager,
|
||||||
|
getDataPath().resolve("container.gz"),
|
||||||
|
getDataPath().resolve("v1-backup.gz")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFeatureEnabled(String feature){
|
public boolean isFeatureEnabled(String feature){
|
||||||
@@ -88,6 +95,7 @@ public final class SModerationPaper extends JavaPlugin {
|
|||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
if (isFeatureEnabled("punishments")) listen(new PunishmentListener(punishmentManager));
|
if (isFeatureEnabled("punishments")) listen(new PunishmentListener(punishmentManager));
|
||||||
|
if (isFeatureEnabled("punishments")) listen(new CacheListener(punishmentManager));
|
||||||
if (isFeatureEnabled("invsee")) listen(new InvSeeListener());
|
if (isFeatureEnabled("invsee")) listen(new InvSeeListener());
|
||||||
if (isFeatureEnabled("enderchestsee")) listen(new EnderchestSeeListener());
|
if (isFeatureEnabled("enderchestsee")) listen(new EnderchestSeeListener());
|
||||||
if (isFeatureEnabled("socialspy")) listen(new SocialSpyListener());
|
if (isFeatureEnabled("socialspy")) listen(new SocialSpyListener());
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public final class BanCommand implements CommandProvider {
|
|||||||
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
||||||
UUID target = context.getArgument("player", UUID.class);
|
UUID target = context.getArgument("player", UUID.class);
|
||||||
long duration = context.getArgument("duration", Long.class);
|
long duration = context.getArgument("duration", Long.class);
|
||||||
executeBan(punishmentManager, sender, target, duration, Punishment.DEFAULT_REASON);
|
executeBan(punishmentManager, sender, target, duration, SModerationPaper.config().getString("default-reason", "No reason provided."));
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public final class KickCommand implements CommandProvider {
|
|||||||
}
|
}
|
||||||
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
||||||
Player target = CommandUtil.getPlayerSingle(context, "player");
|
Player target = CommandUtil.getPlayerSingle(context, "player");
|
||||||
executeKick(punishmentManager, sender, target, Punishment.DEFAULT_REASON);
|
executeKick(punishmentManager, sender, target, SModerationPaper.config().getString("default-reason", "No reason provided."));
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,11 +49,15 @@ public final class ModLogsCommand implements CommandProvider {
|
|||||||
List<Punishment> punishments = punishmentManager.byTargetUUID(uuid);
|
List<Punishment> punishments = punishmentManager.byTargetUUID(uuid);
|
||||||
for (Punishment punishment : punishments) {
|
for (Punishment punishment : punishments) {
|
||||||
if (punishment instanceof TimedPunishment timed && timed.isActive()){
|
if (punishment instanceof TimedPunishment timed && timed.isActive()){
|
||||||
sender.sendMessage(translatable("smod.command.modlogs." + punishment.getType(),
|
if (timed.isPermanent()){
|
||||||
TimeUtil.calendarTimestamp(timed.getExpiry()),
|
sender.sendMessage(translatable("smod.command.modlogs." + punishment.getType() + ".permanent", text(punishment.getReason())));
|
||||||
TimeUtil.formatTimeLong(timed.getExpiry() - System.currentTimeMillis()),
|
} else {
|
||||||
text(punishment.getReason())
|
sender.sendMessage(translatable("smod.command.modlogs." + punishment.getType(),
|
||||||
));
|
TimeUtil.calendarTimestamp(timed.getExpiry()),
|
||||||
|
TimeUtil.formatTimeLong(timed.getExpiry() - System.currentTimeMillis()),
|
||||||
|
text(punishment.getReason())
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (punishments.isEmpty()){
|
if (punishments.isEmpty()){
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public final class MuteCommand implements CommandProvider {
|
|||||||
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
||||||
UUID target = context.getArgument("player", UUID.class);
|
UUID target = context.getArgument("player", UUID.class);
|
||||||
long duration = context.getArgument("duration", Long.class);
|
long duration = context.getArgument("duration", Long.class);
|
||||||
executeMute(punishmentManager, sender, target, duration, Punishment.DEFAULT_REASON);
|
executeMute(punishmentManager, sender, target, duration, SModerationPaper.config().getString("default-reason", "No reason provided."));
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,13 @@ public final class DurationArgument implements CustomArgumentType.Converted<Long
|
|||||||
|
|
||||||
public static final Pattern DURATION_PATTERN = Pattern.compile("([0-9]{1,9})(ms|s|min|h|d|w|mo|y)");
|
public static final Pattern DURATION_PATTERN = Pattern.compile("([0-9]{1,9})(ms|s|min|h|d|w|mo|y)");
|
||||||
public static final Pattern VALIDATION_PATTERN = Pattern.compile("(([0-9]{1,9})(ms|s|min|h|d|w|mo|y))+");
|
public static final Pattern VALIDATION_PATTERN = Pattern.compile("(([0-9]{1,9})(ms|s|min|h|d|w|mo|y))+");
|
||||||
|
public static final long INFINITE_DURATION = -1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Long convert(@NotNull String nativeType) throws CommandSyntaxException {
|
public @NotNull Long convert(@NotNull String nativeType) throws CommandSyntaxException {
|
||||||
|
if (nativeType.startsWith("perm") || nativeType.startsWith("inf")) {
|
||||||
|
return INFINITE_DURATION;
|
||||||
|
}
|
||||||
if (!VALIDATION_PATTERN.matcher(nativeType).matches()){
|
if (!VALIDATION_PATTERN.matcher(nativeType).matches()){
|
||||||
CommandUtil.errorTranslatable("smod.argument.duration.fail.pattern");
|
CommandUtil.errorTranslatable("smod.argument.duration.fail.pattern");
|
||||||
}
|
}
|
||||||
@@ -58,6 +62,8 @@ public final class DurationArgument implements CustomArgumentType.Converted<Long
|
|||||||
public @NotNull <S> CompletableFuture<Suggestions> listSuggestions(@NotNull CommandContext<S> context, @NotNull SuggestionsBuilder builder) {
|
public @NotNull <S> CompletableFuture<Suggestions> listSuggestions(@NotNull CommandContext<S> context, @NotNull SuggestionsBuilder builder) {
|
||||||
if (builder.getRemaining().isBlank()){
|
if (builder.getRemaining().isBlank()){
|
||||||
List.of(
|
List.of(
|
||||||
|
"infinite", "inf",
|
||||||
|
"permanent", "perm",
|
||||||
"100ms",
|
"100ms",
|
||||||
"15s",
|
"15s",
|
||||||
"2min",
|
"2min",
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class SModMenu extends PageableCustomInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum Sort {
|
public enum Sort {
|
||||||
EXPIRY(translatable("smod.menu.sort.expiry"), Comparator.comparingLong(p -> p instanceof TimedPunishment timed ? p.getTimestamp() + timed.getDuration() : p.getTimestamp())),
|
EXPIRY(translatable("smod.menu.sort.expiry"), Comparator.comparingLong(p -> p instanceof TimedPunishment timed ? timed.getExpiry() : p.getTimestamp())),
|
||||||
TIME(translatable("smod.menu.sort.time"), Comparator.comparingLong(Punishment::getTimestamp)),
|
TIME(translatable("smod.menu.sort.time"), Comparator.comparingLong(Punishment::getTimestamp)),
|
||||||
PLAYER_NAME(translatable("smod.menu.sort.playerName"), (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.getTargetID()), PlayerUtil.offlinePlayerName(p2.getTargetID()))),
|
PLAYER_NAME(translatable("smod.menu.sort.playerName"), (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.getTargetID()), PlayerUtil.offlinePlayerName(p2.getTargetID()))),
|
||||||
MODERATOR_NAME(translatable("smod.menu.sort.moderatorName"), (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.getIssuerID()), PlayerUtil.offlinePlayerName(p2.getIssuerID())));
|
MODERATOR_NAME(translatable("smod.menu.sort.moderatorName"), (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.getIssuerID()), PlayerUtil.offlinePlayerName(p2.getIssuerID())));
|
||||||
@@ -330,12 +330,17 @@ public class SModMenu extends PageableCustomInventory {
|
|||||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.timestamp", TimeUtil.calendarTimestamp(punishment.getTimestamp())))));
|
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.timestamp", TimeUtil.calendarTimestamp(punishment.getTimestamp())))));
|
||||||
|
|
||||||
if (punishment instanceof TimedPunishment timed){
|
if (punishment instanceof TimedPunishment timed){
|
||||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.duration", TimeUtil.formatTimeLong(timed.getExpiry() - punishment.getTimestamp())))));
|
if (timed.isPermanent()){
|
||||||
long remainingTime = timed.getExpiry() - System.currentTimeMillis();
|
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.duration", translatable("smod.time.permanent")))));
|
||||||
if (remainingTime > 0){
|
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.never"))));
|
||||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.future", TimeUtil.formatTimeLong(remainingTime)))));
|
|
||||||
} else {
|
} else {
|
||||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.past", TimeUtil.formatTimeLong(-remainingTime)))));
|
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.duration", TimeUtil.formatTimeLong(timed.getExpiry() - punishment.getTimestamp())))));
|
||||||
|
long remainingTime = timed.getExpiry() - System.currentTimeMillis();
|
||||||
|
if (remainingTime > 0){
|
||||||
|
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.future", TimeUtil.formatTimeLong(remainingTime)))));
|
||||||
|
} else {
|
||||||
|
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.past", TimeUtil.formatTimeLong(-remainingTime)))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package de.shiewk.smoderation.paper.listener;
|
||||||
|
|
||||||
|
import de.shiewk.smoderation.paper.punishments.PunishmentManager;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
import static de.shiewk.smoderation.paper.SModerationPaper.LOGGER;
|
||||||
|
import static net.kyori.adventure.text.Component.translatable;
|
||||||
|
|
||||||
|
public class CacheListener implements Listener {
|
||||||
|
|
||||||
|
private final PunishmentManager punishmentManager;
|
||||||
|
|
||||||
|
public CacheListener(PunishmentManager punishmentManager) {
|
||||||
|
this.punishmentManager = punishmentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event){
|
||||||
|
try {
|
||||||
|
punishmentManager.loadToCache(event.getPlayer().getUniqueId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Failed to load punishments", e);
|
||||||
|
event.getPlayer().kick(translatable("mco.errorMessage.connectionFailure"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent event){
|
||||||
|
punishmentManager.removeFromCache(event.getPlayer().getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,8 +17,6 @@ import static net.kyori.adventure.text.Component.translatable;
|
|||||||
|
|
||||||
public abstract class Punishment {
|
public abstract class Punishment {
|
||||||
|
|
||||||
public static final String DEFAULT_REASON = "No reason provided.";
|
|
||||||
|
|
||||||
protected final UUID id;
|
protected final UUID id;
|
||||||
protected final String type;
|
protected final String type;
|
||||||
protected final long timestamp;
|
protected final long timestamp;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ public final class PunishmentManager {
|
|||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(PunishmentManager.class);
|
private static final Logger log = LoggerFactory.getLogger(PunishmentManager.class);
|
||||||
private final Object2ObjectArrayMap<String, PunishmentFactory<?>> typeRegistry = new Object2ObjectArrayMap<>(1);
|
private final Object2ObjectArrayMap<String, PunishmentFactory<?>> typeRegistry = new Object2ObjectArrayMap<>(1);
|
||||||
|
private final ConcurrentHashMap<UUID, List<Punishment>> cache = new ConcurrentHashMap<>(1);
|
||||||
private final Object ioLock = new Object();
|
private final Object ioLock = new Object();
|
||||||
private final Path dataDir;
|
private final Path dataDir;
|
||||||
|
|
||||||
@@ -58,8 +60,13 @@ public final class PunishmentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Punishment> byTargetUUID(UUID target) {
|
public List<Punishment> byTargetUUID(UUID target) {
|
||||||
|
List<Punishment> cached = cache.get(target);
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
synchronized (ioLock) {
|
synchronized (ioLock) {
|
||||||
Path file = getTargetFile(target);
|
Path file = getTargetFile(target);
|
||||||
|
|
||||||
if (!Files.exists(file)) {
|
if (!Files.exists(file)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
@@ -115,7 +122,7 @@ public final class PunishmentManager {
|
|||||||
return List.copyOf(typeRegistry.keySet());
|
return List.copyOf(typeRegistry.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendToSave(Punishment punishment) throws IOException {
|
public void appendToSave(Punishment punishment) throws IOException {
|
||||||
synchronized (ioLock) {
|
synchronized (ioLock) {
|
||||||
Path file = getTargetFile(punishment.getTargetID());
|
Path file = getTargetFile(punishment.getTargetID());
|
||||||
if (!Files.exists(file)) {
|
if (!Files.exists(file)) {
|
||||||
@@ -129,6 +136,7 @@ public final class PunishmentManager {
|
|||||||
writer.append('\n');
|
writer.append('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
addToCachedList(punishment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull List<Punishment> getAll() throws IOException {
|
public @NotNull List<Punishment> getAll() throws IOException {
|
||||||
@@ -165,4 +173,21 @@ public final class PunishmentManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loadToCache(UUID uuid) {
|
||||||
|
removeFromCache(uuid);
|
||||||
|
cache.put(uuid, byTargetUUID(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeFromCache(UUID uuid) {
|
||||||
|
cache.remove(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToCachedList(Punishment punishment) {
|
||||||
|
cache.computeIfPresent(punishment.getTargetID(), (k, v) -> {
|
||||||
|
ObjectArrayList<Punishment> newList = new ObjectArrayList<>(v);
|
||||||
|
newList.add(punishment);
|
||||||
|
return List.copyOf(newList);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package de.shiewk.smoderation.paper.punishments;
|
package de.shiewk.smoderation.paper.punishments;
|
||||||
|
|
||||||
|
import de.shiewk.smoderation.paper.command.argument.DurationArgument;
|
||||||
import de.shiewk.smoderation.paper.util.PlayerUtil;
|
import de.shiewk.smoderation.paper.util.PlayerUtil;
|
||||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||||
import de.shiewk.smoderation.paper.util.TimeUtil;
|
import de.shiewk.smoderation.paper.util.TimeUtil;
|
||||||
@@ -35,7 +36,7 @@ public abstract class TimedPunishment extends Punishment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive(){
|
public boolean isActive(){
|
||||||
return !wasCancelled() && System.currentTimeMillis() < timestamp + duration;
|
return !wasCancelled() && (isPermanent() || System.currentTimeMillis() < getExpiry());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -55,23 +56,40 @@ public abstract class TimedPunishment extends Punishment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component infoMessage(){
|
public Component infoMessage(){
|
||||||
return translatable(
|
if (isPermanent()){
|
||||||
"smod.punishment.playerMessage." + type,
|
return translatable(
|
||||||
text(PlayerUtil.offlinePlayerName(this.issuer)),
|
"smod.punishment.playerMessage." + type + ".permanent",
|
||||||
text(reason),
|
text(PlayerUtil.offlinePlayerName(this.issuer)),
|
||||||
TimeUtil.formatTimeLong(this.timestamp + this.duration - System.currentTimeMillis())
|
text(reason)
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return translatable(
|
||||||
|
"smod.punishment.playerMessage." + type,
|
||||||
|
text(PlayerUtil.offlinePlayerName(this.issuer)),
|
||||||
|
text(reason),
|
||||||
|
TimeUtil.formatTimeLong(this.timestamp + this.duration - System.currentTimeMillis())
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component adminMessage(){
|
public Component adminMessage(){
|
||||||
return translatable(
|
if (isPermanent()){
|
||||||
"smod.punishment.broadcast." + type,
|
return translatable(
|
||||||
text(PlayerUtil.offlinePlayerName(target)),
|
"smod.punishment.broadcast." + type + ".permanent",
|
||||||
text(PlayerUtil.offlinePlayerName(issuer)),
|
text(PlayerUtil.offlinePlayerName(target)),
|
||||||
text(reason),
|
text(PlayerUtil.offlinePlayerName(issuer)),
|
||||||
TimeUtil.formatTimeLong(this.duration)
|
text(reason)
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return translatable(
|
||||||
|
"smod.punishment.broadcast." + type,
|
||||||
|
text(PlayerUtil.offlinePlayerName(target)),
|
||||||
|
text(PlayerUtil.offlinePlayerName(issuer)),
|
||||||
|
text(reason),
|
||||||
|
TimeUtil.formatTimeLong(this.duration)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Component cancelMessage(){
|
public Component cancelMessage(){
|
||||||
@@ -83,7 +101,11 @@ public abstract class TimedPunishment extends Punishment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getExpiry() {
|
public long getExpiry() {
|
||||||
return getTimestamp() + getDuration();
|
return isPermanent() ? Long.MAX_VALUE : getTimestamp() + getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPermanent() {
|
||||||
|
return getDuration() == DurationArgument.INFINITE_DURATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void cancel(UUID canceller) {
|
protected void cancel(UUID canceller) {
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
package de.shiewk.smoderation.paper.util;
|
package de.shiewk.smoderation.paper.util;
|
||||||
|
|
||||||
|
import de.shiewk.smoderation.paper.punishments.*;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import static de.shiewk.smoderation.paper.SModerationPaper.LOGGER;
|
||||||
|
|
||||||
public final class SModLegacy {
|
public final class SModLegacy {
|
||||||
private SModLegacy() {}
|
private SModLegacy() {}
|
||||||
@@ -54,4 +65,76 @@ public final class SModLegacy {
|
|||||||
return buffer.array();
|
return buffer.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 static void migrateV1PunishmentsFile(PunishmentManager manager, Path path, Path copy) {
|
||||||
|
int count = 0;
|
||||||
|
try {
|
||||||
|
if (Files.isRegularFile(path)) {
|
||||||
|
LOGGER.info("Migrating V1 punishment file: {}", path);
|
||||||
|
try (InputStream in = new FileInputStream(path.toFile());
|
||||||
|
GZIPInputStream gzin = new GZIPInputStream(in)){
|
||||||
|
while (gzin.available() > 0){
|
||||||
|
int type = bytesToInt(readStreamInternal(gzin, 4));
|
||||||
|
long time = bytesToLong(readStreamInternal(gzin, 8));
|
||||||
|
long until = bytesToLong(readStreamInternal(gzin, 8));
|
||||||
|
UUID by = bytesToUuid(readStreamInternal(gzin, 16));
|
||||||
|
UUID to = bytesToUuid(readStreamInternal(gzin, 16));
|
||||||
|
int reasonLen = bytesToInt(readStreamInternal(gzin, 4));
|
||||||
|
String reason = new String(readStreamInternal(gzin, reasonLen));
|
||||||
|
UUID canceller = null;
|
||||||
|
boolean cancelled = gzin.read() == 1;
|
||||||
|
if (cancelled){
|
||||||
|
canceller = bytesToUuid(readStreamInternal(gzin, 16));
|
||||||
|
}
|
||||||
|
// Type 0: mute; 1: kick; 2: ban
|
||||||
|
Punishment p = switch (type){
|
||||||
|
case 0 -> new Mute(
|
||||||
|
Punishment.generateUUID(),
|
||||||
|
time,
|
||||||
|
by,
|
||||||
|
to,
|
||||||
|
reason,
|
||||||
|
until - time,
|
||||||
|
canceller
|
||||||
|
);
|
||||||
|
case 1 -> new Kick(
|
||||||
|
Punishment.generateUUID(),
|
||||||
|
time,
|
||||||
|
by,
|
||||||
|
to,
|
||||||
|
reason
|
||||||
|
);
|
||||||
|
case 2 -> new Ban(
|
||||||
|
Punishment.generateUUID(),
|
||||||
|
time,
|
||||||
|
by,
|
||||||
|
to,
|
||||||
|
reason,
|
||||||
|
until - time,
|
||||||
|
canceller
|
||||||
|
);
|
||||||
|
default -> throw new IllegalArgumentException("Invalid legacy type for punishment: " + type);
|
||||||
|
};
|
||||||
|
count++;
|
||||||
|
manager.appendToSave(p);
|
||||||
|
LOGGER.info("Migrated: {}", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGGER.info("Successfully loaded {} items.", count);
|
||||||
|
Files.move(path, copy);
|
||||||
|
}
|
||||||
|
} catch (EOFException e) {
|
||||||
|
LOGGER.error("The file was not correctly saved, {} items could be recovered!", count);
|
||||||
|
} catch (IOException e){
|
||||||
|
LOGGER.error("An error occurred while loading", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
# If enabled, punishments can no longer be issued without providing a reason.
|
# If enabled, punishments can no longer be issued without providing a reason.
|
||||||
force-reason: false
|
force-reason: false
|
||||||
|
|
||||||
|
# The string that should be used when no reason is provided.
|
||||||
|
# This has no effect if 'force-reason' is set to true.
|
||||||
|
default-reason: No reason provided.
|
||||||
|
|
||||||
# Allows you to toggle specific plugin features.
|
# Allows you to toggle specific plugin features.
|
||||||
features:
|
features:
|
||||||
# Should punishments be tracked and executed?
|
# Should punishments be tracked and executed?
|
||||||
|
|||||||
@@ -19,8 +19,10 @@
|
|||||||
"smod.command.kick.fail.protect": "Dieser Spieler kann nicht gekickt werden.",
|
"smod.command.kick.fail.protect": "Dieser Spieler kann nicht gekickt werden.",
|
||||||
"smod.command.kick.fail.self": "Du kannst dich nicht selbst kicken.",
|
"smod.command.kick.fail.self": "Du kannst dich nicht selbst kicken.",
|
||||||
"smod.command.modlogs.ban": "<primary>- ist bis <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray> gebannt. Grund: <secondary><arg:2>",
|
"smod.command.modlogs.ban": "<primary>- ist bis <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray> gebannt. Grund: <secondary><arg:2>",
|
||||||
|
"smod.command.modlogs.ban.permanent": "<primary>- ist <secondary>permanent</secondary> gebannt. Grund: <secondary><arg:0>",
|
||||||
"smod.command.modlogs.heading": "<primary>Spieler <secondary><arg:0> <gray>(<arg:1>)",
|
"smod.command.modlogs.heading": "<primary>Spieler <secondary><arg:0> <gray>(<arg:1>)",
|
||||||
"smod.command.modlogs.mute": "<primary>- ist bis <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray> stummgeschaltet. Grund: <secondary><arg:2>",
|
"smod.command.modlogs.mute": "<primary>- ist bis <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray> stummgeschaltet. Grund: <secondary><arg:2>",
|
||||||
|
"smod.command.modlogs.mute.permanent": "<primary>- ist <secondary>permanent</secondary> stummgeschaltet. Grund: <secondary><arg:0>",
|
||||||
"smod.command.modlogs.none": "<primary>- ist momentan nicht gebannt oder stummgeschaltet.",
|
"smod.command.modlogs.none": "<primary>- ist momentan nicht gebannt oder stummgeschaltet.",
|
||||||
"smod.command.mute.fail.alreadyMuted": "Dieser Spieler ist schon stummgeschaltet.",
|
"smod.command.mute.fail.alreadyMuted": "Dieser Spieler ist schon stummgeschaltet.",
|
||||||
"smod.command.mute.fail.forceReason": "Bitte gib einen Grund an.",
|
"smod.command.mute.fail.forceReason": "Bitte gib einen Grund an.",
|
||||||
@@ -56,6 +58,7 @@
|
|||||||
"smod.menu.info.click": "\u00BB Klicke, um die Strafe aufzuheben",
|
"smod.menu.info.click": "\u00BB Klicke, um die Strafe aufzuheben",
|
||||||
"smod.menu.info.duration": "<secondary>Dauer: <primary><arg:0>",
|
"smod.menu.info.duration": "<secondary>Dauer: <primary><arg:0>",
|
||||||
"smod.menu.info.expiry.future": "<secondary>Läuft ab: <primary>In <arg:0>",
|
"smod.menu.info.expiry.future": "<secondary>Läuft ab: <primary>In <arg:0>",
|
||||||
|
"smod.menu.info.expiry.never": "<secondary>Läuft ab: <primary>Nie",
|
||||||
"smod.menu.info.expiry.past": "<secondary>Ist abgelaufen: <primary><arg:0> ago",
|
"smod.menu.info.expiry.past": "<secondary>Ist abgelaufen: <primary><arg:0> ago",
|
||||||
"smod.menu.info.player": "<secondary>Spieler: <primary><arg:0>",
|
"smod.menu.info.player": "<secondary>Spieler: <primary><arg:0>",
|
||||||
"smod.menu.info.punishedBy": "<secondary>Bestraft von: <primary><arg:0>",
|
"smod.menu.info.punishedBy": "<secondary>Bestraft von: <primary><arg:0>",
|
||||||
@@ -77,16 +80,20 @@
|
|||||||
"smod.menu.type.all": "Alle",
|
"smod.menu.type.all": "Alle",
|
||||||
"smod.menu.type.switch": "\u00BB Klicke, um den Typ zu ändern",
|
"smod.menu.type.switch": "\u00BB Klicke, um den Typ zu ändern",
|
||||||
"smod.punishment.broadcast.ban": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> für <secondary><arg:3></secondary> gebannt.<newline>Grund: <secondary><arg:2>",
|
"smod.punishment.broadcast.ban": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> für <secondary><arg:3></secondary> gebannt.<newline>Grund: <secondary><arg:2>",
|
||||||
|
"smod.punishment.broadcast.ban.permanent": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> <secondary>permanent</secondary> gebannt.<newline>Grund: <secondary><arg:2>",
|
||||||
"smod.punishment.broadcast.kick": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> gekickt.<newline>Grund: <secondary><arg:2>",
|
"smod.punishment.broadcast.kick": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> gekickt.<newline>Grund: <secondary><arg:2>",
|
||||||
"smod.punishment.broadcast.mute": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> für <secondary><arg:3></secondary> stummgeschaltet.<newline>Grund: <secondary><arg:2>",
|
"smod.punishment.broadcast.mute": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> für <secondary><arg:3></secondary> stummgeschaltet.<newline>Grund: <secondary><arg:2>",
|
||||||
|
"smod.punishment.broadcast.mute.permanent": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> <secondary>permanent</secondary> stummgeschaltet.<newline>Grund: <secondary><arg:2>",
|
||||||
"smod.punishment.cancel.ban": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> entbannt.",
|
"smod.punishment.cancel.ban": "<primary><secondary><arg:0></secondary> wurde von <secondary><arg:1></secondary> entbannt.",
|
||||||
"smod.punishment.cancel.mute": "<primary><secondary><arg:0></secondary>s Stummschaltung wurde von <secondary><arg:1></secondary> aufgehoben.",
|
"smod.punishment.cancel.mute": "<primary><secondary><arg:0></secondary>s Stummschaltung wurde von <secondary><arg:1></secondary> aufgehoben.",
|
||||||
"smod.punishment.name.ban": "Bann",
|
"smod.punishment.name.ban": "Bann",
|
||||||
"smod.punishment.name.kick": "Kick",
|
"smod.punishment.name.kick": "Kick",
|
||||||
"smod.punishment.name.mute": "Stummschaltung",
|
"smod.punishment.name.mute": "Stummschaltung",
|
||||||
"smod.punishment.playerMessage.ban": "<primary>Du wurdest von <secondary><arg:0></secondary> vom Server gebannt.<newline>Grund: <secondary><arg:1></secondary><newline>Dein Bann läuft in <secondary><arg:2></secondary> ab.",
|
"smod.punishment.playerMessage.ban": "<primary>Du wurdest von <secondary><arg:0></secondary> vom Server gebannt.<newline>Grund: <secondary><arg:1></secondary><newline>Dein Bann läuft in <secondary><arg:2></secondary> ab.",
|
||||||
|
"smod.punishment.playerMessage.ban.permanent": "<primary>Du wurdest von <secondary><arg:0></secondary> vom Server gebannt.<newline>Grund: <secondary><arg:1></secondary><newline>Dein Bann läuft <secondary>nicht</secondary> ab.",
|
||||||
"smod.punishment.playerMessage.kick": "<primary>Du wurdest von <secondary><arg:0></secondary> vom Server gekickt.<newline>Grund: <secondary><arg:1>",
|
"smod.punishment.playerMessage.kick": "<primary>Du wurdest von <secondary><arg:0></secondary> vom Server gekickt.<newline>Grund: <secondary><arg:1>",
|
||||||
"smod.punishment.playerMessage.mute": "<primary>Du wurdest von <secondary><arg:0></secondary> stummgeschaltet.<newline>Grund: <secondary><arg:1></secondary><newline>Du kannst in <secondary><arg:2></secondary> wieder schreiben.",
|
"smod.punishment.playerMessage.mute": "<primary>Du wurdest von <secondary><arg:0></secondary> stummgeschaltet.<newline>Grund: <secondary><arg:1></secondary><newline>Du kannst in <secondary><arg:2></secondary> wieder schreiben.",
|
||||||
|
"smod.punishment.playerMessage.mute.permanent": "<primary>Du wurdest von <secondary><arg:0></secondary> stummgeschaltet.<newline>Grund: <secondary><arg:1></secondary><newline>Du kannst <secondary>nie</secondary> wieder schreiben.",
|
||||||
"smod.punishment.playerMessage.mute.chat": "<primary>Du kannst diesen Befehl nicht ausführen, während du stummgeschaltet bist.",
|
"smod.punishment.playerMessage.mute.chat": "<primary>Du kannst diesen Befehl nicht ausführen, während du stummgeschaltet bist.",
|
||||||
"smod.socialspy.command": "<primary>[<secondary>SocialSpy</secondary>] <arg:0>: <secondary><arg:1>",
|
"smod.socialspy.command": "<primary>[<secondary>SocialSpy</secondary>] <arg:0>: <secondary><arg:1>",
|
||||||
"smod.time.days": "<arg:0> Tage",
|
"smod.time.days": "<arg:0> Tage",
|
||||||
@@ -106,6 +113,8 @@
|
|||||||
"smod.time.month.8": "September",
|
"smod.time.month.8": "September",
|
||||||
"smod.time.month.9": "Oktober",
|
"smod.time.month.9": "Oktober",
|
||||||
"smod.time.months": "<arg:0> Monate",
|
"smod.time.months": "<arg:0> Monate",
|
||||||
|
"smod.time.never": "Nie",
|
||||||
|
"smod.time.permanent": "Permanent",
|
||||||
"smod.time.seconds": "<arg:0> Sekunden",
|
"smod.time.seconds": "<arg:0> Sekunden",
|
||||||
"smod.time.timestamp": "<arg:2>. <arg:1> <arg:0> <arg:3>:<arg:4>:<arg:5> <arg:6>",
|
"smod.time.timestamp": "<arg:2>. <arg:1> <arg:0> <arg:3>:<arg:4>:<arg:5> <arg:6>",
|
||||||
"smod.time.weeks": "<arg:0> Wochen",
|
"smod.time.weeks": "<arg:0> Wochen",
|
||||||
|
|||||||
@@ -19,8 +19,10 @@
|
|||||||
"smod.command.kick.fail.protect": "This player can't be kicked.",
|
"smod.command.kick.fail.protect": "This player can't be kicked.",
|
||||||
"smod.command.kick.fail.self": "You can't kick yourself.",
|
"smod.command.kick.fail.self": "You can't kick yourself.",
|
||||||
"smod.command.modlogs.ban": "<primary>- is banned until <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray>. Reason: <secondary><arg:2>",
|
"smod.command.modlogs.ban": "<primary>- is banned until <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray>. Reason: <secondary><arg:2>",
|
||||||
|
"smod.command.modlogs.ban.permanent": "<primary>- is banned <secondary>permanently</secondary>. Reason: <secondary><arg:0>",
|
||||||
"smod.command.modlogs.heading": "<primary>Player <secondary><arg:0> <gray>(<arg:1>)",
|
"smod.command.modlogs.heading": "<primary>Player <secondary><arg:0> <gray>(<arg:1>)",
|
||||||
"smod.command.modlogs.mute": "<primary>- is muted until <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray>. Reason: <secondary><arg:2>",
|
"smod.command.modlogs.mute": "<primary>- is muted until <secondary><arg:0></secondary> <gray>(in <arg:1>)</gray>. Reason: <secondary><arg:2>",
|
||||||
|
"smod.command.modlogs.mute.permanent": "<primary>- is muted <secondary>permanently</secondary>. Reason: <secondary><arg:0>",
|
||||||
"smod.command.modlogs.none": "<primary>- is not currently muted or banned.",
|
"smod.command.modlogs.none": "<primary>- is not currently muted or banned.",
|
||||||
"smod.command.mute.fail.alreadyMuted": "This player is already muted.",
|
"smod.command.mute.fail.alreadyMuted": "This player is already muted.",
|
||||||
"smod.command.mute.fail.forceReason": "Please provide a reason.",
|
"smod.command.mute.fail.forceReason": "Please provide a reason.",
|
||||||
@@ -56,6 +58,7 @@
|
|||||||
"smod.menu.info.click": "\u00BB Click to cancel punishment",
|
"smod.menu.info.click": "\u00BB Click to cancel punishment",
|
||||||
"smod.menu.info.duration": "<secondary>Duration: <primary><arg:0>",
|
"smod.menu.info.duration": "<secondary>Duration: <primary><arg:0>",
|
||||||
"smod.menu.info.expiry.future": "<secondary>Expires: <primary>In <arg:0>",
|
"smod.menu.info.expiry.future": "<secondary>Expires: <primary>In <arg:0>",
|
||||||
|
"smod.menu.info.expiry.never": "<secondary>Expires: <primary>Never",
|
||||||
"smod.menu.info.expiry.past": "<secondary>Expired: <primary><arg:0> ago",
|
"smod.menu.info.expiry.past": "<secondary>Expired: <primary><arg:0> ago",
|
||||||
"smod.menu.info.player": "<secondary>Player: <primary><arg:0>",
|
"smod.menu.info.player": "<secondary>Player: <primary><arg:0>",
|
||||||
"smod.menu.info.punishedBy": "<secondary>Punished by: <primary><arg:0>",
|
"smod.menu.info.punishedBy": "<secondary>Punished by: <primary><arg:0>",
|
||||||
@@ -77,16 +80,20 @@
|
|||||||
"smod.menu.type.all": "All",
|
"smod.menu.type.all": "All",
|
||||||
"smod.menu.type.switch": "» Click to switch type",
|
"smod.menu.type.switch": "» Click to switch type",
|
||||||
"smod.punishment.broadcast.ban": "<primary><secondary><arg:0></secondary> was banned by <secondary><arg:1></secondary> for <secondary><arg:3></secondary>.<newline>Reason: <secondary><arg:2>",
|
"smod.punishment.broadcast.ban": "<primary><secondary><arg:0></secondary> was banned by <secondary><arg:1></secondary> for <secondary><arg:3></secondary>.<newline>Reason: <secondary><arg:2>",
|
||||||
|
"smod.punishment.broadcast.ban.permanent": "<primary><secondary><arg:0></secondary> was banned <secondary>permanently</secondary> by <secondary><arg:1></secondary>.<newline>Reason: <secondary><arg:2>",
|
||||||
"smod.punishment.broadcast.kick": "<primary><secondary><arg:0></secondary> was kicked by <secondary><arg:1></secondary>.<newline>Reason: <secondary><arg:2>",
|
"smod.punishment.broadcast.kick": "<primary><secondary><arg:0></secondary> was kicked by <secondary><arg:1></secondary>.<newline>Reason: <secondary><arg:2>",
|
||||||
"smod.punishment.broadcast.mute": "<primary><secondary><arg:0></secondary> was muted by <secondary><arg:1></secondary> for <secondary><arg:3></secondary>.<newline>Reason: <secondary><arg:2>",
|
"smod.punishment.broadcast.mute": "<primary><secondary><arg:0></secondary> was muted by <secondary><arg:1></secondary> for <secondary><arg:3></secondary>.<newline>Reason: <secondary><arg:2>",
|
||||||
|
"smod.punishment.broadcast.mute.permanent": "<primary><secondary><arg:0></secondary> was muted <secondary>permanently</secondary> by <secondary><arg:1></secondary>.<newline>Reason: <secondary><arg:2>",
|
||||||
"smod.punishment.cancel.ban": "<primary><secondary><arg:0></secondary> was unbanned by <secondary><arg:1></secondary>.",
|
"smod.punishment.cancel.ban": "<primary><secondary><arg:0></secondary> was unbanned by <secondary><arg:1></secondary>.",
|
||||||
"smod.punishment.cancel.mute": "<primary><secondary><arg:0></secondary> was unmuted by <secondary><arg:1></secondary>.",
|
"smod.punishment.cancel.mute": "<primary><secondary><arg:0></secondary> was unmuted by <secondary><arg:1></secondary>.",
|
||||||
"smod.punishment.name.ban": "Ban",
|
"smod.punishment.name.ban": "Ban",
|
||||||
"smod.punishment.name.kick": "Kick",
|
"smod.punishment.name.kick": "Kick",
|
||||||
"smod.punishment.name.mute": "Mute",
|
"smod.punishment.name.mute": "Mute",
|
||||||
"smod.punishment.playerMessage.ban": "<primary>You have been banned from this server by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1></secondary><newline>Your ban expires in <secondary><arg:2></secondary>.",
|
"smod.punishment.playerMessage.ban": "<primary>You have been banned from this server by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1></secondary><newline>Your ban expires in <secondary><arg:2></secondary>.",
|
||||||
|
"smod.punishment.playerMessage.ban.permanent": "<primary>You have been banned from this server by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1></secondary><newline>Your ban <secondary>does not</secondary> expire.",
|
||||||
"smod.punishment.playerMessage.kick": "<primary>You have been kicked by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1>",
|
"smod.punishment.playerMessage.kick": "<primary>You have been kicked by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1>",
|
||||||
"smod.punishment.playerMessage.mute": "<primary>You have been muted by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1></secondary><newline>Your mute expires in <secondary><arg:2></secondary>.",
|
"smod.punishment.playerMessage.mute": "<primary>You have been muted by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1></secondary><newline>Your mute expires in <secondary><arg:2></secondary>.",
|
||||||
|
"smod.punishment.playerMessage.mute.permanent": "<primary>You have been muted by <secondary><arg:0></secondary>.<newline>Reason: <secondary><arg:1></secondary><newline>Your mute <secondary>does not</secondary> expire.",
|
||||||
"smod.punishment.playerMessage.mute.chat": "<primary>You can't run this command while you are muted.",
|
"smod.punishment.playerMessage.mute.chat": "<primary>You can't run this command while you are muted.",
|
||||||
"smod.socialspy.command": "<primary>[<secondary>SocialSpy</secondary>] <arg:0>: <secondary><arg:1>",
|
"smod.socialspy.command": "<primary>[<secondary>SocialSpy</secondary>] <arg:0>: <secondary><arg:1>",
|
||||||
"smod.time.days": "<arg:0> days",
|
"smod.time.days": "<arg:0> days",
|
||||||
@@ -106,6 +113,8 @@
|
|||||||
"smod.time.month.8": "September",
|
"smod.time.month.8": "September",
|
||||||
"smod.time.month.9": "October",
|
"smod.time.month.9": "October",
|
||||||
"smod.time.months": "<arg:0> months",
|
"smod.time.months": "<arg:0> months",
|
||||||
|
"smod.time.never": "Never",
|
||||||
|
"smod.time.permanent": "Permanent",
|
||||||
"smod.time.seconds": "<arg:0> seconds",
|
"smod.time.seconds": "<arg:0> seconds",
|
||||||
"smod.time.timestamp": "<arg:2> <arg:1> <arg:0> <arg:3>:<arg:4>:<arg:5> <arg:6>",
|
"smod.time.timestamp": "<arg:2> <arg:1> <arg:0> <arg:3>:<arg:4>:<arg:5> <arg:6>",
|
||||||
"smod.time.weeks": "<arg:0> weeks",
|
"smod.time.weeks": "<arg:0> weeks",
|
||||||
|
|||||||
Reference in New Issue
Block a user