From b108d3859864d637659e0bf3629cd13308ba3101 Mon Sep 17 00:00:00 2001 From: William278 Date: Thu, 6 Mar 2025 14:36:33 +0000 Subject: [PATCH] feat: add `/husksync dump` status dumping, close #460 --- bukkit/build.gradle | 2 + .../william278/husksync/BukkitHuskSync.java | 11 ++ .../listener/BukkitEventListener.java | 2 + common/build.gradle | 5 +- .../net/william278/husksync/HuskSync.java | 11 +- .../husksync/command/HuskSyncCommand.java | 109 +++--------- .../husksync/command/UserDataCommand.java | 4 +- .../husksync/redis/RedisManager.java | 27 +++ .../net/william278/husksync/user/User.java | 36 +--- .../husksync/util/DumpProvider.java | 158 ++++++++++++++++++ .../william278/husksync/util/StatusLine.java | 124 ++++++++++++++ .../{DataDumper.java => UserDataDumper.java} | 24 +-- common/src/main/resources/locales/bg-bg.yml | 3 + common/src/main/resources/locales/de-de.yml | 3 + common/src/main/resources/locales/en-gb.yml | 3 + common/src/main/resources/locales/es-es.yml | 3 + common/src/main/resources/locales/fr-fr.yml | 3 + common/src/main/resources/locales/id-id.yml | 3 + common/src/main/resources/locales/it-it.yml | 3 + common/src/main/resources/locales/ja-jp.yml | 3 + common/src/main/resources/locales/ko-kr.yml | 3 + common/src/main/resources/locales/nl-nl.yml | 3 + common/src/main/resources/locales/pt-br.yml | 3 + common/src/main/resources/locales/ru-ru.yml | 3 + common/src/main/resources/locales/tr-tr.yml | 3 + common/src/main/resources/locales/uk-ua.yml | 3 + common/src/main/resources/locales/zh-cn.yml | 3 + common/src/main/resources/locales/zh-tw.yml | 3 + fabric/build.gradle | 1 + .../william278/husksync/FabricHuskSync.java | 13 +- 30 files changed, 444 insertions(+), 131 deletions(-) create mode 100644 common/src/main/java/net/william278/husksync/util/DumpProvider.java create mode 100644 common/src/main/java/net/william278/husksync/util/StatusLine.java rename common/src/main/java/net/william278/husksync/util/{DataDumper.java => UserDataDumper.java} (87%) diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 030ff116..16199b32 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -10,6 +10,7 @@ dependencies { implementation 'net.william278.uniform:uniform-bukkit:1.3.1' implementation 'net.william278.uniform:uniform-paper:1.3.1' + implementation 'net.william278.toilet:toilet-bukkit:1.0.12' implementation 'net.william278:mpdbdataconverter:1.0.1' implementation 'net.william278:hsldataconverter:1.0' implementation 'net.william278:mapdataapi:2.0' @@ -72,6 +73,7 @@ shadowJar { relocate 'com.zaxxer', 'net.william278.husksync.libraries' relocate 'de.exlll', 'net.william278.husksync.libraries' relocate 'net.william278.uniform', 'net.william278.husksync.libraries.uniform' + relocate 'net.william278.toilet', 'net.william278.husksync.libraries.toilet' relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell' relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown' relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi' diff --git a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java index b32c1cb3..c4303063 100644 --- a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java +++ b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java @@ -47,6 +47,7 @@ import net.william278.husksync.database.PostgresDatabase; import net.william278.husksync.event.BukkitEventDispatcher; import net.william278.husksync.hook.PlanHook; import net.william278.husksync.listener.BukkitEventListener; +import net.william278.husksync.listener.LockedHandler; import net.william278.husksync.migrator.LegacyMigrator; import net.william278.husksync.migrator.Migrator; import net.william278.husksync.migrator.MpdbMigrator; @@ -58,6 +59,8 @@ import net.william278.husksync.util.BukkitLegacyConverter; import net.william278.husksync.util.BukkitMapPersister; import net.william278.husksync.util.BukkitTask; import net.william278.husksync.util.LegacyConverter; +import net.william278.toilet.BukkitToilet; +import net.william278.toilet.Toilet; import net.william278.uniform.Uniform; import net.william278.uniform.bukkit.BukkitUniform; import org.bstats.bukkit.Metrics; @@ -100,6 +103,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S private boolean disabling; private Gson gson; private AudienceProvider audiences; + private Toilet toilet; private MorePaperLib paperLib; private Database database; private RedisManager redisManager; @@ -139,6 +143,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S @Override public void onEnable() { this.audiences = BukkitAudiences.create(this); + this.toilet = BukkitToilet.create(getDumpOptions()); // Check compatibility checkCompatibility(); @@ -366,6 +371,12 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S return Optional.of(legacyConverter); } + @Override + @NotNull + public LockedHandler getLockedHandler() { + return eventListener.getLockedHandler(); + } + @NotNull public GracefulScheduling getScheduler() { return paperLib.scheduling(); diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java index 7bdea7d7..f00ca273 100644 --- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java +++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java @@ -19,6 +19,7 @@ package net.william278.husksync.listener; +import lombok.Getter; import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.data.BukkitData; import net.william278.husksync.user.BukkitUser; @@ -36,6 +37,7 @@ import org.jetbrains.annotations.NotNull; import java.util.stream.Collectors; +@Getter public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener, BukkitDeathEventListener, Listener { diff --git a/common/build.gradle b/common/build.gradle index 764570ae..c5b944cc 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -16,12 +16,15 @@ dependencies { exclude module: 'slf4j-api' } + compileOnlyApi 'net.william278.toilet:toilet-common:1.0.12' + compileOnly 'net.william278.uniform:uniform-common:1.3.1' compileOnly 'com.mojang:brigadier:1.1.8' compileOnly 'org.projectlombok:lombok:1.18.36' compileOnly 'org.jetbrains:annotations:26.0.2' - compileOnly 'net.kyori:adventure-api:4.18.0' + compileOnly 'net.kyori:adventure-api:4.19.0' compileOnly 'net.kyori:adventure-platform-api:4.3.4' + compileOnly "net.kyori:adventure-text-serializer-plain:4.19.0" compileOnly 'com.google.guava:guava:33.4.0-jre' compileOnly 'com.github.plan-player-analytics:Plan:5.5.2272' compileOnly "redis.clients:jedis:$jedis_version" diff --git a/common/src/main/java/net/william278/husksync/HuskSync.java b/common/src/main/java/net/william278/husksync/HuskSync.java index fff9cf1b..53db1c02 100644 --- a/common/src/main/java/net/william278/husksync/HuskSync.java +++ b/common/src/main/java/net/william278/husksync/HuskSync.java @@ -34,12 +34,14 @@ import net.william278.husksync.data.Identifier; import net.william278.husksync.data.SerializerRegistry; import net.william278.husksync.database.Database; import net.william278.husksync.event.EventDispatcher; +import net.william278.husksync.listener.LockedHandler; import net.william278.husksync.migrator.Migrator; import net.william278.husksync.redis.RedisManager; import net.william278.husksync.sync.DataSyncer; import net.william278.husksync.user.ConsoleUser; import net.william278.husksync.user.OnlineUser; import net.william278.husksync.util.CompatibilityChecker; +import net.william278.husksync.util.DumpProvider; import net.william278.husksync.util.LegacyConverter; import net.william278.husksync.util.Task; import net.william278.uniform.Uniform; @@ -54,7 +56,7 @@ import java.util.logging.Level; * Abstract implementation of the HuskSync plugin. */ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider, SerializerRegistry, - CompatibilityChecker { + CompatibilityChecker, DumpProvider { int SPIGOT_RESOURCE_ID = 97144; @@ -302,6 +304,9 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider } } + @NotNull + LockedHandler getLockedHandler(); + /** * Get the set of UUIDs of "locked players", for which events will be canceled. *

@@ -340,12 +345,12 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider private static final String FORMAT = """ HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized. Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup): - + 1) Make sure you've entered your MySQL, MariaDB or MongoDB database details correctly in config.yml 2) Make sure your Redis server details are also correct in config.yml 3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-file) 4) Check the error below for more details - + Caused by: %s"""; public FailedToLoadException(@NotNull String message) { diff --git a/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java b/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java index e2106db7..ae8c41e4 100644 --- a/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java +++ b/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java @@ -24,9 +24,10 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import de.themoep.minedown.adventure.MineDown; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.JoinConfiguration; -import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.william278.desertwell.about.AboutMenu; import net.william278.desertwell.util.UpdateChecker; import net.william278.husksync.HuskSync; @@ -35,18 +36,17 @@ import net.william278.husksync.database.Database; import net.william278.husksync.migrator.Migrator; import net.william278.husksync.user.CommandUser; import net.william278.husksync.util.LegacyConverter; +import net.william278.husksync.util.StatusLine; import net.william278.uniform.BaseCommand; import net.william278.uniform.CommandProvider; import net.william278.uniform.Permission; import net.william278.uniform.element.ArgumentElement; -import org.apache.commons.text.WordUtils; import org.jetbrains.annotations.NotNull; import java.time.OffsetDateTime; import java.util.Arrays; import java.util.List; import java.util.UUID; -import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; @@ -98,6 +98,7 @@ public class HuskSyncCommand extends PluginCommand { command.setDefaultExecutor((ctx) -> about(command, ctx)); command.addSubCommand("about", (sub) -> sub.setDefaultExecutor((ctx) -> about(command, ctx))); command.addSubCommand("status", needsOp("status"), status()); + command.addSubCommand("dump", needsOp("dump"), dump()); command.addSubCommand("reload", needsOp("reload"), reload()); command.addSubCommand("update", needsOp("update"), update()); command.addSubCommand("forceupgrade", forceUpgrade()); @@ -120,6 +121,26 @@ public class HuskSyncCommand extends PluginCommand { }); } + @NotNull + private CommandProvider dump() { + return (sub) -> { + sub.setDefaultExecutor((ctx) -> { + final CommandUser user = user(sub, ctx); + plugin.getLocales().getLocale("system_dump_confirm").ifPresent(user::sendMessage); + }); + sub.addSubCommand("confirm", (con) -> con.setDefaultExecutor((ctx) -> { + final CommandUser user = user(sub, ctx); + plugin.getLocales().getLocale("system_dump_started").ifPresent(user::sendMessage); + plugin.runAsync(() -> { + final String url = plugin.createDump(user); + plugin.getLocales().getLocale("system_dump_ready").ifPresent(user::sendMessage); + user.sendMessage(Component.text(url).clickEvent(ClickEvent.openUrl(url)) + .decorate(TextDecoration.UNDERLINED).color(NamedTextColor.GRAY)); + }); + })); + }; + } + @NotNull private CommandProvider reload() { return (sub) -> sub.setDefaultExecutor((ctx) -> { @@ -234,86 +255,4 @@ public class HuskSyncCommand extends PluginCommand { }); } - private enum StatusLine { - PLUGIN_VERSION(plugin -> Component.text("v" + plugin.getPluginVersion().toStringWithoutMetadata()) - .appendSpace().append(plugin.getPluginVersion().getMetadata().isBlank() ? Component.empty() - : Component.text("(build " + plugin.getPluginVersion().getMetadata() + ")"))), - SERVER_VERSION(plugin -> Component.text(plugin.getServerVersion())), - LANGUAGE(plugin -> Component.text(plugin.getSettings().getLanguage())), - MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())), - JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))), - JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))), - SERVER_NAME(plugin -> Component.text(plugin.getServerName())), - CLUSTER_ID(plugin -> Component.text(plugin.getSettings().getClusterId().isBlank() ? "None" : plugin.getSettings().getClusterId())), - SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully( - plugin.getSettings().getSynchronization().getMode().toString() - ))), - DELAY_LATENCY(plugin -> Component.text( - plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds() + "ms" - )), - DATABASE_TYPE(plugin -> - Component.text(plugin.getSettings().getDatabase().getType().getDisplayName() + - (plugin.getSettings().getDatabase().getType() == Database.Type.MONGO ? - (plugin.getSettings().getDatabase().getMongoSettings().isUsingAtlas() ? " Atlas" : "") : "")) - ), - IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getDatabase().getCredentials().getHost())), - USING_REDIS_SENTINEL(plugin -> getBoolean( - !plugin.getSettings().getRedis().getSentinel().getMaster().isBlank() - )), - USING_REDIS_PASSWORD(plugin -> getBoolean( - !plugin.getSettings().getRedis().getCredentials().getPassword().isBlank() - )), - REDIS_USING_SSL(plugin -> getBoolean( - plugin.getSettings().getRedis().getCredentials().isUseSsl() - )), - IS_REDIS_LOCAL(plugin -> getLocalhostBoolean( - plugin.getSettings().getRedis().getCredentials().getHost() - )), - DATA_TYPES(plugin -> Component.join( - JoinConfiguration.commas(true), - plugin.getRegisteredDataTypes().stream().map(i -> Component.textOfChildren(Component.text(i.toString()) - .appendSpace().append(Component.text(i.isEnabled() ? '✔' : '❌'))) - .color(i.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED) - .hoverEvent(HoverEvent.showText( - Component.text(i.isEnabled() ? "Enabled" : "Disabled") - .append(Component.newline()) - .append(Component.text("Dependencies: %s".formatted(i.getDependencies() - .isEmpty() ? "(None)" : i.getDependencies().stream() - .map(d -> "%s (%s)".formatted( - d.getKey().value(), d.isRequired() ? "Required" : "Optional" - )).collect(Collectors.joining(", "))) - ).color(NamedTextColor.GRAY)) - ))).toList() - )); - - private final Function supplier; - - StatusLine(@NotNull Function supplier) { - this.supplier = supplier; - } - - @NotNull - private Component get(@NotNull HuskSync plugin) { - return Component - .text("•").appendSpace() - .append(Component.text( - WordUtils.capitalizeFully(name().replaceAll("_", " ")), - TextColor.color(0x848484) - )) - .append(Component.text(':')).append(Component.space().color(NamedTextColor.WHITE)) - .append(supplier.apply(plugin)); - } - - @NotNull - private static Component getBoolean(boolean value) { - return Component.text(value ? "Yes" : "No", value ? NamedTextColor.GREEN : NamedTextColor.RED); - } - - @NotNull - private static Component getLocalhostBoolean(@NotNull String value) { - return getBoolean(value.equals("127.0.0.1") || value.equals("0.0.0.0") - || value.equals("localhost") || value.equals("::1")); - } - } - } diff --git a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java index 08f25bfd..dd1a7eef 100644 --- a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java +++ b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java @@ -26,7 +26,7 @@ import net.william278.husksync.redis.RedisKeyType; import net.william278.husksync.redis.RedisManager; import net.william278.husksync.user.CommandUser; import net.william278.husksync.user.User; -import net.william278.husksync.util.DataDumper; +import net.william278.husksync.util.UserDataDumper; import net.william278.husksync.util.DataSnapshotList; import net.william278.husksync.util.DataSnapshotOverview; import net.william278.uniform.BaseCommand; @@ -185,7 +185,7 @@ public class UserDataCommand extends PluginCommand { // Dump the data final DataSnapshot.Packed userData = data.get(); - final DataDumper dumper = DataDumper.create(userData, user, plugin); + final UserDataDumper dumper = UserDataDumper.create(userData, user, plugin); try { plugin.getLocales().getLocale("data_dumped", userData.getShortId(), user.getUsername(), (type == DumpType.WEB ? dumper.toWeb() : dumper.toFile())) diff --git a/common/src/main/java/net/william278/husksync/redis/RedisManager.java b/common/src/main/java/net/william278/husksync/redis/RedisManager.java index c3c1c1e9..d778db1d 100644 --- a/common/src/main/java/net/william278/husksync/redis/RedisManager.java +++ b/common/src/main/java/net/william278/husksync/redis/RedisManager.java @@ -412,6 +412,33 @@ public class RedisManager extends JedisPubSub { } } + @Blocking + public String getStatusDump() { + try (Jedis jedis = jedisPool.getResource()) { + return jedis.info(); + } + } + + @Blocking + public long getLatency() { + final long startTime = System.currentTimeMillis(); + try (Jedis jedis = jedisPool.getResource()) { + jedis.ping(); + return startTime - System.currentTimeMillis(); + } + } + + @Blocking + public String getVersion() { + final String info = getStatusDump(); + for (String line : info.split("\n")) { + if (line.startsWith("redis_version:")) { + return line.split(":")[1]; + } + } + return "unknown"; + } + @Blocking public void terminate() { enabled = false; diff --git a/common/src/main/java/net/william278/husksync/user/User.java b/common/src/main/java/net/william278/husksync/user/User.java index 19c4bbc1..c40db503 100644 --- a/common/src/main/java/net/william278/husksync/user/User.java +++ b/common/src/main/java/net/william278/husksync/user/User.java @@ -19,6 +19,9 @@ package net.william278.husksync.user; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -26,39 +29,18 @@ import java.util.UUID; /** * Represents a user who has their data synchronized by HuskSync */ +@Getter +@AllArgsConstructor +@EqualsAndHashCode(exclude = {"name"}) public class User { private final UUID uuid; + private final String name; - private final String username; - - public User(@NotNull UUID uuid, @NotNull String username) { - this.username = username; - this.uuid = uuid; - } - - /** - * Get the user's unique account ID - */ - @NotNull - public UUID getUuid() { - return uuid; - } - - /** - * Get the user's username - */ @NotNull + @Deprecated(since = "3.7.4") public String getUsername() { - return username; - } - - @Override - public boolean equals(Object object) { - if (object instanceof User other) { - return this.getUuid().equals(other.getUuid()); - } - return super.equals(object); + return name; } } diff --git a/common/src/main/java/net/william278/husksync/util/DumpProvider.java b/common/src/main/java/net/william278/husksync/util/DumpProvider.java new file mode 100644 index 00000000..681ae5a3 --- /dev/null +++ b/common/src/main/java/net/william278/husksync/util/DumpProvider.java @@ -0,0 +1,158 @@ +/* + * This file is part of HuskSync, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.husksync.util; + +import net.william278.husksync.HuskSync; +import net.william278.husksync.user.CommandUser; +import net.william278.husksync.user.OnlineUser; +import net.william278.toilet.DumpOptions; +import net.william278.toilet.Toilet; +import net.william278.toilet.dump.*; +import org.jetbrains.annotations.Blocking; +import org.jetbrains.annotations.NotNull; + +import static net.william278.toilet.DumpOptions.*; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public interface DumpProvider { + + @NotNull String BYTEBIN_URL = "https://bytebin.lucko.me"; + @NotNull String VIEWER_URL = "https://william278.net/dump"; + + @NotNull + Toilet getToilet(); + + @NotNull + @Blocking + default String createDump(@NotNull CommandUser u) { + return getToilet().dump(getPluginStatus(), u instanceof OnlineUser o + ? new DumpUser(o.getName(), o.getUuid()) : null, + getRedisInfo()).toString(); + } + + @NotNull + default DumpOptions getDumpOptions() { + return builder() + .bytebinUrl(BYTEBIN_URL) + .viewerUrl(VIEWER_URL) + .projectMeta(ProjectMeta.builder() + .id("husksync") + .name("HuskSync") + .version(getPlugin().getPluginVersion().toString()) + .md5("unknown") + .author("William278") + .sourceCode("https://github.com/WiIIiam278/HuskSync") + .website("https://william278.net/project/husksync") + .support("https://discord.gg/tVYhJfyDWG") + .build()) + .fileInclusionRules(List.of( + FileInclusionRule.configFile("config.yml", "Config File"), + FileInclusionRule.configFile(getMessagesFile(), "Locales File") + )) + .compatibilityRules(List.of( + getCompatibilityWarning("CombatLogX", "Combat loggers require additional" + + "configuration for use with HuskSync. Check https://william278.net/docs/husksync/event-priorities"), + getIncompatibleNotice("UltimateAutoRestart", "Restart plugins are not" + + "compatible with HuskSync as they affect the way the server shuts down, preventing data" + + "from saving correctly during a restart. Check https://william278.net/docs/husksync/troubleshooting") + )) + .build(); + } + + @NotNull + @Blocking + private PluginStatus getPluginStatus() { + return PluginStatus.builder() + .blocks(List.of(getSystemStatus(), getRegisteredDataTypes())) + .build(); + } + + @NotNull + @Blocking + private PluginStatus.MapStatusBlock getSystemStatus() { + return new PluginStatus.MapStatusBlock( + Map.ofEntries( + Map.entry("Language", StatusLine.LANGUAGE.getValue(getPlugin())), + Map.entry("Database Type", StatusLine.DATABASE_TYPE.getValue(getPlugin())), + Map.entry("Database Local", StatusLine.IS_DATABASE_LOCAL.getValue(getPlugin())), + Map.entry("Locked User Handler", StatusLine.LOCKED_USER_HANDLER.getValue(getPlugin())), + Map.entry("Server Name", StatusLine.SERVER_NAME.getValue(getPlugin())), + Map.entry("Redis Version", StatusLine.REDIS_VERSION.getValue(getPlugin())), + Map.entry("Redis Latency", StatusLine.REDIS_LATENCY.getValue(getPlugin())), + Map.entry("Redis Sentinel", StatusLine.USING_REDIS_SENTINEL.getValue(getPlugin())), + Map.entry("Redis Password", StatusLine.USING_REDIS_PASSWORD.getValue(getPlugin())), + Map.entry("Redis SSL", StatusLine.REDIS_USING_SSL.getValue(getPlugin())), + Map.entry("Redis Local", StatusLine.IS_REDIS_LOCAL.getValue(getPlugin())) + ), + "Plugin Status", "fa6-solid:wrench" + ); + } + + @NotNull + @Blocking + private PluginStatus.MapStatusBlock getRegisteredDataTypes() { + return new PluginStatus.MapStatusBlock( + getPlugin().getRegisteredDataTypes().stream().collect(Collectors.toMap( + i -> i.getKey().asMinimalString(), + i -> i.isEnabled() ? "✅ Enabled" : "❌ Disabled", + (a, b) -> a) + ), + "Registered Data Types", "carbon:data-blob" + ); + } + + @NotNull + @Blocking + private ExtraFile getRedisInfo() { + return new ExtraFile( + "redis-status", "Redis Status", "devicon-plain:redis", + getPlugin().getRedisManager().getStatusDump(), + "markdown" + ); + } + + @NotNull + @SuppressWarnings("SameParameterValue") + private CompatibilityRule getCompatibilityWarning(@NotNull String plugin, @NotNull String description) { + return CompatibilityRule.builder() + .labelToApply(new PluginInfo.Label("Warning", "#fcba03", description)) + .resourceName(plugin).build(); + } + + @NotNull + @SuppressWarnings("SameParameterValue") + private CompatibilityRule getIncompatibleNotice(@NotNull String plugin, @NotNull String description) { + return CompatibilityRule.builder() + .labelToApply(new PluginInfo.Label("Incompatible", "#ff3300", description)) + .resourceName(plugin).build(); + } + + @NotNull + private String getMessagesFile() { + return "messages-%s.yml".formatted(getPlugin().getSettings().getLanguage()); + } + + @NotNull + HuskSync getPlugin(); + +} \ No newline at end of file diff --git a/common/src/main/java/net/william278/husksync/util/StatusLine.java b/common/src/main/java/net/william278/husksync/util/StatusLine.java new file mode 100644 index 00000000..ad9c8808 --- /dev/null +++ b/common/src/main/java/net/william278/husksync/util/StatusLine.java @@ -0,0 +1,124 @@ +/* + * This file is part of HuskSync, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.husksync.util; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.william278.husksync.HuskSync; +import net.william278.husksync.database.Database; +import org.apache.commons.text.WordUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum StatusLine { + PLUGIN_VERSION(plugin -> Component.text("v" + plugin.getPluginVersion().toStringWithoutMetadata()) + .appendSpace().append(plugin.getPluginVersion().getMetadata().isBlank() ? Component.empty() + : Component.text("(build " + plugin.getPluginVersion().getMetadata() + ")"))), + SERVER_VERSION(plugin -> Component.text(plugin.getServerVersion())), + LANGUAGE(plugin -> Component.text(plugin.getSettings().getLanguage())), + MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())), + JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))), + JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))), + SERVER_NAME(plugin -> Component.text(plugin.getServerName())), + CLUSTER_ID(plugin -> Component.text(plugin.getSettings().getClusterId().isBlank() ? "None" : plugin.getSettings().getClusterId())), + SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully( + plugin.getSettings().getSynchronization().getMode().toString() + ))), + DELAY_LATENCY(plugin -> Component.text( + plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds() + "ms" + )), + DATABASE_TYPE(plugin -> + Component.text(plugin.getSettings().getDatabase().getType().getDisplayName() + + (plugin.getSettings().getDatabase().getType() == Database.Type.MONGO ? + (plugin.getSettings().getDatabase().getMongoSettings().isUsingAtlas() ? " Atlas" : "") : "")) + ), + IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getDatabase().getCredentials().getHost())), + REDIS_VERSION(plugin -> Component.text(plugin.getRedisManager().getVersion())), + USING_REDIS_SENTINEL(plugin -> getBoolean( + !plugin.getSettings().getRedis().getSentinel().getMaster().isBlank() + )), + USING_REDIS_PASSWORD(plugin -> getBoolean( + !plugin.getSettings().getRedis().getCredentials().getPassword().isBlank() + )), + REDIS_USING_SSL(plugin -> getBoolean( + plugin.getSettings().getRedis().getCredentials().isUseSsl() + )), + REDIS_LATENCY(plugin -> Component.text("%sms".formatted(plugin.getRedisManager().getLatency()))), + IS_REDIS_LOCAL(plugin -> getLocalhostBoolean( + plugin.getSettings().getRedis().getCredentials().getHost() + )), + LOCKED_USER_HANDLER(plugin -> Component.text(plugin.getLockedHandler().getClass().getSimpleName())), + DATA_TYPES(plugin -> Component.join( + JoinConfiguration.commas(true), + plugin.getRegisteredDataTypes().stream().map(i -> Component.textOfChildren(Component.text(i.toString()) + .appendSpace().append(Component.text(i.isEnabled() ? '✔' : '❌'))) + .color(i.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED) + .hoverEvent(HoverEvent.showText( + Component.text(i.isEnabled() ? "Enabled" : "Disabled") + .append(Component.newline()) + .append(Component.text("Dependencies: %s".formatted(i.getDependencies() + .isEmpty() ? "(None)" : i.getDependencies().stream() + .map(d -> "%s (%s)".formatted( + d.getKey().value(), d.isRequired() ? "Required" : "Optional" + )).collect(Collectors.joining(", "))) + ).color(NamedTextColor.GRAY)) + ))).toList() + )); + + private final Function supplier; + + StatusLine(@NotNull Function supplier) { + this.supplier = supplier; + } + + @NotNull + public Component get(@NotNull HuskSync plugin) { + return Component + .text("•").appendSpace() + .append(Component.text( + WordUtils.capitalizeFully(name().replaceAll("_", " ")), + TextColor.color(0x848484) + )) + .append(Component.text(':')).append(Component.space().color(NamedTextColor.WHITE)) + .append(supplier.apply(plugin)); + } + + @NotNull + public String getValue(@NotNull HuskSync plugin) { + return PlainTextComponentSerializer.plainText().serialize(supplier.apply(plugin)); + } + + @NotNull + private static Component getBoolean(boolean value) { + return Component.text(value ? "Yes" : "No", value ? NamedTextColor.GREEN : NamedTextColor.RED); + } + + @NotNull + private static Component getLocalhostBoolean(@NotNull String value) { + return getBoolean(value.equals("127.0.0.1") || value.equals("0.0.0.0") + || value.equals("localhost") || value.equals("::1")); + } +} diff --git a/common/src/main/java/net/william278/husksync/util/DataDumper.java b/common/src/main/java/net/william278/husksync/util/UserDataDumper.java similarity index 87% rename from common/src/main/java/net/william278/husksync/util/DataDumper.java rename to common/src/main/java/net/william278/husksync/util/UserDataDumper.java index e2917d5f..4e1c2686 100644 --- a/common/src/main/java/net/william278/husksync/util/DataDumper.java +++ b/common/src/main/java/net/william278/husksync/util/UserDataDumper.java @@ -42,7 +42,7 @@ import java.util.logging.Level; /** * Utility class for dumping {@link DataSnapshot}s to a file or as a paste on the web */ -public class DataDumper { +public class UserDataDumper { private static final String LOGS_SITE_ENDPOINT = "https://api.mclo.gs/1/log"; @@ -50,23 +50,23 @@ public class DataDumper { private final DataSnapshot.Packed snapshot; private final User user; - private DataDumper(@NotNull DataSnapshot.Packed snapshot, @NotNull User user, @NotNull HuskSync implementor) { + private UserDataDumper(@NotNull DataSnapshot.Packed snapshot, @NotNull User user, @NotNull HuskSync implementor) { this.snapshot = snapshot; this.user = user; this.plugin = implementor; } /** - * Create a {@link DataDumper} of the given {@link DataSnapshot} + * Create a {@link UserDataDumper} of the given {@link DataSnapshot} * * @param dataSnapshot The {@link DataSnapshot} to dump * @param user The {@link User} whose data is being dumped * @param plugin The implementing {@link HuskSync} plugin - * @return A {@link DataDumper} for the given {@link DataSnapshot} + * @return A {@link UserDataDumper} for the given {@link DataSnapshot} */ - public static DataDumper create(@NotNull DataSnapshot.Packed dataSnapshot, - @NotNull User user, @NotNull HuskSync plugin) { - return new DataDumper(dataSnapshot, user, plugin); + public static UserDataDumper create(@NotNull DataSnapshot.Packed dataSnapshot, + @NotNull User user, @NotNull HuskSync plugin) { + return new UserDataDumper(dataSnapshot, user, plugin); } /** @@ -179,11 +179,11 @@ public class DataDumper { @NotNull private String getFileName() { return new StringJoiner("_") - .add(user.getUsername()) - .add(snapshot.getTimestamp().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"))) - .add(snapshot.getSaveCause().name().toLowerCase(Locale.ENGLISH)) - .add(snapshot.getShortId()) - + ".json"; + .add(user.getUsername()) + .add(snapshot.getTimestamp().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"))) + .add(snapshot.getSaveCause().name().toLowerCase(Locale.ENGLISH)) + .add(snapshot.getShortId()) + + ".json"; } } diff --git a/common/src/main/resources/locales/bg-bg.yml b/common/src/main/resources/locales/bg-bg.yml index cbf0fb2b..eeda8a2b 100644 --- a/common/src/main/resources/locales/bg-bg.yml +++ b/common/src/main/resources/locales/bg-bg.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Презаредихме конфигурацията и файловете със съобщения.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Грешка:](#ff3300) [Не можахме да открием играч с това име.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/de-de.yml b/common/src/main/resources/locales/de-de.yml index 0c158481..acda15b4 100644 --- a/common/src/main/resources/locales/de-de.yml +++ b/common/src/main/resources/locales/de-de.yml @@ -51,6 +51,9 @@ locales: up_to_date: '[HuskSync](#00fb9a bold) [| Du verwendest die neuste Version von HuskSync (v%1%).](#00fb9a)' update_available: '[HuskSync](#ff7e5e bold) [| Eine neue Version von HuskSync ist verfügbar: v%1% (Aktuelle Version: v%2%).](#ff7e5e)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Fehler:](#ff3300) [Es konnte kein Spieler mit diesem Namen gefunden werden.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml index f0cca4be..8b5534a4 100644 --- a/common/src/main/resources/locales/en-gb.yml +++ b/common/src/main/resources/locales/en-gb.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Reloaded config and message files.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [Could not find a player by that name.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/es-es.yml b/common/src/main/resources/locales/es-es.yml index e80b16f7..104462c7 100644 --- a/common/src/main/resources/locales/es-es.yml +++ b/common/src/main/resources/locales/es-es.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Recargada la configuración y los archivos de lenguaje.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Sintanxis incorrecta. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [No se ha podido encontrar un jugador con ese nombre.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/fr-fr.yml b/common/src/main/resources/locales/fr-fr.yml index bd56a8c2..efb9c4b2 100644 --- a/common/src/main/resources/locales/fr-fr.yml +++ b/common/src/main/resources/locales/fr-fr.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| Une nouvelle version de HuskSync est disponible:v%1% (version actuelle: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Config et messages rechargés.](#00fb9a)\n[⚠Assurez-vous que les fichiers de configuration sont à jour sur tous les serveurs!](#00fb9a)\n[Un redémarrage est nécessairepour que les modifications de configuration prennent effet.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| Rapport d''état du système:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Erreur:](#ff3300) [Syntaxe incorrecte. Utilisation:](#ff7e5e) [%1%](#ff7e5eitalic show_text=&#ff7e5e&Cliquez pour suggérer suggest_command=%1%)' error_invalid_player: '[Erreur:](#ff3300) [Impossible de trouver un joueur avec ce nom.](#ff7e5e)' error_invalid_data: '[Erreur:](#ff3300) [Impossible de déballer les données de l''instantané car elles sont invalides ou corrompues.](#ff7e5e) [(Détails…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/id-id.yml b/common/src/main/resources/locales/id-id.yml index fae1e11e..7f54550d 100644 --- a/common/src/main/resources/locales/id-id.yml +++ b/common/src/main/resources/locales/id-id.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| Versi baru HuskSync tersedia: v%1% (menjalankan: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Memuat ulang file konfigurasi dan pesan.](#00fb9a)\n[⚠ Pastikan file konfigurasi sudah diperbarui di semua server!](#00fb9a)\n[Diperlukan pengaktifan ulang agar perubahan konfigurasi dapat diterapkan.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| Laporan status sistem:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Kesalahan:](#ff3300) [Sintaks salah. Penggunaan:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Klik untuk menyarankan suggest_command=%1%)' error_invalid_player: '[Kesalahan:](#ff3300) [Tidak dapat menemukan pemain dengan nama tersebut.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/it-it.yml b/common/src/main/resources/locales/it-it.yml index 7d7afbf0..484b5e89 100644 --- a/common/src/main/resources/locales/it-it.yml +++ b/common/src/main/resources/locales/it-it.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| Disponibile una nuova versione: v%1% (running: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Configurazione e messaggi ricaricati.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Errore:](#ff3300) [Impossibile trovare un giocatore con questo nome.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/ja-jp.yml b/common/src/main/resources/locales/ja-jp.yml index 5c97bf5f..a705d7e9 100644 --- a/common/src/main/resources/locales/ja-jp.yml +++ b/common/src/main/resources/locales/ja-jp.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| HuskSyncの最新バージョンが更新されています: v%1% (実行中: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| 設定ファイルとメッセージファイルを再読み込みしました。](#00fb9a)\n[⚠ すべてのサーバーで設定ファイルが最新であることを確認してください!](#00fb9a)\n[設定の変更を有効にするには再起動が必要です。](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [構文が正しくありません。使用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&クリックでサジェスト suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [そのプレイヤーは見つかりませんでした](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/ko-kr.yml b/common/src/main/resources/locales/ko-kr.yml index fd2cfdc3..5ca6a7b7 100644 --- a/common/src/main/resources/locales/ko-kr.yml +++ b/common/src/main/resources/locales/ko-kr.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| 새로운 버전의 HuskSync가 존재합니다: v%1% (현재 버전: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| 콘피그와 메시지 파일을 다시 불러왔습니다.](#00fb9a)\n[⚠ 모든 서버의 컨피그 파일을 변경하였는지 확인하세요!](#00fb9a)\n[몇몇 설정은 재시작 후에 적용됩니다.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[오류:](#ff3300) [잘못된 사용법. 사용법:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&클릭하여 입력할 수 있습니다. suggest_command=%1%)' error_invalid_player: '[오류:](#ff3300) [해당 이름의 사용자를 찾을 수 없습니다.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/nl-nl.yml b/common/src/main/resources/locales/nl-nl.yml index de59dcf6..07b297a5 100644 --- a/common/src/main/resources/locales/nl-nl.yml +++ b/common/src/main/resources/locales/nl-nl.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| Er is een nieuwe versie van HuskSync beschikbaar: v%1% (huidige versie: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Configuratie- en berichtbestanden opnieuw geladen.](#00fb9a)\n[⚠ Controleer of de configuratiebestanden up-to-date zijn op alle servers!](#00fb9a)\n[Een herstart is nodig voor de configuratiewijzigingen van kracht te laten worden.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Onjuiste syntaxis. Gebruik:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [Kan geen speler met die naam vinden.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/pt-br.yml b/common/src/main/resources/locales/pt-br.yml index 2b2e315c..35e68a0b 100644 --- a/common/src/main/resources/locales/pt-br.yml +++ b/common/src/main/resources/locales/pt-br.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Arquivos de configuração e mensagens recarregados.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Error:](#ff3300) [Sintaxe incorreta. Utilize:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Error:](#ff3300) [Não foi possível encontrar um jogador com esse nome.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/ru-ru.yml b/common/src/main/resources/locales/ru-ru.yml index 6afa61a1..f2a83bba 100644 --- a/common/src/main/resources/locales/ru-ru.yml +++ b/common/src/main/resources/locales/ru-ru.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| Доступна новая версия HuskSync: v%1% (текущая: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Конфигурация и файлы локализации перезагружены.](#00fb9a)\n[⚠ Убедитесь, что файлы конфигурации обновлены на всех серверах!](#00fb9a)\n[Необходима перезагрузка для вступления изменений конфигурации в силу.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Ошибка:](#ff3300) [Неправильный синтаксис. Используйте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Ошибка:](#ff3300) [Не удалось найти игрока с данным именем.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/tr-tr.yml b/common/src/main/resources/locales/tr-tr.yml index 6f2bf872..004f0442 100644 --- a/common/src/main/resources/locales/tr-tr.yml +++ b/common/src/main/resources/locales/tr-tr.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| HuskSync\''in yeni bir sürümü mevcut: v%1% (kullanılan sürüm: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Yapılandırma ve mesaj dosyaları yeniden yüklendi.](#00fb9a)\n[⚠ Lütfen yapılandırma dosyalarının tüm sunucularda güncel olduğundan emin olun!](#00fb9a)\n[Yapılandırma değişikliklerinin etkili olabilmesi için bir yeniden başlatma gereklidir.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| Sistem durumu raporu:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Hata:](#ff3300) [Yanlış sözdizimi. Kullanım:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Öneri için tıklayın Suggest_command=%1%)' error_invalid_player: '[Hata:](#ff3300) [Bu isimde bir oyuncu bulunamadı.](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/uk-ua.yml b/common/src/main/resources/locales/uk-ua.yml index bf847cc7..ed1081a8 100644 --- a/common/src/main/resources/locales/uk-ua.yml +++ b/common/src/main/resources/locales/uk-ua.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| Перезавантажено конфіґ та файли повідомлень.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[Помилка:](#ff3300) [Неправильний синтакс. Використання:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' error_invalid_player: '[Помилка:](#ff3300) [Гравця не знайдено](#ff7e5e)' error_invalid_data: '[Error:](#ff3300) [Failed to unpack user data as the snapshot is invalid or corrupt.](#ff7e5e) [(Details…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/zh-cn.yml b/common/src/main/resources/locales/zh-cn.yml index 88c950a9..78049904 100644 --- a/common/src/main/resources/locales/zh-cn.yml +++ b/common/src/main/resources/locales/zh-cn.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| 检测到HuskSync有新版本可以更新了:v%1%(当前版本:v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| 重新加载配置和消息文件完成.](#00fb9a)\n[⚠ 确保在所有服务器上更新配置文件!](#00fb9a)\n[需要重新启动才能使配置更改生效.](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| 系统状态报告:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[错误:](#ff3300) [语法错误.用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)' error_invalid_player: '[错误:](#ff3300) [找不到这个名称的玩家.](#ff7e5e)' error_invalid_data: '[错误:](#ff3300) [无法解压缩快照数据, 因为它无效或已损坏.](#ff7e5e) [(详情…)](gray show_text=&7⚠ %1%)' diff --git a/common/src/main/resources/locales/zh-tw.yml b/common/src/main/resources/locales/zh-tw.yml index 7d793ae6..504dba82 100644 --- a/common/src/main/resources/locales/zh-tw.yml +++ b/common/src/main/resources/locales/zh-tw.yml @@ -51,6 +51,9 @@ locales: update_available: '[HuskSync](#ff7e5e bold) [| 發現可用的新版本: v%1% (running: v%2%).](#ff7e5e)' reload_complete: '[HuskSync](#00fb9a bold) [| 配置和語言文件已重新加載。](#00fb9a)\n[⚠ 確保所有伺服器上的配置文件都是最新的!](#00fb9a)\n[重啟後配置變更才會生效。](#00fb9a italic)' system_status_header: '[HuskSync](#00fb9a bold) [| 系統狀態報告:](#00fb9a)' + system_dump_confirm: '[HuskSync](#00fb9a bold) [| Prepare a system dump? This will include:](#00fb9a)\n[• Your latest server logs and HuskSync config files](gray)\n[• Current plugin system status information](gray)\n[• Information about your Java & Minecraft server environment](gray)\n[• A list of other currently installed plugins](gray)\n[To confirm, use:](#00fb9a) [/husksync dump confirm](#00fb9a italic show_text=&7Click to prepare dump run_command=/husksync dump confirm)' + system_dump_started: '[HuskSync](#00fb9a bold) [| Preparing system status dump, please wait…](#00fb9a)' + system_dump_ready: '[HuskSync](#00fb9a bold) [| System status dump prepared! Click to view:](#00fb9a)' error_invalid_syntax: '[錯誤:](#ff3300) [語法不正確,用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&點擊建議 suggest_command=%1%)' error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家](#ff7e5e)' error_invalid_data: '[錯誤:](#ff3300) [無法解壓使用者資料,因為快照無效或已損壞。](#ff7e5e) [(詳細資訊…)](gray show_text=&7⚠ %1%)' diff --git a/fabric/build.gradle b/fabric/build.gradle index 61c9365a..83e54deb 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -15,6 +15,7 @@ dependencies { modImplementation include("me.lucko:fabric-permissions-api:${fabric_permissions_api_version}") modImplementation include("eu.pb4:sgui:${fabric_sgui_version}") modImplementation include("net.william278.uniform:uniform-fabric:1.3.1+${project.name}") + modImplementation include("net.william278.toilet:toilet-fabric:1.0.12+${project.name}") modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_api_version}" implementation include('org.apache.commons:commons-pool2:2.12.1') diff --git a/fabric/src/main/java/net/william278/husksync/FabricHuskSync.java b/fabric/src/main/java/net/william278/husksync/FabricHuskSync.java index bbc0670c..ea973633 100644 --- a/fabric/src/main/java/net/william278/husksync/FabricHuskSync.java +++ b/fabric/src/main/java/net/william278/husksync/FabricHuskSync.java @@ -56,6 +56,7 @@ import net.william278.husksync.event.ModLoadedCallback; import net.william278.husksync.hook.PlanHook; import net.william278.husksync.listener.EventListener; import net.william278.husksync.listener.FabricEventListener; +import net.william278.husksync.listener.LockedHandler; import net.william278.husksync.migrator.Migrator; import net.william278.husksync.redis.RedisManager; import net.william278.husksync.sync.DataSyncer; @@ -64,6 +65,8 @@ import net.william278.husksync.user.FabricUser; import net.william278.husksync.user.OnlineUser; import net.william278.husksync.util.FabricTask; import net.william278.husksync.util.LegacyConverter; +import net.william278.toilet.Toilet; +import net.william278.toilet.fabric.FabricToilet; import net.william278.uniform.Uniform; import net.william278.uniform.fabric.FabricUniform; import org.jetbrains.annotations.NotNull; @@ -118,9 +121,10 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync, //#else //$$ private FabricServerAudiences audiences; //#endif + private Toilet toilet; private Database database; private RedisManager redisManager; - private EventListener eventListener; + private FabricEventListener eventListener; private DataAdapter dataAdapter; @Setter private DataSyncer dataSyncer; @@ -168,6 +172,7 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync, //#else //$$ this.audiences = FabricServerAudiences.of(minecraftServer); //#endif + this.toilet = FabricToilet.create(getDumpOptions(), minecraftServer); // Check compatibility checkCompatibility(); @@ -411,6 +416,12 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync, return Optional.empty(); } + @Override + @NotNull + public LockedHandler getLockedHandler() { + return eventListener; + } + @Override @NotNull public FabricHuskSync getPlugin() {