Skip to content

Commit b108d38

Browse files
committed
feat: add /husksync dump status dumping, close #460
1 parent 8b7e891 commit b108d38

30 files changed

+444
-131
lines changed

bukkit/build.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010

1111
implementation 'net.william278.uniform:uniform-bukkit:1.3.1'
1212
implementation 'net.william278.uniform:uniform-paper:1.3.1'
13+
implementation 'net.william278.toilet:toilet-bukkit:1.0.12'
1314
implementation 'net.william278:mpdbdataconverter:1.0.1'
1415
implementation 'net.william278:hsldataconverter:1.0'
1516
implementation 'net.william278:mapdataapi:2.0'
@@ -72,6 +73,7 @@ shadowJar {
7273
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
7374
relocate 'de.exlll', 'net.william278.husksync.libraries'
7475
relocate 'net.william278.uniform', 'net.william278.husksync.libraries.uniform'
76+
relocate 'net.william278.toilet', 'net.william278.husksync.libraries.toilet'
7577
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
7678
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
7779
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'

bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java

+11
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import net.william278.husksync.event.BukkitEventDispatcher;
4848
import net.william278.husksync.hook.PlanHook;
4949
import net.william278.husksync.listener.BukkitEventListener;
50+
import net.william278.husksync.listener.LockedHandler;
5051
import net.william278.husksync.migrator.LegacyMigrator;
5152
import net.william278.husksync.migrator.Migrator;
5253
import net.william278.husksync.migrator.MpdbMigrator;
@@ -58,6 +59,8 @@
5859
import net.william278.husksync.util.BukkitMapPersister;
5960
import net.william278.husksync.util.BukkitTask;
6061
import net.william278.husksync.util.LegacyConverter;
62+
import net.william278.toilet.BukkitToilet;
63+
import net.william278.toilet.Toilet;
6164
import net.william278.uniform.Uniform;
6265
import net.william278.uniform.bukkit.BukkitUniform;
6366
import org.bstats.bukkit.Metrics;
@@ -100,6 +103,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
100103
private boolean disabling;
101104
private Gson gson;
102105
private AudienceProvider audiences;
106+
private Toilet toilet;
103107
private MorePaperLib paperLib;
104108
private Database database;
105109
private RedisManager redisManager;
@@ -139,6 +143,7 @@ public void onLoad() {
139143
@Override
140144
public void onEnable() {
141145
this.audiences = BukkitAudiences.create(this);
146+
this.toilet = BukkitToilet.create(getDumpOptions());
142147

143148
// Check compatibility
144149
checkCompatibility();
@@ -366,6 +371,12 @@ public Optional<LegacyConverter> getLegacyConverter() {
366371
return Optional.of(legacyConverter);
367372
}
368373

374+
@Override
375+
@NotNull
376+
public LockedHandler getLockedHandler() {
377+
return eventListener.getLockedHandler();
378+
}
379+
369380
@NotNull
370381
public GracefulScheduling getScheduler() {
371382
return paperLib.scheduling();

bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package net.william278.husksync.listener;
2121

22+
import lombok.Getter;
2223
import net.william278.husksync.BukkitHuskSync;
2324
import net.william278.husksync.data.BukkitData;
2425
import net.william278.husksync.user.BukkitUser;
@@ -36,6 +37,7 @@
3637

3738
import java.util.stream.Collectors;
3839

40+
@Getter
3941
public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener,
4042
BukkitDeathEventListener, Listener {
4143

common/build.gradle

+4-1
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ dependencies {
1616
exclude module: 'slf4j-api'
1717
}
1818

19+
compileOnlyApi 'net.william278.toilet:toilet-common:1.0.12'
20+
1921
compileOnly 'net.william278.uniform:uniform-common:1.3.1'
2022
compileOnly 'com.mojang:brigadier:1.1.8'
2123
compileOnly 'org.projectlombok:lombok:1.18.36'
2224
compileOnly 'org.jetbrains:annotations:26.0.2'
23-
compileOnly 'net.kyori:adventure-api:4.18.0'
25+
compileOnly 'net.kyori:adventure-api:4.19.0'
2426
compileOnly 'net.kyori:adventure-platform-api:4.3.4'
27+
compileOnly "net.kyori:adventure-text-serializer-plain:4.19.0"
2528
compileOnly 'com.google.guava:guava:33.4.0-jre'
2629
compileOnly 'com.github.plan-player-analytics:Plan:5.5.2272'
2730
compileOnly "redis.clients:jedis:$jedis_version"

common/src/main/java/net/william278/husksync/HuskSync.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@
3434
import net.william278.husksync.data.SerializerRegistry;
3535
import net.william278.husksync.database.Database;
3636
import net.william278.husksync.event.EventDispatcher;
37+
import net.william278.husksync.listener.LockedHandler;
3738
import net.william278.husksync.migrator.Migrator;
3839
import net.william278.husksync.redis.RedisManager;
3940
import net.william278.husksync.sync.DataSyncer;
4041
import net.william278.husksync.user.ConsoleUser;
4142
import net.william278.husksync.user.OnlineUser;
4243
import net.william278.husksync.util.CompatibilityChecker;
44+
import net.william278.husksync.util.DumpProvider;
4345
import net.william278.husksync.util.LegacyConverter;
4446
import net.william278.husksync.util.Task;
4547
import net.william278.uniform.Uniform;
@@ -54,7 +56,7 @@
5456
* Abstract implementation of the HuskSync plugin.
5557
*/
5658
public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider, SerializerRegistry,
57-
CompatibilityChecker {
59+
CompatibilityChecker, DumpProvider {
5860

5961
int SPIGOT_RESOURCE_ID = 97144;
6062

@@ -302,6 +304,9 @@ default void checkForUpdates() {
302304
}
303305
}
304306

307+
@NotNull
308+
LockedHandler getLockedHandler();
309+
305310
/**
306311
* Get the set of UUIDs of "locked players", for which events will be canceled.
307312
* </p>
@@ -340,12 +345,12 @@ final class FailedToLoadException extends IllegalStateException {
340345
private static final String FORMAT = """
341346
HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized.
342347
Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup):
343-
348+
344349
1) Make sure you've entered your MySQL, MariaDB or MongoDB database details correctly in config.yml
345350
2) Make sure your Redis server details are also correct in config.yml
346351
3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-file)
347352
4) Check the error below for more details
348-
353+
349354
Caused by: %s""";
350355

351356
public FailedToLoadException(@NotNull String message) {

common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java

+24-85
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
import de.themoep.minedown.adventure.MineDown;
2525
import net.kyori.adventure.text.Component;
2626
import net.kyori.adventure.text.JoinConfiguration;
27-
import net.kyori.adventure.text.event.HoverEvent;
27+
import net.kyori.adventure.text.event.ClickEvent;
2828
import net.kyori.adventure.text.format.NamedTextColor;
2929
import net.kyori.adventure.text.format.TextColor;
30+
import net.kyori.adventure.text.format.TextDecoration;
3031
import net.william278.desertwell.about.AboutMenu;
3132
import net.william278.desertwell.util.UpdateChecker;
3233
import net.william278.husksync.HuskSync;
@@ -35,18 +36,17 @@
3536
import net.william278.husksync.migrator.Migrator;
3637
import net.william278.husksync.user.CommandUser;
3738
import net.william278.husksync.util.LegacyConverter;
39+
import net.william278.husksync.util.StatusLine;
3840
import net.william278.uniform.BaseCommand;
3941
import net.william278.uniform.CommandProvider;
4042
import net.william278.uniform.Permission;
4143
import net.william278.uniform.element.ArgumentElement;
42-
import org.apache.commons.text.WordUtils;
4344
import org.jetbrains.annotations.NotNull;
4445

4546
import java.time.OffsetDateTime;
4647
import java.util.Arrays;
4748
import java.util.List;
4849
import java.util.UUID;
49-
import java.util.function.Function;
5050
import java.util.logging.Level;
5151
import java.util.stream.Collectors;
5252

@@ -98,6 +98,7 @@ public void provide(@NotNull BaseCommand<?> command) {
9898
command.setDefaultExecutor((ctx) -> about(command, ctx));
9999
command.addSubCommand("about", (sub) -> sub.setDefaultExecutor((ctx) -> about(command, ctx)));
100100
command.addSubCommand("status", needsOp("status"), status());
101+
command.addSubCommand("dump", needsOp("dump"), dump());
101102
command.addSubCommand("reload", needsOp("reload"), reload());
102103
command.addSubCommand("update", needsOp("update"), update());
103104
command.addSubCommand("forceupgrade", forceUpgrade());
@@ -120,6 +121,26 @@ private CommandProvider status() {
120121
});
121122
}
122123

124+
@NotNull
125+
private CommandProvider dump() {
126+
return (sub) -> {
127+
sub.setDefaultExecutor((ctx) -> {
128+
final CommandUser user = user(sub, ctx);
129+
plugin.getLocales().getLocale("system_dump_confirm").ifPresent(user::sendMessage);
130+
});
131+
sub.addSubCommand("confirm", (con) -> con.setDefaultExecutor((ctx) -> {
132+
final CommandUser user = user(sub, ctx);
133+
plugin.getLocales().getLocale("system_dump_started").ifPresent(user::sendMessage);
134+
plugin.runAsync(() -> {
135+
final String url = plugin.createDump(user);
136+
plugin.getLocales().getLocale("system_dump_ready").ifPresent(user::sendMessage);
137+
user.sendMessage(Component.text(url).clickEvent(ClickEvent.openUrl(url))
138+
.decorate(TextDecoration.UNDERLINED).color(NamedTextColor.GRAY));
139+
});
140+
}));
141+
};
142+
}
143+
123144
@NotNull
124145
private CommandProvider reload() {
125146
return (sub) -> sub.setDefaultExecutor((ctx) -> {
@@ -234,86 +255,4 @@ private <S> ArgumentElement<S, Migrator> migrator() {
234255
});
235256
}
236257

237-
private enum StatusLine {
238-
PLUGIN_VERSION(plugin -> Component.text("v" + plugin.getPluginVersion().toStringWithoutMetadata())
239-
.appendSpace().append(plugin.getPluginVersion().getMetadata().isBlank() ? Component.empty()
240-
: Component.text("(build " + plugin.getPluginVersion().getMetadata() + ")"))),
241-
SERVER_VERSION(plugin -> Component.text(plugin.getServerVersion())),
242-
LANGUAGE(plugin -> Component.text(plugin.getSettings().getLanguage())),
243-
MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())),
244-
JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))),
245-
JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))),
246-
SERVER_NAME(plugin -> Component.text(plugin.getServerName())),
247-
CLUSTER_ID(plugin -> Component.text(plugin.getSettings().getClusterId().isBlank() ? "None" : plugin.getSettings().getClusterId())),
248-
SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully(
249-
plugin.getSettings().getSynchronization().getMode().toString()
250-
))),
251-
DELAY_LATENCY(plugin -> Component.text(
252-
plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds() + "ms"
253-
)),
254-
DATABASE_TYPE(plugin ->
255-
Component.text(plugin.getSettings().getDatabase().getType().getDisplayName() +
256-
(plugin.getSettings().getDatabase().getType() == Database.Type.MONGO ?
257-
(plugin.getSettings().getDatabase().getMongoSettings().isUsingAtlas() ? " Atlas" : "") : ""))
258-
),
259-
IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getDatabase().getCredentials().getHost())),
260-
USING_REDIS_SENTINEL(plugin -> getBoolean(
261-
!plugin.getSettings().getRedis().getSentinel().getMaster().isBlank()
262-
)),
263-
USING_REDIS_PASSWORD(plugin -> getBoolean(
264-
!plugin.getSettings().getRedis().getCredentials().getPassword().isBlank()
265-
)),
266-
REDIS_USING_SSL(plugin -> getBoolean(
267-
plugin.getSettings().getRedis().getCredentials().isUseSsl()
268-
)),
269-
IS_REDIS_LOCAL(plugin -> getLocalhostBoolean(
270-
plugin.getSettings().getRedis().getCredentials().getHost()
271-
)),
272-
DATA_TYPES(plugin -> Component.join(
273-
JoinConfiguration.commas(true),
274-
plugin.getRegisteredDataTypes().stream().map(i -> Component.textOfChildren(Component.text(i.toString())
275-
.appendSpace().append(Component.text(i.isEnabled() ? '✔' : '❌')))
276-
.color(i.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED)
277-
.hoverEvent(HoverEvent.showText(
278-
Component.text(i.isEnabled() ? "Enabled" : "Disabled")
279-
.append(Component.newline())
280-
.append(Component.text("Dependencies: %s".formatted(i.getDependencies()
281-
.isEmpty() ? "(None)" : i.getDependencies().stream()
282-
.map(d -> "%s (%s)".formatted(
283-
d.getKey().value(), d.isRequired() ? "Required" : "Optional"
284-
)).collect(Collectors.joining(", ")))
285-
).color(NamedTextColor.GRAY))
286-
))).toList()
287-
));
288-
289-
private final Function<HuskSync, Component> supplier;
290-
291-
StatusLine(@NotNull Function<HuskSync, Component> supplier) {
292-
this.supplier = supplier;
293-
}
294-
295-
@NotNull
296-
private Component get(@NotNull HuskSync plugin) {
297-
return Component
298-
.text("•").appendSpace()
299-
.append(Component.text(
300-
WordUtils.capitalizeFully(name().replaceAll("_", " ")),
301-
TextColor.color(0x848484)
302-
))
303-
.append(Component.text(':')).append(Component.space().color(NamedTextColor.WHITE))
304-
.append(supplier.apply(plugin));
305-
}
306-
307-
@NotNull
308-
private static Component getBoolean(boolean value) {
309-
return Component.text(value ? "Yes" : "No", value ? NamedTextColor.GREEN : NamedTextColor.RED);
310-
}
311-
312-
@NotNull
313-
private static Component getLocalhostBoolean(@NotNull String value) {
314-
return getBoolean(value.equals("127.0.0.1") || value.equals("0.0.0.0")
315-
|| value.equals("localhost") || value.equals("::1"));
316-
}
317-
}
318-
319258
}

common/src/main/java/net/william278/husksync/command/UserDataCommand.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import net.william278.husksync.redis.RedisManager;
2727
import net.william278.husksync.user.CommandUser;
2828
import net.william278.husksync.user.User;
29-
import net.william278.husksync.util.DataDumper;
29+
import net.william278.husksync.util.UserDataDumper;
3030
import net.william278.husksync.util.DataSnapshotList;
3131
import net.william278.husksync.util.DataSnapshotOverview;
3232
import net.william278.uniform.BaseCommand;
@@ -185,7 +185,7 @@ private void dumpSnapshot(@NotNull CommandUser executor, @NotNull User user, @No
185185

186186
// Dump the data
187187
final DataSnapshot.Packed userData = data.get();
188-
final DataDumper dumper = DataDumper.create(userData, user, plugin);
188+
final UserDataDumper dumper = UserDataDumper.create(userData, user, plugin);
189189
try {
190190
plugin.getLocales().getLocale("data_dumped", userData.getShortId(), user.getUsername(),
191191
(type == DumpType.WEB ? dumper.toWeb() : dumper.toFile()))

common/src/main/java/net/william278/husksync/redis/RedisManager.java

+27
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,33 @@ public boolean getUserServerSwitch(@NotNull User user) {
412412
}
413413
}
414414

415+
@Blocking
416+
public String getStatusDump() {
417+
try (Jedis jedis = jedisPool.getResource()) {
418+
return jedis.info();
419+
}
420+
}
421+
422+
@Blocking
423+
public long getLatency() {
424+
final long startTime = System.currentTimeMillis();
425+
try (Jedis jedis = jedisPool.getResource()) {
426+
jedis.ping();
427+
return startTime - System.currentTimeMillis();
428+
}
429+
}
430+
431+
@Blocking
432+
public String getVersion() {
433+
final String info = getStatusDump();
434+
for (String line : info.split("\n")) {
435+
if (line.startsWith("redis_version:")) {
436+
return line.split(":")[1];
437+
}
438+
}
439+
return "unknown";
440+
}
441+
415442
@Blocking
416443
public void terminate() {
417444
enabled = false;

0 commit comments

Comments
 (0)