mirror of
https://github.com/Shiewk/SModeration.git
synced 2026-04-29 06:34:17 +02:00
Compare commits
6 Commits
823093be35
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
f3ea35fd93
|
|||
|
a947a303e1
|
|||
|
2bccf45439
|
|||
|
24da79642a
|
|||
|
ea54f83909
|
|||
|
6c76066d8b
|
@@ -10,11 +10,12 @@ import de.shiewk.smoderation.paper.punishments.Kick;
|
||||
import de.shiewk.smoderation.paper.punishments.Mute;
|
||||
import de.shiewk.smoderation.paper.punishments.PunishmentManager;
|
||||
import de.shiewk.smoderation.paper.translation.TranslatorManager;
|
||||
import de.shiewk.smoderation.paper.util.ColorPalette;
|
||||
import de.shiewk.smoderation.paper.util.SModLegacy;
|
||||
import de.shiewk.smoderation.paper.util.SchedulerUtil;
|
||||
import io.papermc.paper.command.brigadier.Commands;
|
||||
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
@@ -41,14 +42,11 @@ import static org.bukkit.Bukkit.getPluginManager;
|
||||
@SuppressWarnings("UnstableApiUsage") // Paper Brigadier API
|
||||
public final class SModerationPaper extends JavaPlugin {
|
||||
|
||||
public static final TextColor PRIMARY_COLOR = TextColor.color(212, 0, 255);
|
||||
public static final TextColor SECONDARY_COLOR = TextColor.color(52, 143, 255);
|
||||
public static final TextColor INACTIVE_COLOR = NamedTextColor.GRAY;
|
||||
|
||||
public static final Gson gson = new Gson();
|
||||
public static ComponentLogger LOGGER = null;
|
||||
public static SModerationPaper PLUGIN = null;
|
||||
private static SkinTextureProvider textureProvider = null;
|
||||
private ColorPalette colors;
|
||||
|
||||
private final TranslatorManager translatorManager = new TranslatorManager(
|
||||
Key.key("smoderation", "translations"),
|
||||
@@ -66,19 +64,50 @@ public final class SModerationPaper extends JavaPlugin {
|
||||
return PLUGIN.getConfig();
|
||||
}
|
||||
|
||||
public static ColorPalette colors() {
|
||||
return PLUGIN.colors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
LOGGER = getComponentLogger();
|
||||
LOGGER.info("Folia: {}", SchedulerUtil.isFolia ? "yes" : "no");
|
||||
PLUGIN = this;
|
||||
LOGGER.info("Loading translations");
|
||||
if (config().getBoolean("custom-messages", false)) {
|
||||
translatorManager.loadCustomMessages(getDataPath().resolve("messages.json"));
|
||||
} else {
|
||||
translatorManager.load();
|
||||
}
|
||||
updateConfig();
|
||||
|
||||
this.punishmentManager = new PunishmentManager(getDataPath().resolve("punishments.v2"));
|
||||
this.punishmentManager.registerType("mute", new Mute.Factory());
|
||||
this.punishmentManager.registerType("ban", new Ban.Factory());
|
||||
this.punishmentManager.registerType("kick", new Kick.Factory());
|
||||
|
||||
this.colors = new ColorPalette(
|
||||
parseColor(config().getString("colors.primary")),
|
||||
parseColor(config().getString("colors.secondary")),
|
||||
parseColor(config().getString("colors.detail"))
|
||||
);
|
||||
|
||||
SModLegacy.migrateV1PunishmentsFile(
|
||||
this.punishmentManager,
|
||||
getDataPath().resolve("container.gz"),
|
||||
getDataPath().resolve("v1-backup.gz")
|
||||
);
|
||||
}
|
||||
|
||||
private TextColor parseColor(String string) {
|
||||
if (string == null || string.length() != 7) {
|
||||
throw new IllegalArgumentException("Color not formatted correctly: " + string);
|
||||
}
|
||||
TextColor color = TextColor.fromHexString(string);
|
||||
if (color == null) {
|
||||
throw new IllegalArgumentException("Color not formatted correctly: " + string);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
public boolean isFeatureEnabled(String feature){
|
||||
@@ -88,6 +117,7 @@ public final class SModerationPaper extends JavaPlugin {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (isFeatureEnabled("punishments")) listen(new PunishmentListener(punishmentManager));
|
||||
if (isFeatureEnabled("punishments")) listen(new CacheListener(punishmentManager));
|
||||
if (isFeatureEnabled("invsee")) listen(new InvSeeListener());
|
||||
if (isFeatureEnabled("enderchestsee")) listen(new EnderchestSeeListener());
|
||||
if (isFeatureEnabled("socialspy")) listen(new SocialSpyListener());
|
||||
@@ -162,8 +192,9 @@ public final class SModerationPaper extends JavaPlugin {
|
||||
private MiniMessage createMiniMessage() {
|
||||
return MiniMessage.builder()
|
||||
.tags(TagResolver.builder()
|
||||
.resolver(TagResolver.resolver("primary", Tag.styling(style -> style.color(PRIMARY_COLOR))))
|
||||
.resolver(TagResolver.resolver("secondary", Tag.styling(style -> style.color(SECONDARY_COLOR))))
|
||||
.resolver(TagResolver.resolver("primary", Tag.styling(style -> style.color(colors().primary()))))
|
||||
.resolver(TagResolver.resolver("secondary", Tag.styling(style -> style.color(colors().secondary()))))
|
||||
.resolver(TagResolver.resolver("detail", Tag.styling(style -> style.color(colors().detail()))))
|
||||
.resolver(TagResolver.standard())
|
||||
.build()
|
||||
)
|
||||
|
||||
@@ -54,7 +54,7 @@ public final class BanCommand implements CommandProvider {
|
||||
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
||||
UUID target = context.getArgument("player", UUID.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;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ public final class KickCommand implements CommandProvider {
|
||||
}
|
||||
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ public final class ModLogsCommand implements CommandProvider {
|
||||
List<Punishment> punishments = punishmentManager.byTargetUUID(uuid);
|
||||
for (Punishment punishment : punishments) {
|
||||
if (punishment instanceof TimedPunishment timed && timed.isActive()){
|
||||
if (timed.isPermanent()){
|
||||
sender.sendMessage(translatable("smod.command.modlogs." + punishment.getType() + ".permanent", text(punishment.getReason())));
|
||||
} else {
|
||||
sender.sendMessage(translatable("smod.command.modlogs." + punishment.getType(),
|
||||
TimeUtil.calendarTimestamp(timed.getExpiry()),
|
||||
TimeUtil.formatTimeLong(timed.getExpiry() - System.currentTimeMillis()),
|
||||
@@ -56,6 +59,7 @@ public final class ModLogsCommand implements CommandProvider {
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (punishments.isEmpty()){
|
||||
sender.sendMessage(translatable("smod.command.modlogs.none"));
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public final class MuteCommand implements CommandProvider {
|
||||
UUID sender = CommandUtil.getSenderUUID(context.getSource());
|
||||
UUID target = context.getArgument("player", UUID.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;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import de.shiewk.smoderation.paper.SModerationPaper;
|
||||
import de.shiewk.smoderation.paper.event.VanishToggleEvent;
|
||||
import de.shiewk.smoderation.paper.punishments.Punishment;
|
||||
import de.shiewk.smoderation.paper.util.CommandUtil;
|
||||
@@ -150,11 +151,11 @@ public final class VanishCommand implements CommandProvider {
|
||||
for (ObjectListIterator<Player> iterator = vanishedPlayers.iterator(); iterator.hasNext(); ) {
|
||||
Player vanishedPlayer = iterator.next();
|
||||
vanishList = vanishList.append(
|
||||
vanishedPlayer.teamDisplayName().colorIfAbsent(SECONDARY_COLOR)
|
||||
vanishedPlayer.teamDisplayName().colorIfAbsent(SModerationPaper.colors().secondary())
|
||||
);
|
||||
if (iterator.hasNext()){
|
||||
vanishList = vanishList.append(
|
||||
text().content(", ").color(PRIMARY_COLOR)
|
||||
text().content(", ").color(SModerationPaper.colors().primary())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 VALIDATION_PATTERN = Pattern.compile("(([0-9]{1,9})(ms|s|min|h|d|w|mo|y))+");
|
||||
public static final long INFINITE_DURATION = -1L;
|
||||
|
||||
@Override
|
||||
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()){
|
||||
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) {
|
||||
if (builder.getRemaining().isBlank()){
|
||||
List.of(
|
||||
"infinite", "inf",
|
||||
"permanent", "perm",
|
||||
"100ms",
|
||||
"15s",
|
||||
"2min",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.shiewk.smoderation.paper.input;
|
||||
|
||||
import de.shiewk.smoderation.paper.SModerationPaper;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.title.Title;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -10,7 +11,6 @@ import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static de.shiewk.smoderation.paper.SModerationPaper.PRIMARY_COLOR;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
@@ -53,7 +53,7 @@ public class ChatInput {
|
||||
|
||||
public static void prompt(Player player, Consumer<Component> consumer, Component prompt, int timeSeconds){
|
||||
runningInputs.put(player, new ChatInput(player, prompt, consumer, timeSeconds));
|
||||
player.sendMessage(prompt.colorIfAbsent(PRIMARY_COLOR));
|
||||
player.sendMessage(prompt.colorIfAbsent(SModerationPaper.colors().primary()));
|
||||
}
|
||||
|
||||
public Component getPrompt() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.shiewk.smoderation.paper.inventory;
|
||||
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.destroystokyo.paper.profile.ProfileProperty;
|
||||
import de.shiewk.smoderation.paper.SModerationPaper;
|
||||
import de.shiewk.smoderation.paper.SkinTextureProvider;
|
||||
import de.shiewk.smoderation.paper.input.ChatInput;
|
||||
import de.shiewk.smoderation.paper.punishments.Punishment;
|
||||
@@ -58,7 +59,7 @@ public class SModMenu extends PageableCustomInventory {
|
||||
}
|
||||
|
||||
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)),
|
||||
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())));
|
||||
@@ -126,7 +127,7 @@ public class SModMenu extends PageableCustomInventory {
|
||||
// chat event is async
|
||||
SchedulerUtil.scheduleForEntity(PLUGIN, player, this::open);
|
||||
}
|
||||
}, translatable("smod.menu.search.query").color(SECONDARY_COLOR), 30);
|
||||
}, translatable("smod.menu.search.query").color(SModerationPaper.colors().secondary()), 30);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,13 +200,13 @@ public class SModMenu extends PageableCustomInventory {
|
||||
private ItemStack createFilterItem(){
|
||||
final Filter filter = getFilter();
|
||||
final ItemStack stack = new ItemStack(Filter.ICON);
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.filter", filter.name).color(PRIMARY_COLOR)));
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.filter", filter.name).color(SModerationPaper.colors().primary())));
|
||||
ItemLore.Builder loreBuilder = ItemLore.lore();
|
||||
|
||||
loreBuilder.addLine(empty());
|
||||
for (Filter value : Filter.values()) {
|
||||
final boolean selected = filter == value;
|
||||
Component filterText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SECONDARY_COLOR : INACTIVE_COLOR).append(value.name)));
|
||||
Component filterText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SModerationPaper.colors().secondary() : SModerationPaper.colors().detail()).append(value.name)));
|
||||
loreBuilder.addLine(filterText);
|
||||
}
|
||||
loreBuilder.addLine(empty());
|
||||
@@ -218,13 +219,13 @@ public class SModMenu extends PageableCustomInventory {
|
||||
private ItemStack createTypeItem(){
|
||||
final String type = getType();
|
||||
final ItemStack stack = new ItemStack(Material.CHEST);
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.type", (type == null ? translatable("smod.menu.type.all") : translatable("smod.punishment.name." + type)))).color(PRIMARY_COLOR));
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.type", (type == null ? translatable("smod.menu.type.all") : translatable("smod.punishment.name." + type)))).color(SModerationPaper.colors().primary()));
|
||||
|
||||
ItemLore.Builder loreBuilder = ItemLore.lore();
|
||||
loreBuilder.addLine(empty());
|
||||
final Consumer<String> addToLore = value -> {
|
||||
final boolean selected = Objects.equals(type, value);
|
||||
Component typeText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SECONDARY_COLOR : INACTIVE_COLOR).append(value == null ? translatable("smod.menu.type.all") : translatable("smod.punishment.name." + value))));
|
||||
Component typeText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SModerationPaper.colors().secondary() : SModerationPaper.colors().detail()).append(value == null ? translatable("smod.menu.type.all") : translatable("smod.punishment.name." + value))));
|
||||
loreBuilder.addLine(typeText);
|
||||
};
|
||||
addToLore.accept(null);
|
||||
@@ -242,14 +243,14 @@ public class SModMenu extends PageableCustomInventory {
|
||||
private ItemStack createSortItem(){
|
||||
final Sort sort = getSort();
|
||||
final ItemStack stack = new ItemStack(Sort.ICON);
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.sort", sort.name).color(PRIMARY_COLOR)));
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.sort", sort.name).color(SModerationPaper.colors().primary())));
|
||||
|
||||
ItemLore.Builder loreBuilder = ItemLore.lore();
|
||||
loreBuilder.addLine(empty());
|
||||
|
||||
for (Sort value : Sort.values()) {
|
||||
final boolean selected = sort == value;
|
||||
Component sortText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SECONDARY_COLOR : INACTIVE_COLOR).append(value.name)));
|
||||
Component sortText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SModerationPaper.colors().secondary() : SModerationPaper.colors().detail()).append(value.name)));
|
||||
loreBuilder.addLine(sortText);
|
||||
}
|
||||
|
||||
@@ -270,12 +271,12 @@ public class SModMenu extends PageableCustomInventory {
|
||||
// we just create the stack without it instead of throwing
|
||||
}
|
||||
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.search", PRIMARY_COLOR)));
|
||||
stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.search", SModerationPaper.colors().primary())));
|
||||
|
||||
ItemLore.Builder loreBuilder = ItemLore.lore();
|
||||
loreBuilder.addLines(List.of(
|
||||
empty(),
|
||||
renderComponent(player, applyFormatting(translatable("smod.menu.search.current", searchQuery == null ? translatable("smod.menu.search.none") : text('"' + searchQuery + '"'))).color(SECONDARY_COLOR)),
|
||||
renderComponent(player, applyFormatting(translatable("smod.menu.search.current", searchQuery == null ? translatable("smod.menu.search.none") : text('"' + searchQuery + '"'))).color(SModerationPaper.colors().secondary())),
|
||||
empty(),
|
||||
renderComponent(player, applyFormatting(translatable("smod.menu.search.new", NamedTextColor.GOLD)))
|
||||
));
|
||||
@@ -330,6 +331,10 @@ public class SModMenu extends PageableCustomInventory {
|
||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.timestamp", TimeUtil.calendarTimestamp(punishment.getTimestamp())))));
|
||||
|
||||
if (punishment instanceof TimedPunishment timed){
|
||||
if (timed.isPermanent()){
|
||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.duration", translatable("smod.time.permanent")))));
|
||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.never"))));
|
||||
} else {
|
||||
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){
|
||||
@@ -338,6 +343,7 @@ public class SModMenu extends PageableCustomInventory {
|
||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.past", TimeUtil.formatTimeLong(-remainingTime)))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.reason", text(punishment.getReason())))));
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static de.shiewk.smoderation.paper.SModerationPaper.PRIMARY_COLOR;
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public class PunishmentListener implements Listener {
|
||||
@@ -41,7 +40,7 @@ public class PunishmentListener implements Listener {
|
||||
List<Punishment> list = punishmentManager.byTargetUUID(player.getUniqueId(), p -> p instanceof Mute mute && mute.isActive());
|
||||
if (!list.isEmpty()) {
|
||||
event.setCancelled(true);
|
||||
player.sendMessage(list.getFirst().infoMessage().colorIfAbsent(PRIMARY_COLOR));
|
||||
player.sendMessage(list.getFirst().infoMessage().colorIfAbsent(SModerationPaper.colors().primary()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +56,7 @@ public class PunishmentListener implements Listener {
|
||||
|| message.toLowerCase().startsWith(str.toLowerCase()+" ")
|
||||
)){
|
||||
Bukkit.getConsoleSender().sendMessage(player.getName() + " tried to run forbidden command while muted");
|
||||
player.sendMessage(translatable("smod.punishment.playerMessage.mute.chat", PRIMARY_COLOR));
|
||||
player.sendMessage(translatable("smod.punishment.playerMessage.mute.chat", SModerationPaper.colors().primary()));
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,12 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import static de.shiewk.smoderation.paper.SModerationPaper.SECONDARY_COLOR;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public class VanishListener implements Listener {
|
||||
|
||||
public static final Component PREFIX = text("[VANISH] ").color(SECONDARY_COLOR);
|
||||
public static final Component PREFIX = text("[VANISH] ").color(SModerationPaper.colors().secondary());
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH) public void onPlayerQuit(PlayerQuitEvent event){
|
||||
final Player player = event.getPlayer();
|
||||
|
||||
@@ -17,8 +17,6 @@ import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public abstract class Punishment {
|
||||
|
||||
public static final String DEFAULT_REASON = "No reason provided.";
|
||||
|
||||
protected final UUID id;
|
||||
protected final String type;
|
||||
protected final long timestamp;
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -31,6 +32,7 @@ public final class PunishmentManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PunishmentManager.class);
|
||||
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 Path dataDir;
|
||||
|
||||
@@ -58,8 +60,13 @@ public final class PunishmentManager {
|
||||
}
|
||||
|
||||
public List<Punishment> byTargetUUID(UUID target) {
|
||||
List<Punishment> cached = cache.get(target);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
synchronized (ioLock) {
|
||||
Path file = getTargetFile(target);
|
||||
|
||||
if (!Files.exists(file)) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -115,7 +122,7 @@ public final class PunishmentManager {
|
||||
return List.copyOf(typeRegistry.keySet());
|
||||
}
|
||||
|
||||
private void appendToSave(Punishment punishment) throws IOException {
|
||||
public void appendToSave(Punishment punishment) throws IOException {
|
||||
synchronized (ioLock) {
|
||||
Path file = getTargetFile(punishment.getTargetID());
|
||||
if (!Files.exists(file)) {
|
||||
@@ -129,6 +136,7 @@ public final class PunishmentManager {
|
||||
writer.append('\n');
|
||||
}
|
||||
}
|
||||
addToCachedList(punishment);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import de.shiewk.smoderation.paper.command.argument.DurationArgument;
|
||||
import de.shiewk.smoderation.paper.util.PlayerUtil;
|
||||
import de.shiewk.smoderation.paper.util.SerializationHelper;
|
||||
import de.shiewk.smoderation.paper.util.TimeUtil;
|
||||
@@ -35,7 +36,7 @@ public abstract class TimedPunishment extends Punishment {
|
||||
}
|
||||
|
||||
public boolean isActive(){
|
||||
return !wasCancelled() && System.currentTimeMillis() < timestamp + duration;
|
||||
return !wasCancelled() && (isPermanent() || System.currentTimeMillis() < getExpiry());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,6 +56,13 @@ public abstract class TimedPunishment extends Punishment {
|
||||
|
||||
@Override
|
||||
public Component infoMessage(){
|
||||
if (isPermanent()){
|
||||
return translatable(
|
||||
"smod.punishment.playerMessage." + type + ".permanent",
|
||||
text(PlayerUtil.offlinePlayerName(this.issuer)),
|
||||
text(reason)
|
||||
);
|
||||
} else {
|
||||
return translatable(
|
||||
"smod.punishment.playerMessage." + type,
|
||||
text(PlayerUtil.offlinePlayerName(this.issuer)),
|
||||
@@ -62,9 +70,18 @@ public abstract class TimedPunishment extends Punishment {
|
||||
TimeUtil.formatTimeLong(this.timestamp + this.duration - System.currentTimeMillis())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component adminMessage(){
|
||||
if (isPermanent()){
|
||||
return translatable(
|
||||
"smod.punishment.broadcast." + type + ".permanent",
|
||||
text(PlayerUtil.offlinePlayerName(target)),
|
||||
text(PlayerUtil.offlinePlayerName(issuer)),
|
||||
text(reason)
|
||||
);
|
||||
} else {
|
||||
return translatable(
|
||||
"smod.punishment.broadcast." + type,
|
||||
text(PlayerUtil.offlinePlayerName(target)),
|
||||
@@ -73,6 +90,7 @@ public abstract class TimedPunishment extends Punishment {
|
||||
TimeUtil.formatTimeLong(this.duration)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Component cancelMessage(){
|
||||
return translatable(
|
||||
@@ -83,7 +101,11 @@ public abstract class TimedPunishment extends Punishment {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
package de.shiewk.smoderation.paper.translation;
|
||||
|
||||
import com.google.gson.FormattingStyle;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import de.shiewk.smoderation.paper.SModerationPaper;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.translation.MiniMessageTranslationStore;
|
||||
import net.kyori.adventure.translation.GlobalTranslator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -45,4 +48,54 @@ public class TranslatorManager {
|
||||
}
|
||||
GlobalTranslator.translator().addSource(translationStore);
|
||||
}
|
||||
|
||||
public void loadCustomMessages(Path customPath) {
|
||||
try {
|
||||
if (Files.notExists(customPath)) {
|
||||
Files.createDirectories(customPath.getParent());
|
||||
Files.write(customPath, "{}".getBytes(), StandardOpenOption.CREATE);
|
||||
}
|
||||
|
||||
Map<String, String> predefinedMap;
|
||||
try (InputStream stream = SModerationPaper.class.getClassLoader().getResourceAsStream(resourcePath + "en_us.json")) {
|
||||
if (stream == null) {
|
||||
SModerationPaper.LOGGER.warn("English (US) predefined translations not found or not accessible");
|
||||
predefinedMap = Map.of();
|
||||
} else {
|
||||
predefinedMap = SModerationPaper.gson.fromJson(new InputStreamReader(stream), new TypeToken<>(){});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> translationMap;
|
||||
try (InputStream stream = Files.newInputStream(customPath)) {
|
||||
translationMap = SModerationPaper.gson.fromJson(new InputStreamReader(stream), new TypeToken<>(){});
|
||||
}
|
||||
|
||||
boolean updated = false;
|
||||
for (Map.Entry<String, String> entry : predefinedMap.entrySet()) {
|
||||
if (!translationMap.containsKey(entry.getKey())) {
|
||||
translationMap.put(entry.getKey(), entry.getValue());
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
SModerationPaper.LOGGER.warn("Updating {} custom translations", translationMap.size());
|
||||
try (OutputStream stream = Files.newOutputStream(customPath, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream);
|
||||
JsonWriter jsonWriter = new JsonWriter(writer)) {
|
||||
jsonWriter.setFormattingStyle(FormattingStyle.PRETTY);
|
||||
SModerationPaper.gson.toJson(translationMap, translationMap.getClass(), jsonWriter);
|
||||
}
|
||||
SModerationPaper.LOGGER.info("Update successful");
|
||||
}
|
||||
|
||||
translationStore.registerAll(Locale.forLanguageTag("en-US"), translationMap);
|
||||
SModerationPaper.LOGGER.info("Registered {} custom translations", translationMap.size());
|
||||
|
||||
} catch (IOException e) {
|
||||
SModerationPaper.LOGGER.warn("Failed to load custom translations", e);
|
||||
}
|
||||
GlobalTranslator.translator().addSource(translationStore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.shiewk.smoderation.paper.util;
|
||||
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
|
||||
public record ColorPalette(TextColor primary, TextColor secondary, TextColor detail) {
|
||||
|
||||
}
|
||||
@@ -1,7 +1,18 @@
|
||||
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.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static de.shiewk.smoderation.paper.SModerationPaper.LOGGER;
|
||||
|
||||
public final class SModLegacy {
|
||||
private SModLegacy() {}
|
||||
@@ -54,4 +65,76 @@ public final class SModLegacy {
|
||||
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,11 +1,15 @@
|
||||
# If enabled, punishments can no longer be issued without providing a reason.
|
||||
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.
|
||||
features:
|
||||
# Should punishments be tracked and executed?
|
||||
# Note that disabling this will also disable the SMod menu and
|
||||
# /modlogs command since their only use is viewing punishments
|
||||
# /modlogs command since their only use is viewing punishments.
|
||||
punishments: true
|
||||
# Should commands for opening the SMod menu be registered?
|
||||
smodmenu: true
|
||||
@@ -20,6 +24,21 @@ features:
|
||||
# Should players be able to enable vanish mode?
|
||||
vanish: true
|
||||
|
||||
# Allows you to customize plugin colors.
|
||||
# Colors have to be formatted #rrggbb.
|
||||
colors:
|
||||
primary: '#e26c04'
|
||||
secondary: '#fc9e07'
|
||||
detail: '#ababab'
|
||||
|
||||
# Allows you to customize translation files.
|
||||
# - At 'false', the plugin's predefined localization is used
|
||||
# and available for multiple languages:
|
||||
# https://github.com/Shiewk/SModeration/tree/main/src/main/resources/smoderation/translations
|
||||
# - At 'true', a JSON localization file will be created in the plugin directory.
|
||||
# Only the contained strings will be used and localization will be disabled.
|
||||
custom-messages: false
|
||||
|
||||
# A list of commands which will be captured and broadcast
|
||||
# to players which have SocialSpy enabled (/socialspy).
|
||||
socialspy-commands:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"smod.argument.duration.fail.pattern": "Bitte gib eine gültige Zeit an, z.B. '1d6h30min'",
|
||||
"smod.argument.offlinePlayer.fail.notCached": "Die Daten des Spielers sind nicht gespeichert.",
|
||||
"smod.argument.uuid.fail.notCached": "Die Daten des Spielers sind nicht gespeichert. Versuche, stattdessen seine UUID anzugeben.",
|
||||
"smod.chatInput.remainingTime": "<gray><arg:0> Sekunden",
|
||||
"smod.chatInput.remainingTime": "<detail><arg:0> Sekunden",
|
||||
"smod.command.ban.fail.alreadyBanned": "Dieser Spieler ist schon gebannt.",
|
||||
"smod.command.ban.fail.forceReason": "Bitte gib einen Grund an.",
|
||||
"smod.command.ban.fail.protect": "Dieser Spieler kann nicht gebannt werden.",
|
||||
@@ -18,9 +18,11 @@
|
||||
"smod.command.kick.fail.forceReason": "Bitte gib einen Grund an.",
|
||||
"smod.command.kick.fail.protect": "Dieser Spieler kann nicht gekickt werden.",
|
||||
"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.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.ban": "<primary>- ist bis <secondary><arg:0></secondary> <detail>(in <arg:1>)</detail> 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> <detail>(<arg:1>)",
|
||||
"smod.command.modlogs.mute": "<primary>- ist bis <secondary><arg:0></secondary> <detail>(in <arg:1>)</detail> 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.mute.fail.alreadyMuted": "Dieser Spieler ist schon stummgeschaltet.",
|
||||
"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.duration": "<secondary>Dauer: <primary><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.player": "<secondary>Spieler: <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.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.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.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.mute": "<primary><secondary><arg:0></secondary>s Stummschaltung wurde von <secondary><arg:1></secondary> aufgehoben.",
|
||||
"smod.punishment.name.ban": "Bann",
|
||||
"smod.punishment.name.kick": "Kick",
|
||||
"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.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.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.socialspy.command": "<primary>[<secondary>SocialSpy</secondary>] <arg:0>: <secondary><arg:1>",
|
||||
"smod.time.days": "<arg:0> Tage",
|
||||
@@ -106,6 +113,8 @@
|
||||
"smod.time.month.8": "September",
|
||||
"smod.time.month.9": "Oktober",
|
||||
"smod.time.months": "<arg:0> Monate",
|
||||
"smod.time.never": "Nie",
|
||||
"smod.time.permanent": "Permanent",
|
||||
"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.weeks": "<arg:0> Wochen",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"smod.argument.duration.fail.pattern": "Please provide a valid duration, e.g. '1d6h30min'",
|
||||
"smod.argument.offlinePlayer.fail.notCached": "That player's data is not saved.",
|
||||
"smod.argument.uuid.fail.notCached": "That player's data is not saved. Try providing an UUID instead.",
|
||||
"smod.chatInput.remainingTime": "<gray><arg:0> seconds",
|
||||
"smod.chatInput.remainingTime": "<detail><arg:0> seconds",
|
||||
"smod.command.ban.fail.alreadyBanned": "This player is already banned.",
|
||||
"smod.command.ban.fail.forceReason": "Please provide a reason.",
|
||||
"smod.command.ban.fail.protect": "This player can't be banned.",
|
||||
@@ -18,9 +18,11 @@
|
||||
"smod.command.kick.fail.forceReason": "Please provide a reason.",
|
||||
"smod.command.kick.fail.protect": "This player can't be kicked.",
|
||||
"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.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.ban": "<primary>- is banned until <secondary><arg:0></secondary> <detail>(in <arg:1>)</detail>. 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> <detail>(<arg:1>)",
|
||||
"smod.command.modlogs.mute": "<primary>- is muted until <secondary><arg:0></secondary> <detail>(in <arg:1>)</detail>. 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.mute.fail.alreadyMuted": "This player is already muted.",
|
||||
"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.duration": "<secondary>Duration: <primary><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.player": "<secondary>Player: <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.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.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.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.mute": "<primary><secondary><arg:0></secondary> was unmuted by <secondary><arg:1></secondary>.",
|
||||
"smod.punishment.name.ban": "Ban",
|
||||
"smod.punishment.name.kick": "Kick",
|
||||
"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.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.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.socialspy.command": "<primary>[<secondary>SocialSpy</secondary>] <arg:0>: <secondary><arg:1>",
|
||||
"smod.time.days": "<arg:0> days",
|
||||
@@ -106,6 +113,8 @@
|
||||
"smod.time.month.8": "September",
|
||||
"smod.time.month.9": "October",
|
||||
"smod.time.months": "<arg:0> months",
|
||||
"smod.time.never": "Never",
|
||||
"smod.time.permanent": "Permanent",
|
||||
"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.weeks": "<arg:0> weeks",
|
||||
|
||||
Reference in New Issue
Block a user