Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/main/java/pro/cloudnode/smp/smpcore/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
return config.getInt("join.request-expire-minutes");
}

public @NotNull Component relativeTime(final int t, final @NotNull ChronoUnit unit) {
public @NotNull Component relativeTime(final Number t, final @NotNull ChronoUnit unit) {
final @NotNull String formatString = Objects.requireNonNull(config.getString("relative-time." + switch (unit) {
case SECONDS -> "seconds";
case MINUTES -> "minutes";
Expand All @@ -54,8 +54,10 @@
throw new IllegalStateException("No relative time format for ChronoUnit " + unit);
}
}));
return MiniMessage.miniMessage()
.deserialize(formatString, Formatter.number("t", t), Formatter.choice("format", Math.abs(t)));
return MiniMessage.miniMessage().deserialize(formatString,
Formatter.number("t", t),

Check warning on line 58 in src/main/java/pro/cloudnode/smp/smpcore/Configuration.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

The String "t" appears 4 times in the file.
Formatter.choice("format", Math.abs(t.doubleValue()))
);
}

public @NotNull Component relativeTimeFuture(final @NotNull Component relativeTime) {
Expand All @@ -67,4 +69,18 @@
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("relative-time.past")), Placeholder.component("t", relativeTime));
}

public @NotNull Component relativeTimeDuration(final @NotNull Component duration) {
return MiniMessage.miniMessage()
.deserialize(
Objects.requireNonNull(config.getString("relative-time.duration")),
Placeholder.component("t", duration)
);
}

public @NotNull Component relativeTimeDurationIndefinite() {
return MiniMessage.miniMessage().deserialize(
Objects.requireNonNull(config.getString("relative-time.duration-indefinite"))
);
}
}
80 changes: 69 additions & 11 deletions src/main/java/pro/cloudnode/smp/smpcore/Messages.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pro.cloudnode.smp.smpcore;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Formatter;
Expand All @@ -10,7 +11,9 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.Duration;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
Expand All @@ -22,7 +25,7 @@
import java.util.TimeZone;
import java.util.stream.Collectors;

public class Messages extends BaseConfig {
public final class Messages extends BaseConfig {

Check warning on line 28 in src/main/java/pro/cloudnode/smp/smpcore/Messages.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

Possible God Class (WMC=112, ATFD=182, TCC=0.000%)

Check warning on line 28 in src/main/java/pro/cloudnode/smp/smpcore/Messages.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

The class 'Messages' has a total cyclomatic complexity of 112 (highest 20).

Check warning on line 28 in src/main/java/pro/cloudnode/smp/smpcore/Messages.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

This class has too many methods, consider refactoring it.

public Messages() {
super("messages.yml");
Expand All @@ -37,26 +40,70 @@
.deserialize(Objects.requireNonNull(config.getString("usage")), Placeholder.unparsed("label", label), Placeholder.unparsed("args", args));
}

public @NotNull Component bannedPlayer(final @NotNull OfflinePlayer player) {
private @NotNull Component formatDuration(@Nullable Duration duration) {
if (duration == null) return SMPCore.config().relativeTimeDurationIndefinite();

final long seconds = Math.abs(duration.getSeconds());
final long days = seconds / 86_400;
final long hours = (seconds % 86_400) / 3_600;
final long minutes = (seconds % 3_600) / 60;
final long secs = seconds % 60;

final ArrayList<Component> components = new ArrayList<>();

if (days > 0) components.add(SMPCore.config().relativeTime(days, ChronoUnit.DAYS));
if (hours > 0) components.add(SMPCore.config().relativeTime(hours, ChronoUnit.HOURS));
if (minutes > 0) components.add(SMPCore.config().relativeTime(minutes, ChronoUnit.MINUTES));
if (secs > 0) components.add(SMPCore.config().relativeTime(secs, ChronoUnit.SECONDS));

final Component joined = Component.join(JoinConfiguration.spaces(), components);

return SMPCore.config().relativeTimeDuration(joined);
}

public @NotNull Component bannedPlayer(final @NotNull OfflinePlayer player, final @Nullable Duration duration) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("banned-player")), Placeholder.unparsed("player", Optional
.ofNullable(player.getName()).orElse(player.getUniqueId().toString())));
.deserialize(
Objects.requireNonNull(config.getString("banned-player")),
Placeholder.unparsed("player",

Check warning on line 68 in src/main/java/pro/cloudnode/smp/smpcore/Messages.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

The String "player" appears 30 times in the file.

Check warning on line 68 in src/main/java/pro/cloudnode/smp/smpcore/Messages.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

The String literal "player" appears 30 times in this file; the first occurrence is on line 68
Optional.ofNullable(player.getName())
.orElse(player.getUniqueId().toString())
),
Placeholder.component("duration", formatDuration(duration))

Check warning on line 72 in src/main/java/pro/cloudnode/smp/smpcore/Messages.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

The String "duration" appears 3 times in the file.

Check warning on line 72 in src/main/java/pro/cloudnode/smp/smpcore/Messages.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

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

The String literal "duration" appears 4 times in this file; the first occurrence is on line 72
);
}

public @NotNull Component bannedMember(final @NotNull Member member) {
public @NotNull Component bannedMember(final @NotNull Member member, final @Nullable Duration duration) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("banned-member")), Placeholder.unparsed("player", Optional
.ofNullable(member.player().getName()).orElse(member.player().getUniqueId().toString())));
.deserialize(
Objects.requireNonNull(config.getString("banned-member")),
Placeholder.unparsed("player",
Optional.ofNullable(member.player().getName())
.orElse(member.player().getUniqueId().toString())
),
Placeholder.component("duration", formatDuration(duration))
);
}

public @NotNull Component bannedMemberChain(final @NotNull Member member, final @NotNull List<@NotNull Member> alts) {
public @NotNull Component bannedMemberChain(
final @NotNull Member member,
final @NotNull List<@NotNull Member> alts,
final @Nullable Duration duration
) {
final @NotNull String altsString = alts.stream()
.map(m -> Optional.ofNullable(m.player().getName()).orElse(m.player().getUniqueId().toString()))
.collect(Collectors.joining(", "));
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("banned-member-chain")), Placeholder.unparsed("player", Optional
.ofNullable(member.player().getName()).orElse(member.player().getUniqueId()
.toString())), Placeholder.unparsed("n-alt", String.valueOf(alts.size())), Placeholder.unparsed("alts", altsString));
.deserialize(
Objects.requireNonNull(config.getString("banned-member-chain")),
Placeholder.unparsed("player",
Optional.ofNullable(member.player().getName())
.orElse(member.player().getUniqueId().toString())
),
Placeholder.unparsed("n-alt", String.valueOf(alts.size())),
Placeholder.unparsed("alts", altsString),
Placeholder.component("duration", formatDuration(duration))
);
}

public @NotNull Component unbannedPlayer(final @NotNull OfflinePlayer player) {
Expand Down Expand Up @@ -568,6 +615,17 @@
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("error.demote-citizen")));
}

public @NotNull Component errorDurationZeroOrLess() {
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("error.duration-zero-or-less")));
}

public @NotNull Component invalidDuration(final @NotNull String duration) {
return MiniMessage.miniMessage().deserialize(
Objects.requireNonNull(config.getString("error.invalid-duration")),
Placeholder.unparsed("duration", duration)
);
}

public record SubCommandArgument(@NotNull String name, boolean required) {
public @NotNull Component component() {
return required ? SMPCore.messages().subCommandArgumentRequired(name) : SMPCore.messages()
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/pro/cloudnode/smp/smpcore/SMPCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public static boolean ifDisallowedCharacters(final @NotNull String source, final
final double years = Math.floor(months / 12.0);

final @NotNull Component t;
if (years > 0) t = SMPCore.config().relativeTime((int) years, ChronoUnit.YEARS);
if (years > 0) t = SMPCore.config().relativeTime(years, ChronoUnit.YEARS);
else if (months > 0) t = SMPCore.config().relativeTime((int) months, ChronoUnit.MONTHS);
else if (days > 0) t = SMPCore.config().relativeTime((int) days, ChronoUnit.DAYS);
else if (hours > 0) t = SMPCore.config().relativeTime((int) hours, ChronoUnit.HOURS);
Expand Down
35 changes: 27 additions & 8 deletions src/main/java/pro/cloudnode/smp/smpcore/command/BanCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import pro.cloudnode.smp.smpcore.Permission;
import pro.cloudnode.smp.smpcore.SMPCore;

import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
Expand All @@ -19,38 +22,54 @@

public final class BanCommand extends Command {
/**
* Usage: {@code /<command> <username> [reason]}
* Usage: {@code /<command> <username> [duration] [reason]}
*/
@Override
public boolean run(@NotNull CommandSender sender, @NotNull String label, @NotNull String @NotNull [] args) {
if (!sender.hasPermission(Permission.BAN))
return sendMessage(sender, SMPCore.messages().errorNoPermission());
if (args.length < 1) return sendMessage(sender, SMPCore.messages().usage(label, "<username> [reason]"));
final @NotNull OfflinePlayer target = SMPCore.getInstance().getServer().getOfflinePlayer(args[0]);
if (args.length < 1)
return sendMessage(sender, SMPCore.messages().usage(label, "<username> [duration] [reason]"));

final @Nullable String durationArg = args.length > 1 ? args[1] : null;
@Nullable Duration duration = null;
if (durationArg != null && durationArg.matches("(?i)^PT\\d.*")) try {
duration = Duration.parse(durationArg);
}
catch (DateTimeParseException ignored) {
return sendMessage(sender, SMPCore.messages().invalidDuration(durationArg));
}

final @Nullable String reason = args.length > 1 ? String.join(" ", Arrays.copyOfRange(args, 1, args.length)) : null;
final @Nullable Date banExpiry = null;
if (duration != null && (duration.isNegative() || duration.isZero()))
return sendMessage(sender, SMPCore.messages().errorDurationZeroOrLess());

final @Nullable Date banExpiry = duration == null ? null : Date.from(Instant.now().plus(duration));

final @Nullable String reason = args.length > 1
? String.join(" ", Arrays.copyOfRange(args, duration == null ? 1 : 2, args.length))
: null;
final @NotNull NamespacedKey banSource;
if (sender instanceof final @NotNull Player player)
banSource = new NamespacedKey(SMPCore.getInstance(), "player/" + player.getUniqueId());
else banSource = new NamespacedKey(SMPCore.getInstance(), "console");

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

SMPCore.runMain(() -> main.player().ban(reason, banExpiry, banSource.asString()));
if (alts.isEmpty()) return sendMessage(sender, SMPCore.messages().bannedMember(main));
if (alts.isEmpty()) return sendMessage(sender, SMPCore.messages().bannedMember(main, duration));
else {
SMPCore.runMain(() -> {
for (final @NotNull Member alt : alts)
alt.player().ban(reason, banExpiry, banSource.asString());
});
return sendMessage(sender, SMPCore.messages().bannedMemberChain(main, alts.stream().toList()));
return sendMessage(sender, SMPCore.messages().bannedMemberChain(main, alts.stream().toList(), duration));
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ relative-time:
years: <t> <format:'0#years|1#year|1<years'>
future: in <t>
past: <t> ago
duration: for <t>
duration-indefinite: <u><b>forever</b></u>
8 changes: 5 additions & 3 deletions src/main/resources/messages.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
reloaded: <green>(!) Plugin successfully reloaded.</green>
usage: "<yellow>(!) Usage: <white>/<label> <args></white></yellow>"

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

unbanned-player: <yellow>(!) Unbanned singular non-member player <gray><player></gray>.</yellow>
unbanned-member: <green>(!) Unbanned member <gray><player></gray> and <gray><n-alts></gray> alts.</green>
Expand Down Expand Up @@ -116,3 +116,5 @@ error:
already-vice: <red>(!) The vice-leader is already <gray><player></gray>.</red>
demote-leader: <red>(!) You cannot demote the nation leader.</red>
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>
duration-zero-or-less: <red>(!) Duration must be greater than zero.</red>
invalid-duration: <red>(!) Invalid duration format <gray>‘<duration></gray>’.</red>
Loading