Skip to content

Commit 61278a5

Browse files
authored
Command to check when player was last online (#18)
2 parents 2a6f798 + 9386103 commit 61278a5

File tree

8 files changed

+158
-0
lines changed

8 files changed

+158
-0
lines changed

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
package pro.cloudnode.smp.smpcore;
22

3+
import net.kyori.adventure.text.Component;
4+
import net.kyori.adventure.text.minimessage.MiniMessage;
5+
import net.kyori.adventure.text.minimessage.tag.resolver.Formatter;
6+
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
import java.time.temporal.ChronoUnit;
10+
import java.util.Objects;
11+
312
public final class Configuration extends BaseConfig {
413
public Configuration() {
514
super("config.yml");
@@ -25,4 +34,30 @@ public int membersInactiveDays() {
2534
public int altsMax() {
2635
return config.getInt("alts.max");
2736
}
37+
38+
public @NotNull Component relativeTime(final int t, final @NotNull ChronoUnit unit) {
39+
final @NotNull String formatString = Objects.requireNonNull(config.getString("relative-time." + switch (unit) {
40+
case SECONDS -> "seconds";
41+
case MINUTES -> "minutes";
42+
case HOURS -> "hours";
43+
case DAYS -> "days";
44+
case MONTHS -> "months";
45+
case YEARS -> "years";
46+
default -> {
47+
throw new IllegalStateException("No relative time format for ChronoUnit " + unit);
48+
}
49+
}));
50+
return MiniMessage.miniMessage()
51+
.deserialize(formatString, Formatter.number("t", t), Formatter.choice("format", Math.abs(t)));
52+
}
53+
54+
public @NotNull Component relativeTimeFuture(final @NotNull Component relativeTime) {
55+
return MiniMessage.miniMessage()
56+
.deserialize(Objects.requireNonNull(config.getString("relative-time.future")), Placeholder.component("t", relativeTime));
57+
}
58+
59+
public @NotNull Component relativeTimePast(final @NotNull Component relativeTime) {
60+
return MiniMessage.miniMessage()
61+
.deserialize(Objects.requireNonNull(config.getString("relative-time.past")), Placeholder.component("t", relativeTime));
62+
}
2863
}

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
import net.kyori.adventure.text.Component;
44
import net.kyori.adventure.text.TextComponent;
55
import net.kyori.adventure.text.minimessage.MiniMessage;
6+
import net.kyori.adventure.text.minimessage.tag.resolver.Formatter;
67
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
78
import org.bukkit.OfflinePlayer;
89
import org.jetbrains.annotations.NotNull;
910
import org.jetbrains.annotations.Nullable;
1011

12+
import java.time.ZoneOffset;
1113
import java.util.Arrays;
14+
import java.util.Date;
1215
import java.util.HashSet;
1316
import java.util.List;
1417
import java.util.Objects;
@@ -141,6 +144,32 @@ public Messages() {
141144
.ofNullable(alt.player().getName()).orElse(alt.player().getUniqueId().toString())));
142145
}
143146

147+
public @NotNull Component seen(final @NotNull Member member) {
148+
if (member.player().isOnline()) return MiniMessage.miniMessage()
149+
.deserialize(Objects.requireNonNull(config.getString("seen.online")), Placeholder.unparsed("player", Optional
150+
.ofNullable(member.player().getName()).orElse(member.uuid.toString())));
151+
final @NotNull Date lastSeen = new Date(member.player().getLastSeen());
152+
return MiniMessage.miniMessage()
153+
.deserialize(Objects.requireNonNull(config.getString(member.isActive() ? "seen.active" : "seen.inactive")), Placeholder.unparsed("player", Optional
154+
.ofNullable(member.player().getName())
155+
.orElse(member.uuid.toString())), Formatter.date("last-seen", lastSeen.toInstant()
156+
.atZone(ZoneOffset.UTC)
157+
.toLocalDateTime()), Placeholder.component("last-seen-relative", SMPCore.relativeTime(lastSeen)));
158+
}
159+
160+
public @NotNull Component seen(final @NotNull OfflinePlayer player) {
161+
if (player.isOnline()) return MiniMessage.miniMessage()
162+
.deserialize(Objects.requireNonNull(config.getString("seen.online")), Placeholder.unparsed("player", Optional
163+
.ofNullable(player.getName()).orElse(player.getUniqueId().toString())));
164+
final @NotNull Date lastSeen = new Date(player.getLastSeen());
165+
return MiniMessage.miniMessage()
166+
.deserialize(Objects.requireNonNull(config.getString("seen.non-member")), Placeholder.unparsed("player", Optional
167+
.ofNullable(player.getName())
168+
.orElse(player.getUniqueId().toString())), Formatter.date("last-seen", lastSeen.toInstant()
169+
.atZone(ZoneOffset.UTC)
170+
.toLocalDateTime()), Placeholder.component("last-seen-relative", SMPCore.relativeTime(lastSeen)));
171+
}
172+
144173
// errors
145174

146175
public @NotNull Component errorNoPermission() {
@@ -204,6 +233,12 @@ public Messages() {
204233
.ofNullable(player.player().getName()).orElse(player.player().getUniqueId().toString())));
205234
}
206235

236+
public @NotNull Component errorNeverJoined(final @NotNull OfflinePlayer player) {
237+
return MiniMessage.miniMessage()
238+
.deserialize(Objects.requireNonNull(config.getString("error.never-joined")), Placeholder.unparsed("player", Optional
239+
.ofNullable(player.getName()).orElse(player.getUniqueId().toString())));
240+
}
241+
207242
public record SubCommandArgument(@NotNull String name, boolean required) {
208243
public @NotNull Component component() {
209244
return required ? SMPCore.messages().subCommandArgumentRequired(name) : SMPCore.messages()

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ public final class Permission {
4444
* Remove an alt that has joined the server
4545
*/
4646
public static @NotNull String ALT_REMOVE_JOINED = "smpcore.alt.remove.joined";
47+
48+
public static @NotNull String SEEN = "smpcore.seen";
4749
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import com.zaxxer.hikari.HikariConfig;
44
import com.zaxxer.hikari.HikariDataSource;
5+
import net.kyori.adventure.text.Component;
56
import org.bukkit.plugin.java.JavaPlugin;
67
import org.jetbrains.annotations.NotNull;
78
import org.jetbrains.annotations.Nullable;
89
import pro.cloudnode.smp.smpcore.command.AltsCommand;
910
import pro.cloudnode.smp.smpcore.command.BanCommand;
1011
import pro.cloudnode.smp.smpcore.command.Command;
1112
import pro.cloudnode.smp.smpcore.command.MainCommand;
13+
import pro.cloudnode.smp.smpcore.command.SeenCommand;
1214
import pro.cloudnode.smp.smpcore.command.UnbanCommand;
1315
import pro.cloudnode.smp.smpcore.listener.NationTeamUpdaterListener;
1416

@@ -17,6 +19,8 @@
1719
import java.sql.Connection;
1820
import java.sql.PreparedStatement;
1921
import java.sql.SQLException;
22+
import java.time.temporal.ChronoUnit;
23+
import java.util.Date;
2024
import java.util.HashMap;
2125
import java.util.HashSet;
2226
import java.util.Map;
@@ -68,6 +72,7 @@ public void onEnable() {
6872
put("smpcore", new MainCommand());
6973
put("ban", new BanCommand());
7074
put("unban", new UnbanCommand());
75+
put("seen", new SeenCommand());
7176
}};
7277
commands.put("alts", new AltsCommand(commands.get("smpcore")));
7378
for (final @NotNull Map.Entry<@NotNull String, @NotNull Command> entry : commands.entrySet())
@@ -162,4 +167,29 @@ public static boolean ifDisallowedCharacters(final @NotNull String source, final
162167
}
163168
return false;
164169
}
170+
171+
public static @NotNull Component relativeTime(final @NotNull Date date1, final @NotNull Date date2) {
172+
final long diff = date1.getTime() - date2.getTime();
173+
final long abs = Math.abs(diff);
174+
final double seconds = Math.floor(abs / 1000.0);
175+
final double minutes = Math.floor(seconds / 60.0);
176+
final double hours = Math.floor(minutes / 60.0);
177+
final double days = Math.floor(hours / 24.0);
178+
final double months = Math.floor(days / 30.0);
179+
final double years = Math.floor(months / 12.0);
180+
181+
final @NotNull Component t;
182+
if (years > 0) t = SMPCore.config().relativeTime((int) years, ChronoUnit.YEARS);
183+
else if (months > 0) t = SMPCore.config().relativeTime((int) months, ChronoUnit.MONTHS);
184+
else if (days > 0) t = SMPCore.config().relativeTime((int) days, ChronoUnit.DAYS);
185+
else if (hours > 0) t = SMPCore.config().relativeTime((int) hours, ChronoUnit.HOURS);
186+
else if (minutes > 0) t = SMPCore.config().relativeTime((int) minutes, ChronoUnit.MINUTES);
187+
else t = SMPCore.config().relativeTime((int) seconds, ChronoUnit.SECONDS);
188+
189+
return diff < 0 ? SMPCore.config().relativeTimePast(t) : SMPCore.config().relativeTimeFuture(t);
190+
}
191+
192+
public static @NotNull Component relativeTime(final @NotNull Date date) {
193+
return relativeTime(date, new Date());
194+
}
165195
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package pro.cloudnode.smp.smpcore.command;
2+
3+
import org.bukkit.OfflinePlayer;
4+
import org.bukkit.command.CommandSender;
5+
import org.jetbrains.annotations.NotNull;
6+
import pro.cloudnode.smp.smpcore.Member;
7+
import pro.cloudnode.smp.smpcore.Permission;
8+
import pro.cloudnode.smp.smpcore.SMPCore;
9+
10+
import java.util.List;
11+
import java.util.Optional;
12+
13+
public final class SeenCommand extends Command {
14+
15+
@Override
16+
public boolean run(@NotNull CommandSender sender, @NotNull String label, @NotNull String @NotNull [] args) {
17+
if (!sender.hasPermission(Permission.SEEN)) return sendMessage(sender, SMPCore.messages().errorNoPermission());
18+
19+
if (args.length != 1) return sendMessage(sender, SMPCore.messages().usage(label, "<player>"));
20+
21+
final @NotNull OfflinePlayer player = sender.getServer().getOfflinePlayer(args[0]);
22+
23+
if (!player.hasPlayedBefore()) return sendMessage(sender, SMPCore.messages().errorNeverJoined(player));
24+
25+
final @NotNull Optional<@NotNull Member> member = Member.get(player.getUniqueId());
26+
return member.map(m -> sendMessage(sender, SMPCore.messages().seen(m)))
27+
.orElseGet(() -> sendMessage(sender, SMPCore.messages().seen(player)));
28+
}
29+
30+
@Override
31+
public @NotNull List<@NotNull String> tab(@NotNull CommandSender sender, @NotNull String label, @NotNull String @NotNull [] args) {
32+
return Member.getNames().stream().toList();
33+
}
34+
}

src/main/resources/config.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,13 @@ members:
99
alts:
1010
# Maximum number of alts you can have
1111
max: 10
12+
13+
relative-time:
14+
seconds: <t> <format:'0#seconds|1#second|1<seconds'>
15+
minutes: <t> <format:'0#minutes|1#minute|1<minutes'>
16+
hours: <t> <format:'0#hours|1#hour|1<hours'>
17+
days: <t> <format:'0#days|1#day|1<days'>
18+
months: <t> <format:'0#months|1#month|1<months'>
19+
years: <t> <format:'0#years|1#year|1<years'>
20+
future: in <t>
21+
past: <t> ago

src/main/resources/messages.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ alts:
3333
created: <green>(!) Created member profile for <gray><alt></gray> and added as an alt.</green>
3434
deleted: <green>(!) Deleted alt member profile for <gray><alt></gray>.</green>
3535

36+
seen:
37+
online: <green>(!) Player <gray><player></gray> is online.</green>
38+
active: <aqua>(!) Member <white><player></white> is <green>active</green> and last seen on <white><last-seen:'dd MMM yyyy HH:mm'> UTC</white> <gray>(<last-seen-relative>)</aqua>
39+
inactive: <aqua>(!) Member <white><player></white> is <red>inactive</red> and last seen on <white><last-seen:'dd MMM yyyy HH:mm'> UTC</white> <gray>(<last-seen-relative>)</aqua>
40+
non-member: <aqua>(!) Player <white><player></white> was last seen on <white><last-seen:'dd MMM yyyy HH:mm'> UTC</white> <gray>(<last-seen-relative>)</aqua>
41+
3642
error:
3743
no-permission: <red>(!) You don't have permission to use this command.</red>
3844
player-not-banned: <red>(!) Player <gray><player></gray> is not banned and is not a member.</red>
@@ -45,3 +51,4 @@ error:
4551
max-alts-reached: <red>(!) You cannot have more than <gray><max></gray> alts.</red>
4652
member-not-alt: <red>(!) Member <gray><player></gray> is not an alt.</red>
4753
remove-joined-alt: <red>(!) You cannot remove alt player <gray><alt></gray> because they have played the server.</red>
54+
never-joined: <red>(!) Player <gray><player></gray> has never played on the server.</red>

src/main/resources/plugin.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ commands:
2222
description: Manage alternate accounts
2323
usage: /<command>
2424
aliases: [ alt ]
25+
seen:
26+
permission: smpcore.seen
27+
description: Check when a player was last online
28+
usage: /<command> <player>
29+
aliases: [ lastseen ]

0 commit comments

Comments
 (0)