Skip to content

Commit 2cc5d1a

Browse files
committed
Added optional duration for bans to enable temporary bans
1 parent 5c9a464 commit 2cc5d1a

File tree

6 files changed

+88
-24
lines changed

6 files changed

+88
-24
lines changed

src/main/java/pro/cloudnode/smp/smpcore/Configuration.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public int joinRequestExpireMinutes() {
4242
return config.getInt("join.request-expire-minutes");
4343
}
4444

45-
public @NotNull Component relativeTime(final int t, final @NotNull ChronoUnit unit) {
45+
public @NotNull Component relativeTime(final Number t, final @NotNull ChronoUnit unit) {
4646
final @NotNull String formatString = Objects.requireNonNull(config.getString("relative-time." + switch (unit) {
4747
case SECONDS -> "seconds";
4848
case MINUTES -> "minutes";
@@ -55,7 +55,7 @@ public int joinRequestExpireMinutes() {
5555
}
5656
}));
5757
return MiniMessage.miniMessage()
58-
.deserialize(formatString, Formatter.number("t", t), Formatter.choice("format", Math.abs(t)));
58+
.deserialize(formatString, Formatter.number("t", t), Formatter.choice("format", Math.abs(t.doubleValue())));
5959
}
6060

6161
public @NotNull Component relativeTimeFuture(final @NotNull Component relativeTime) {
@@ -67,4 +67,13 @@ public int joinRequestExpireMinutes() {
6767
return MiniMessage.miniMessage()
6868
.deserialize(Objects.requireNonNull(config.getString("relative-time.past")), Placeholder.component("t", relativeTime));
6969
}
70+
71+
public @NotNull Component relativeTimeDuration(final @NotNull Component duration) {
72+
return MiniMessage.miniMessage()
73+
.deserialize(Objects.requireNonNull(config.getString("relative-time.duration")), Placeholder.component("t", duration));
74+
}
75+
76+
public @NotNull Component relativeTimeDurationIndefinite() {
77+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("relative-time.duration-indefinite")));
78+
}
7079
}

src/main/java/pro/cloudnode/smp/smpcore/Messages.java

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pro.cloudnode.smp.smpcore;
22

33
import net.kyori.adventure.text.Component;
4+
import net.kyori.adventure.text.JoinConfiguration;
45
import net.kyori.adventure.text.TextComponent;
56
import net.kyori.adventure.text.minimessage.MiniMessage;
67
import net.kyori.adventure.text.minimessage.tag.resolver.Formatter;
@@ -10,7 +11,9 @@
1011
import org.jetbrains.annotations.NotNull;
1112
import org.jetbrains.annotations.Nullable;
1213

14+
import java.time.Duration;
1315
import java.time.ZoneOffset;
16+
import java.time.temporal.ChronoUnit;
1417
import java.util.ArrayList;
1518
import java.util.Arrays;
1619
import java.util.Calendar;
@@ -37,26 +40,57 @@ public Messages() {
3740
.deserialize(Objects.requireNonNull(config.getString("usage")), Placeholder.unparsed("label", label), Placeholder.unparsed("args", args));
3841
}
3942

40-
public @NotNull Component bannedPlayer(final @NotNull OfflinePlayer player) {
43+
private @NotNull Component formatDuration(@Nullable Duration duration) {
44+
if (duration == null) return SMPCore.config().relativeTimeDurationIndefinite();
45+
46+
final long seconds = Math.abs(duration.getSeconds());
47+
final long days = seconds / 86_400;
48+
final long hours = (seconds % 86_400) / 3_600;
49+
final long minutes = (seconds % 3_600) / 60;
50+
final long secs = seconds % 60;
51+
52+
final ArrayList<Component> components = new ArrayList<>();
53+
54+
if (days > 0) components.add(SMPCore.config().relativeTime(days, ChronoUnit.DAYS));
55+
if (hours > 0) components.add(SMPCore.config().relativeTime(hours, ChronoUnit.HOURS));
56+
if (minutes > 0) components.add(SMPCore.config().relativeTime(minutes, ChronoUnit.MINUTES));
57+
if (secs > 0) components.add(SMPCore.config().relativeTime(secs, ChronoUnit.SECONDS));
58+
59+
final Component joined = Component.join(JoinConfiguration.spaces(), components);
60+
61+
return SMPCore.config().relativeTimeDuration(joined);
62+
}
63+
64+
public @NotNull Component bannedPlayer(final @NotNull OfflinePlayer player, final @Nullable Duration duration) {
4165
return MiniMessage.miniMessage()
42-
.deserialize(Objects.requireNonNull(config.getString("banned-player")), Placeholder.unparsed("player", Optional
43-
.ofNullable(player.getName()).orElse(player.getUniqueId().toString())));
66+
.deserialize(
67+
Objects.requireNonNull(config.getString("banned-player")),
68+
Placeholder.unparsed("player", Optional.ofNullable(player.getName()).orElse(player.getUniqueId().toString())),
69+
Placeholder.component("duration", formatDuration(duration))
70+
);
4471
}
4572

46-
public @NotNull Component bannedMember(final @NotNull Member member) {
73+
public @NotNull Component bannedMember(final @NotNull Member member, final @Nullable Duration duration) {
4774
return MiniMessage.miniMessage()
48-
.deserialize(Objects.requireNonNull(config.getString("banned-member")), Placeholder.unparsed("player", Optional
49-
.ofNullable(member.player().getName()).orElse(member.player().getUniqueId().toString())));
75+
.deserialize(
76+
Objects.requireNonNull(config.getString("banned-member")),
77+
Placeholder.unparsed("player", Optional.ofNullable(member.player().getName()).orElse(member.player().getUniqueId().toString())),
78+
Placeholder.component("duration", formatDuration(duration))
79+
);
5080
}
5181

52-
public @NotNull Component bannedMemberChain(final @NotNull Member member, final @NotNull List<@NotNull Member> alts) {
82+
public @NotNull Component bannedMemberChain(final @NotNull Member member, final @NotNull List<@NotNull Member> alts, final @Nullable Duration duration) {
5383
final @NotNull String altsString = alts.stream()
5484
.map(m -> Optional.ofNullable(m.player().getName()).orElse(m.player().getUniqueId().toString()))
5585
.collect(Collectors.joining(", "));
5686
return MiniMessage.miniMessage()
57-
.deserialize(Objects.requireNonNull(config.getString("banned-member-chain")), Placeholder.unparsed("player", Optional
58-
.ofNullable(member.player().getName()).orElse(member.player().getUniqueId()
59-
.toString())), Placeholder.unparsed("n-alt", String.valueOf(alts.size())), Placeholder.unparsed("alts", altsString));
87+
.deserialize(
88+
Objects.requireNonNull(config.getString("banned-member-chain")),
89+
Placeholder.unparsed("player", Optional.ofNullable(member.player().getName()).orElse(member.player().getUniqueId().toString())),
90+
Placeholder.unparsed("n-alt", String.valueOf(alts.size())),
91+
Placeholder.unparsed("alts", altsString),
92+
Placeholder.component("duration", formatDuration(duration))
93+
);
6094
}
6195

6296
public @NotNull Component unbannedPlayer(final @NotNull OfflinePlayer player) {
@@ -568,6 +602,10 @@ public Messages() {
568602
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("error.demote-citizen")));
569603
}
570604

605+
public @NotNull Component errorDurationZeroOrLess() {
606+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("error.duration-zero-or-less")));
607+
}
608+
571609
public record SubCommandArgument(@NotNull String name, boolean required) {
572610
public @NotNull Component component() {
573611
return required ? SMPCore.messages().subCommandArgumentRequired(name) : SMPCore.messages()

src/main/java/pro/cloudnode/smp/smpcore/SMPCore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ public static boolean ifDisallowedCharacters(final @NotNull String source, final
188188
final double years = Math.floor(months / 12.0);
189189

190190
final @NotNull Component t;
191-
if (years > 0) t = SMPCore.config().relativeTime((int) years, ChronoUnit.YEARS);
191+
if (years > 0) t = SMPCore.config().relativeTime(years, ChronoUnit.YEARS);
192192
else if (months > 0) t = SMPCore.config().relativeTime((int) months, ChronoUnit.MONTHS);
193193
else if (days > 0) t = SMPCore.config().relativeTime((int) days, ChronoUnit.DAYS);
194194
else if (hours > 0) t = SMPCore.config().relativeTime((int) hours, ChronoUnit.HOURS);

src/main/java/pro/cloudnode/smp/smpcore/command/BanCommand.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import pro.cloudnode.smp.smpcore.Permission;
1111
import pro.cloudnode.smp.smpcore.SMPCore;
1212

13+
import java.time.Duration;
14+
import java.time.Instant;
15+
import java.time.format.DateTimeParseException;
1316
import java.util.Arrays;
1417
import java.util.Date;
1518
import java.util.HashSet;
@@ -19,38 +22,49 @@
1922

2023
public final class BanCommand extends Command {
2124
/**
22-
* Usage: {@code /<command> <username> [reason]}
25+
* Usage: {@code /<command> <username> [duration] [reason]}
2326
*/
2427
@Override
2528
public boolean run(@NotNull CommandSender sender, @NotNull String label, @NotNull String @NotNull [] args) {
2629
if (!sender.hasPermission(Permission.BAN))
2730
return sendMessage(sender, SMPCore.messages().errorNoPermission());
28-
if (args.length < 1) return sendMessage(sender, SMPCore.messages().usage(label, "<username> [reason]"));
29-
final @NotNull OfflinePlayer target = SMPCore.getInstance().getServer().getOfflinePlayer(args[0]);
31+
if (args.length < 1) return sendMessage(sender, SMPCore.messages().usage(label, "<username> [duration] [reason]"));
32+
33+
final @Nullable String durationArg = args.length > 1 ? args[1] : null;
34+
@Nullable Duration duration = null;
35+
if (durationArg != null) try {
36+
duration = Duration.parse(durationArg);
37+
}
38+
catch (DateTimeParseException ignored) {}
3039

31-
final @Nullable String reason = args.length > 1 ? String.join(" ", Arrays.copyOfRange(args, 1, args.length)) : null;
32-
final @Nullable Date banExpiry = null;
40+
if (duration != null && (duration.isNegative() || duration.isZero()))
41+
return sendMessage(sender, SMPCore.messages().errorDurationZeroOrLess());
42+
43+
final @Nullable Date banExpiry = duration == null ? null : Date.from(Instant.now().plus(duration));
44+
45+
final @Nullable String reason = args.length > 1 ? String.join(" ", Arrays.copyOfRange(args, duration == null ? 1 : 2, args.length)) : null;
3346
final @NotNull NamespacedKey banSource;
3447
if (sender instanceof final @NotNull Player player)
3548
banSource = new NamespacedKey(SMPCore.getInstance(), "player/" + player.getUniqueId());
3649
else banSource = new NamespacedKey(SMPCore.getInstance(), "console");
3750

51+
final @NotNull OfflinePlayer target = SMPCore.getInstance().getServer().getOfflinePlayer(args[0]);
3852
final @NotNull Optional<@NotNull Member> targetMember = Member.get(target);
3953
if (targetMember.isEmpty()) {
4054
SMPCore.runMain(() -> target.ban(reason, banExpiry, banSource.asString()));
41-
return sendMessage(sender, SMPCore.messages().bannedPlayer(target));
55+
return sendMessage(sender, SMPCore.messages().bannedPlayer(target, duration));
4256
}
4357
final @NotNull Member main = targetMember.get().altOwner().orElse(targetMember.get());
4458
final @NotNull HashSet<@NotNull Member> alts = main.getAlts();
4559

4660
SMPCore.runMain(() -> main.player().ban(reason, banExpiry, banSource.asString()));
47-
if (alts.isEmpty()) return sendMessage(sender, SMPCore.messages().bannedMember(main));
61+
if (alts.isEmpty()) return sendMessage(sender, SMPCore.messages().bannedMember(main, duration));
4862
else {
4963
SMPCore.runMain(() -> {
5064
for (final @NotNull Member alt : alts)
5165
alt.player().ban(reason, banExpiry, banSource.asString());
5266
});
53-
return sendMessage(sender, SMPCore.messages().bannedMemberChain(main, alts.stream().toList()));
67+
return sendMessage(sender, SMPCore.messages().bannedMemberChain(main, alts.stream().toList(), duration));
5468
}
5569
}
5670

src/main/resources/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ relative-time:
2323
years: <t> <format:'0#years|1#year|1<years'>
2424
future: in <t>
2525
past: <t> ago
26+
duration: for <t>
27+
duration-indefinite: <u><b>forever</b></u>

src/main/resources/messages.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
reloaded: <green>(!) Plugin successfully reloaded.</green>
22
usage: "<yellow>(!) Usage: <white>/<label> <args></white></yellow>"
33

4-
banned-player: <yellow>(!) Banned singular non-member player <gray><player></gray>.</yellow>
5-
banned-member: <green>(!) Banned member <gray><player></gray>. No alts found.</green>
6-
banned-member-chain: "<green>(!) Banned member <gray><player></gray> and <gray><n-alt></gray> alts: <gray><alts></gray>.</green>"
4+
banned-player: <yellow>(!) Banned singular non-member player <gray><player></gray> <duration>.</yellow>
5+
banned-member: <green>(!) Banned member <gray><player></gray> <duration>. No alts found.</green>
6+
banned-member-chain: "<green>(!) Banned member <gray><player></gray> and <gray><n-alt></gray> alts <duration>: <gray><alts></gray>.</green>"
77

88
unbanned-player: <yellow>(!) Unbanned singular non-member player <gray><player></gray>.</yellow>
99
unbanned-member: <green>(!) Unbanned member <gray><player></gray> and <gray><n-alts></gray> alts.</green>
@@ -116,3 +116,4 @@ error:
116116
already-vice: <red>(!) The vice-leader is already <gray><player></gray>.</red>
117117
demote-leader: <red>(!) You cannot demote the nation leader.</red>
118118
demote-citizen: <red>(!) Don't make this <i>citizen</i> unworthy of the nation! (To remove from nation, use <click:suggest_command:/nation citizens kick ><gray>/nation citizens kick</gray></click>.)</red>
119+
duration-zero-or-less: <red>(!) Duration must be greater than zero.</red>

0 commit comments

Comments
 (0)