Merge remote-tracking branch 'origin/master'

feat/data-edit-commands
William 2 years ago
commit 2ed7705903

@ -1,12 +1,15 @@
# [![HuskSync Banner](images/banner-graphic.png)](https://github.com/WiIIiam278/HuskSync) # [![HuskSync Banner](images/banner-graphic.png)](https://github.com/WiIIiam278/HuskSync)
![Github Actions](https://github.com/WiIIiam278/HuskSync/workflows/Java%20CI/badge.svg) [![GitHub CI](https://img.shields.io/github/workflow/status/WiIIiam278/HuskSync/Java%20CI?logo=github)](https://github.com/WiIIiam278/HuskSync/actions/workflows/java_ci.yml)
[![Discord](https://img.shields.io/discord/818135932103557162?color=7289da&logo=discord)](https://discord.gg/tVYhJfyDWG) [![JitPack API](https://img.shields.io/jitpack/version/net.william278/HuskSync?color=%2300fb9a&label=api&logo=gradle)](https://jitpack.io/#net.william278/HuskSync)
[![Support Discord](https://img.shields.io/discord/818135932103557162.svg?label=&logo=discord&logoColor=fff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/tVYhJfyDWG)
[Documentation, Guides & API](https://william278.net/docs/husksync/Home) · [Resource Page](https://www.spigotmc.org/resources/husksync.97144/) · [Bug Reports](https://github.com/WiIIiam278/HuskSync/issues) [Documentation, Guides & API](https://william278.net/docs/husksync) · [Resource Page](https://www.spigotmc.org/resources/husksync.97144/) · [Bug Reports](https://github.com/WiIIiam278/HuskSync/issues)
**HuskSync** is a modern, cross-server player data synchronisation system that enables the comprehensive synchronisation of your user's data across multiple proxied servers. It does this by making use of Redis and MySQL to optimally cache data while players change servers. **HuskSync** is a modern, cross-server player data synchronisation system that enables the comprehensive synchronisation of your user's data across multiple proxied servers. It does this by making use of Redis and MySQL to optimally cache data while players change servers.
## Features ## Features
![Data snapshot viewer](images/data-snapshot-viewer.png)
- Synchronise inventories, ender chests, advancements, statistics, experience points, health, max health, hunger, saturation, potion effects, persistent data container tags, game mode, location and more across multiple proxied servers. - Synchronise inventories, ender chests, advancements, statistics, experience points, health, max health, hunger, saturation, potion effects, persistent data container tags, game mode, location and more across multiple proxied servers.
- Create and manage "snapshot" backups of user data and roll back users to previous states on-the-fly. (`/userdata`) - Create and manage "snapshot" backups of user data and roll back users to previous states on-the-fly. (`/userdata`)
- Preview, list, delete, restore & pin user data snapshots in-game with an intuitive menu. - Preview, list, delete, restore & pin user data snapshots in-game with an intuitive menu.
@ -23,7 +26,7 @@
1. Place the plugin jar file in the `/plugins/` directory of each Spigot server. You do not need to install HuskSync as a proxy plugin. 1. Place the plugin jar file in the `/plugins/` directory of each Spigot server. You do not need to install HuskSync as a proxy plugin.
2. Start, then stop every server to let HuskSync generate the config file. 2. Start, then stop every server to let HuskSync generate the config file.
3. Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`) and fill in both the MySQL and Redis database credentials. 3. Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`) and fill in both the MySQL and Redis database credentials.
4. Start every server again and synchronistaion will begin. 4. Start every server again and synchronization will begin.
## Building ## Building
To build HuskSync, simply run the following in the root of the repository: To build HuskSync, simply run the following in the root of the repository:
@ -52,7 +55,7 @@ This plugin uses bStats to provide me with metrics about its usage:
You can turn metric collection off by navigating to `~/plugins/bStats/config.yml` and editing the config to disable plugin metrics. You can turn metric collection off by navigating to `~/plugins/bStats/config.yml` and editing the config to disable plugin metrics.
## Links ## Links
- [Documentation, Guides & API](https://william278.net/docs/husksync/Home) - [Documentation, Guides & API](https://william278.net/docs/husksync)
- [Resource Page](https://www.spigotmc.org/resources/husksync.97144/) - [Resource Page](https://www.spigotmc.org/resources/husksync.97144/)
- [Bug Reports](https://github.com/WiIIiam278/HuskSync/issues) - [Bug Reports](https://github.com/WiIIiam278/HuskSync/issues)
- [Discord Support](https://discord.gg/tVYhJfyDWG) (Proof of purchase required) - [Discord Support](https://discord.gg/tVYhJfyDWG) (Proof of purchase required)

@ -6,7 +6,7 @@ plugins {
} }
group 'net.william278' group 'net.william278'
version "$ext.plugin_version+${versionMetadata()}" version "$ext.plugin_version-${versionMetadata()}"
ext { ext {
set 'version', version.toString() set 'version', version.toString()
@ -102,5 +102,5 @@ def versionMetadata() {
if (grgit == null) { if (grgit == null) {
return System.getenv("GITHUB_RUN_NUMBER") ? 'build.' + System.getenv("GITHUB_RUN_NUMBER") : 'unknown' return System.getenv("GITHUB_RUN_NUMBER") ? 'build.' + System.getenv("GITHUB_RUN_NUMBER") : 'unknown'
} }
return 'rev.' + grgit.head().abbreviatedId + (grgit.status().clean ? '' : '-indev') return grgit.head().abbreviatedId + (grgit.status().clean ? '' : '-indev')
} }

@ -10,6 +10,7 @@ dependencies {
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT' compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'dev.dejvokep:boosted-yaml:1.3' compileOnly 'dev.dejvokep:boosted-yaml:1.3'
compileOnly 'com.zaxxer:HikariCP:5.0.1' compileOnly 'com.zaxxer:HikariCP:5.0.1'
compileOnly 'net.william278:DesertWell:1.0'
testImplementation 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT' testImplementation 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
} }
@ -26,6 +27,7 @@ shadowJar {
relocate 'org.intellij', 'net.william278.husksync.libraries' relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries' relocate 'com.zaxxer', 'net.william278.husksync.libraries'
relocate 'dev.dejvokep', 'net.william278.husksync.libraries' relocate 'dev.dejvokep', 'net.william278.husksync.libraries'
relocate 'net.william278.desertwell', 'net.william278.huskhomes.libraries.desertwell'
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore' relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby' relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'

@ -6,6 +6,7 @@ import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import net.william278.desertwell.Version;
import net.william278.husksync.command.BukkitCommand; import net.william278.husksync.command.BukkitCommand;
import net.william278.husksync.command.BukkitCommandType; import net.william278.husksync.command.BukkitCommandType;
import net.william278.husksync.command.Permission; import net.william278.husksync.command.Permission;
@ -28,7 +29,10 @@ import net.william278.husksync.migrator.MpdbMigrator;
import net.william278.husksync.player.BukkitPlayer; import net.william278.husksync.player.BukkitPlayer;
import net.william278.husksync.player.OnlineUser; import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.redis.RedisManager; import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.util.*; import net.william278.husksync.util.BukkitLogger;
import net.william278.husksync.util.BukkitResourceReader;
import net.william278.husksync.util.Logger;
import net.william278.husksync.util.ResourceReader;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
@ -181,7 +185,10 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
// Check for updates // Check for updates
if (settings.getBooleanValue(Settings.ConfigOption.CHECK_FOR_UPDATES)) { if (settings.getBooleanValue(Settings.ConfigOption.CHECK_FOR_UPDATES)) {
getLoggingAdapter().log(Level.INFO, "Checking for updates..."); getLoggingAdapter().log(Level.INFO, "Checking for updates...");
CompletableFuture.runAsync(() -> new UpdateChecker(getPluginVersion(), getLoggingAdapter()).logToConsole()); getLatestVersionIfOutdated().thenAccept(newestVersion ->
newestVersion.ifPresent(newVersion -> getLoggingAdapter().log(Level.WARNING,
"An update is available for HuskSync, v" + newVersion
+ " (Currently running v" + getPluginVersion() + ")")));
} }
} catch (HuskSyncInitializationException exception) { } catch (HuskSyncInitializationException exception) {
getLoggingAdapter().log(Level.SEVERE, """ getLoggingAdapter().log(Level.SEVERE, """
@ -286,14 +293,16 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
return resourceReader; return resourceReader;
} }
@NotNull
@Override @Override
public @NotNull Version getPluginVersion() { public Version getPluginVersion() {
return Version.pluginVersion(getDescription().getVersion()); return Version.fromString(getDescription().getVersion(), "-");
} }
@NotNull
@Override @Override
public @NotNull Version getMinecraftVersion() { public Version getMinecraftVersion() {
return Version.minecraftVersion(Bukkit.getBukkitVersion()); return Version.fromMinecraftVersionString(Bukkit.getBukkitVersion());
} }
@Override @Override

@ -6,7 +6,7 @@ import net.md_5.bungee.api.chat.BaseComponent;
import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.data.*; import net.william278.husksync.data.*;
import net.william278.husksync.editor.ItemEditorMenu; import net.william278.husksync.editor.ItemEditorMenu;
import net.william278.husksync.util.Version; import net.william278.desertwell.Version;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.advancement.Advancement; import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress; import org.bukkit.advancement.AdvancementProgress;
@ -554,7 +554,7 @@ public class BukkitPlayer extends OnlineUser {
@NotNull @NotNull
@Override @Override
public Version getMinecraftVersion() { public Version getMinecraftVersion() {
return Version.minecraftVersion(Bukkit.getBukkitVersion()); return Version.fromMinecraftVersionString(Bukkit.getBukkitVersion());
} }
@Override @Override

@ -3,6 +3,7 @@ dependencies {
implementation 'de.themoep:minedown:1.7.1-SNAPSHOT' implementation 'de.themoep:minedown:1.7.1-SNAPSHOT'
implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.code.gson:gson:2.9.0'
implementation 'dev.dejvokep:boosted-yaml:1.3' implementation 'dev.dejvokep:boosted-yaml:1.3'
implementation 'net.william278:DesertWell:1.0'
implementation ('com.zaxxer:HikariCP:5.0.1') { implementation ('com.zaxxer:HikariCP:5.0.1') {
exclude module: 'slf4j-api' exclude module: 'slf4j-api'
} }
@ -26,4 +27,5 @@ shadowJar {
relocate 'org.intellij', 'net.william278.husksync.libraries' relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries' relocate 'com.zaxxer', 'net.william278.husksync.libraries'
relocate 'dev.dejvokep', 'net.william278.husksync.libraries' relocate 'dev.dejvokep', 'net.william278.husksync.libraries'
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
} }

@ -1,5 +1,6 @@
package net.william278.husksync; package net.william278.husksync;
import net.william278.desertwell.UpdateChecker;
import net.william278.husksync.config.Locales; import net.william278.husksync.config.Locales;
import net.william278.husksync.config.Settings; import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataAdapter; import net.william278.husksync.data.DataAdapter;
@ -11,7 +12,7 @@ import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.redis.RedisManager; import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.util.Logger; import net.william278.husksync.util.Logger;
import net.william278.husksync.util.ResourceReader; import net.william278.husksync.util.ResourceReader;
import net.william278.husksync.util.Version; import net.william278.desertwell.Version;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
@ -25,6 +26,8 @@ import java.util.concurrent.CompletableFuture;
*/ */
public interface HuskSync { public interface HuskSync {
int SPIGOT_RESOURCE_ID = 97144;
/** /**
* Returns a set of online players. * Returns a set of online players.
* *
@ -131,6 +134,22 @@ public interface HuskSync {
@NotNull @NotNull
Version getPluginVersion(); Version getPluginVersion();
/**
* Returns a future returning the latest plugin {@link Version} if the plugin is out-of-date
*
* @return a {@link CompletableFuture} returning the latest {@link Version} if the current one is out-of-date
*/
default CompletableFuture<Optional<Version>> getLatestVersionIfOutdated() {
final UpdateChecker updateChecker = UpdateChecker.create(getPluginVersion(), SPIGOT_RESOURCE_ID);
return updateChecker.isUpToDate().thenApply(upToDate -> {
if (upToDate) {
return Optional.empty();
} else {
return Optional.of(updateChecker.getLatestVersion().join());
}
});
}
/** /**
* Returns the Minecraft version implementation * Returns the Minecraft version implementation
* *

@ -52,7 +52,7 @@ public abstract class CommandBase {
*/ */
public String getDescription() { public String getDescription() {
return plugin.getLocales().getRawLocale(command + "_command_description") return plugin.getLocales().getRawLocale(command + "_command_description")
.orElse("A HuskHomes command"); .orElse("A HuskSync command");
} }
} }

@ -1,11 +1,10 @@
package net.william278.husksync.command; package net.william278.husksync.command;
import de.themoep.minedown.MineDown; import de.themoep.minedown.MineDown;
import net.william278.desertwell.AboutMenu;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Locales;
import net.william278.husksync.migrator.Migrator; import net.william278.husksync.migrator.Migrator;
import net.william278.husksync.player.OnlineUser; import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.util.UpdateChecker;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;
@ -17,16 +16,41 @@ import java.util.stream.Collectors;
public class HuskSyncCommand extends CommandBase implements TabCompletable, ConsoleExecutable { public class HuskSyncCommand extends CommandBase implements TabCompletable, ConsoleExecutable {
private final String[] COMMAND_ARGUMENTS = {"update", "about", "reload", "migrate"}; private final String[] SUB_COMMANDS = {"update", "about", "reload", "migrate"};
private final AboutMenu aboutMenu;
public HuskSyncCommand(@NotNull HuskSync implementor) { public HuskSyncCommand(@NotNull HuskSync implementor) {
super("husksync", Permission.COMMAND_HUSKSYNC, implementor); super("husksync", Permission.COMMAND_HUSKSYNC, implementor);
this.aboutMenu = AboutMenu.create("HuskSync")
.withDescription("A modern, cross-server player data synchronization system")
.withVersion(implementor.getPluginVersion())
.addAttribution("Author",
AboutMenu.Credit.of("William278").withDescription("Click to visit website").withUrl("https://william278.net"))
.addAttribution("Contributors",
AboutMenu.Credit.of("HarvelsX").withDescription("Code"),
AboutMenu.Credit.of("HookWoods").withDescription("Code"))
.addAttribution("Translators",
AboutMenu.Credit.of("Namiu").withDescription("Japanese (ja-jp)"),
AboutMenu.Credit.of("anchelthe").withDescription("Spanish (es-es)"),
AboutMenu.Credit.of("Melonzio").withDescription("Spanish (es-es)"),
AboutMenu.Credit.of("Ceddix").withDescription("German (de-de)"),
AboutMenu.Credit.of("Pukejoy_1").withDescription("Bulgarian (bg-bg)"),
AboutMenu.Credit.of("mateusneresrb").withDescription("Brazilian Portuguese (pt-br)"),
AboutMenu.Credit.of("小蔡").withDescription("Traditional Chinese (zh-tw)"),
AboutMenu.Credit.of("Ghost-chu").withDescription("Simplified Chinese (zh-cn)"),
AboutMenu.Credit.of("DJelly4K").withDescription("Simplified Chinese (zh-cn)"),
AboutMenu.Credit.of("Thourgard").withDescription("Ukrainian (uk-ua)"),
AboutMenu.Credit.of("xF3d3").withDescription("Italian (it-it)"))
.addButtons(
AboutMenu.Link.of("https://william278.net/docs/husksync").withText("Documentation").withIcon("⛏"),
AboutMenu.Link.of("https://github.com/WiIIiam278/HuskSync/issues").withText("Issues").withIcon("❌").withColor("#ff9f0f"),
AboutMenu.Link.of("https://discord.gg/tVYhJfyDWG").withText("Discord").withIcon("⭐").withColor("#6773f5"));
} }
@Override @Override
public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) {
if (args.length < 1) { if (args.length < 1) {
displayPluginInformation(player); sendAboutMenu(player);
return; return;
} }
switch (args[0].toLowerCase()) { switch (args[0].toLowerCase()) {
@ -35,18 +59,16 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage); plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage);
return; return;
} }
final UpdateChecker updateChecker = new UpdateChecker(plugin.getPluginVersion(), plugin.getLoggingAdapter()); plugin.getLatestVersionIfOutdated().thenAccept(newestVersion ->
updateChecker.fetchLatestVersion().thenAccept(latestVersion -> { newestVersion.ifPresentOrElse(
if (updateChecker.isUpdateAvailable(latestVersion)) { newVersion -> player.sendMessage(
player.sendMessage(new MineDown("[HuskSync](#00fb9a bold) [| A new update is available:](#00fb9a) [HuskSync " + latestVersion + "](#00fb9a bold)" + new MineDown("[HuskSync](#00fb9a bold) [| A new version of HuskSync is available!"
"[•](white) [Currently running:](#00fb9a) [Version " + updateChecker.getCurrentVersion() + "](gray)" + + " (v" + newVersion + " (Running: v" + plugin.getPluginVersion() + ")](#00fb9a)")),
"[•](white) [Download links:](#00fb9a) [[⏩ Spigot]](gray open_url=https://www.spigotmc.org/resources/husksync.97144/updates) [•](#262626) [[⏩ Polymart]](gray open_url=https://polymart.org/resource/husksync.1634/updates) [•](#262626) [[⏩ Songoda]](gray open_url=https://songoda.com/marketplace/product/husksync-a-modern-cross-server-player-data-synchronization-system.758)")); () -> player.sendMessage(
} else { new MineDown("[HuskSync](#00fb9a bold) [| HuskSync is up-to-date."
player.sendMessage(new MineDown("[HuskSync](#00fb9a bold) [| HuskSync is up-to-date, running version " + updateChecker.getCurrentVersion() + "](#00fb9a)")); + " (Running: v" + plugin.getPluginVersion() + ")](#00fb9a)"))));
}
});
} }
case "info", "about" -> displayPluginInformation(player); case "about", "info" -> sendAboutMenu(player);
case "reload" -> { case "reload" -> {
if (!player.hasPermission(Permission.COMMAND_HUSKSYNC_RELOAD.node)) { if (!player.hasPermission(Permission.COMMAND_HUSKSYNC_RELOAD.node)) {
plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage); plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage);
@ -70,11 +92,14 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
return; return;
} }
switch (args[0].toLowerCase()) { switch (args[0].toLowerCase()) {
case "update", "version" -> case "update", "version" -> plugin.getLatestVersionIfOutdated().thenAccept(newestVersion ->
new UpdateChecker(plugin.getPluginVersion(), plugin.getLoggingAdapter()).logToConsole(); newestVersion.ifPresentOrElse(newVersion -> plugin.getLoggingAdapter().log(Level.WARNING,
case "info", "about" -> "An update is available for HuskSync, v" + newVersion
plugin.getLoggingAdapter().log(Level.INFO, new MineDown(plugin.getLocales().stripMineDown( + " (Running v" + plugin.getPluginVersion() + ")"),
Locales.PLUGIN_INFORMATION.replace("%version%", plugin.getPluginVersion().toString())))); () -> plugin.getLoggingAdapter().log(Level.INFO,
"HuskSync is up to date" +
" (Running v" + plugin.getPluginVersion() + ")")));
case "about", "info" -> aboutMenu.toString().lines().forEach(plugin.getLoggingAdapter()::info);
case "reload" -> { case "reload" -> {
plugin.reload(); plugin.reload();
plugin.getLoggingAdapter().log(Level.INFO, "Reloaded config & message files."); plugin.getLoggingAdapter().log(Level.INFO, "Reloaded config & message files.");
@ -128,18 +153,18 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
@Override @Override
public List<String> onTabComplete(@NotNull String[] args) { public List<String> onTabComplete(@NotNull String[] args) {
if (args.length <= 1) { if (args.length <= 1) {
return Arrays.stream(COMMAND_ARGUMENTS) return Arrays.stream(SUB_COMMANDS)
.filter(argument -> argument.startsWith(args.length >= 1 ? args[0] : "")) .filter(argument -> argument.startsWith(args.length >= 1 ? args[0] : ""))
.sorted().collect(Collectors.toList()); .sorted().collect(Collectors.toList());
} }
return Collections.emptyList(); return Collections.emptyList();
} }
private void displayPluginInformation(@NotNull OnlineUser player) { private void sendAboutMenu(@NotNull OnlineUser player) {
if (!player.hasPermission(Permission.COMMAND_HUSKSYNC_INFO.node)) { if (!player.hasPermission(Permission.COMMAND_HUSKSYNC_ABOUT.node)) {
plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage); plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage);
return; return;
} }
player.sendMessage(new MineDown(Locales.PLUGIN_INFORMATION.replace("%version%", plugin.getPluginVersion().toString()))); player.sendMessage(aboutMenu.toMineDown());
} }
} }

@ -18,7 +18,7 @@ public enum Permission {
/** /**
* Lets the user view plugin info {@code /husksync info} * Lets the user view plugin info {@code /husksync info}
*/ */
COMMAND_HUSKSYNC_INFO("husksync.command.husksync.info", DefaultAccess.EVERYONE), COMMAND_HUSKSYNC_ABOUT("husksync.command.husksync.info", DefaultAccess.EVERYONE),
/** /**
* Lets the user reload the plugin {@code /husksync reload} * Lets the user reload the plugin {@code /husksync reload}
*/ */

@ -6,7 +6,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.HashMap; import java.util.HashMap;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -14,16 +13,6 @@ import java.util.regex.Pattern;
*/ */
public class Locales { public class Locales {
public static final String PLUGIN_INFORMATION = """
[HuskSync](#00fb9a bold) [| Version %version%](#00fb9a)
[A modern, cross-server player data synchronization system](gray)
[ Author:](white) [William278](gray show_text=&7Click to visit website open_url=https://william278.net)
[ Contributors:](white) [HarvelsX](gray show_text=&7Code), [HookWoods](gray show_text=&7Code)
[ Translators:](white) [Namiu](gray show_text=&7\\(\\) - Japanese, ja-jp), [anchelthe](gray show_text=&7Spanish, es-es), [Melonzio](gray show_text=&7Spanish, es-es), [Ceddix](gray show_text=&7German, de-de), [Pukejoy_1](gray show_text=&7Bulgarian, bg-bg), [mateusneresrb](gray show_text=&7Brazilian Portuguese, pt-br], [](gray show_text=&7Traditional Chinese, zh-tw), [Ghost-chu](gray show_text=&7Simplified Chinese, zh-cn), [DJelly4K](gray show_text=&7Simplified Chinese, zh-cn), [Thourgard](gray show_text=&7Ukrainian, uk-ua), [xF3d3](gray show_text=&7Italian, it-it)
[ Documentation:](white) [[Link]](#00fb9a show_text=&7Click to open link open_url=https://william278.net/docs/husksync/Home/)
[ Bug reporting:](white) [[Link]](#00fb9a show_text=&7Click to open link open_url=https://github.com/WiIIiam278/HuskSync/issues)
[ Discord support:](white) [[Link]](#00fb9a show_text=&7Click to join open_url=https://discord.gg/tVYhJfyDWG)""";
@NotNull @NotNull
private final HashMap<String, String> rawLocales; private final HashMap<String, String> rawLocales;
@ -106,34 +95,4 @@ public class Locales {
return new Locales(localesConfig); return new Locales(localesConfig);
} }
/**
* Strips a string of basic MineDown formatting, used for displaying plugin info to console
*
* @param string The string to strip
* @return The MineDown-stripped string
*/
public String stripMineDown(@NotNull String string) {
final String[] in = string.split("\n");
final StringBuilder out = new StringBuilder();
String regex = "[^\\[\\]() ]*\\[([^()]+)]\\([^()]+open_url=(\\S+).*\\)";
for (int i = 0; i < in.length; i++) {
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(in[i]);
if (m.find()) {
out.append(in[i].replace(m.group(0), ""));
out.append(m.group(2));
} else {
out.append(in[i]);
}
if (i + 1 != in.length) {
out.append("\n");
}
}
return out.toString();
}
} }

@ -7,7 +7,7 @@ import net.william278.husksync.editor.ItemEditorMenu;
import net.william278.husksync.event.EventCannon; import net.william278.husksync.event.EventCannon;
import net.william278.husksync.event.PreSyncEvent; import net.william278.husksync.event.PreSyncEvent;
import net.william278.husksync.util.Logger; import net.william278.husksync.util.Logger;
import net.william278.husksync.util.Version; import net.william278.desertwell.Version;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
@ -176,7 +176,7 @@ public abstract class OnlineUser extends User {
@NotNull Version serverMinecraftVersion) { @NotNull Version serverMinecraftVersion) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
// Prevent synchronising user data from newer versions of Minecraft // Prevent synchronising user data from newer versions of Minecraft
if (Version.minecraftVersion(data.getMinecraftVersion()).compareTo(serverMinecraftVersion) > 0) { if (Version.fromMinecraftVersionString(data.getMinecraftVersion()).compareTo(serverMinecraftVersion) > 0) {
logger.log(Level.SEVERE, "Cannot set data for " + username + logger.log(Level.SEVERE, "Cannot set data for " + username +
" because the Minecraft version of their user data (" + data.getMinecraftVersion() + " because the Minecraft version of their user data (" + data.getMinecraftVersion() +
") is newer than the server's Minecraft version (" + serverMinecraftVersion + ")."); ") is newer than the server's Minecraft version (" + serverMinecraftVersion + ").");

@ -1,57 +0,0 @@
package net.william278.husksync.util;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
public class UpdateChecker {
private final static int SPIGOT_PROJECT_ID = 97144;
private final Logger logger;
private final Version currentVersion;
public UpdateChecker(@NotNull Version currentVersion, @NotNull Logger logger) {
this.currentVersion = currentVersion;
this.logger = logger;
}
public CompletableFuture<Version> fetchLatestVersion() {
return CompletableFuture.supplyAsync(() -> {
try {
final URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + SPIGOT_PROJECT_ID);
URLConnection urlConnection = url.openConnection();
return Version.pluginVersion(new BufferedReader(new InputStreamReader(urlConnection.getInputStream())).readLine());
} catch (Exception e) {
logger.log(Level.WARNING, "Failed to fetch the latest plugin version", e);
}
return new Version();
});
}
public boolean isUpdateAvailable(@NotNull Version latestVersion) {
return latestVersion.compareTo(currentVersion) > 0;
}
public Version getCurrentVersion() {
return currentVersion;
}
public CompletableFuture<Boolean> isUpToDate() {
return fetchLatestVersion().thenApply(this::isUpdateAvailable);
}
public void logToConsole() {
fetchLatestVersion().thenAccept(latestVersion -> {
if (isUpdateAvailable(latestVersion)) {
logger.log(Level.WARNING, "A new version of HuskSync is available: v" + latestVersion);
} else {
logger.log(Level.INFO, "HuskSync is up-to-date! (Running: v" + getCurrentVersion().toString() + ")");
}
});
}
}

@ -1,71 +0,0 @@
package net.william278.husksync.util;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.StringJoiner;
import java.util.regex.Pattern;
public class Version implements Comparable<Version> {
private final static String VERSION_SEPARATOR = ".";
private final static String MINECRAFT_META_SEPARATOR = "-";
private final static String PLUGIN_META_SEPARATOR = "+";
private int[] versions = new int[]{};
@NotNull
private String metadata = "";
@NotNull
private String metaSeparator = "";
protected Version() {
}
private Version(@NotNull String version, @NotNull String metaSeparator) {
this.parse(version, metaSeparator);
this.metaSeparator = metaSeparator;
}
@NotNull
public static Version pluginVersion(@NotNull String versionString) {
return new Version(versionString, PLUGIN_META_SEPARATOR);
}
@NotNull
public static Version minecraftVersion(@NotNull String versionString) {
return new Version(versionString, MINECRAFT_META_SEPARATOR);
}
private void parse(@NotNull String version, @NotNull String metaSeparator) {
int metaIndex = version.indexOf(metaSeparator);
if (metaIndex > 0) {
this.metadata = version.substring(metaIndex + 1);
version = version.substring(0, metaIndex);
}
String[] versions = version.split(Pattern.quote(VERSION_SEPARATOR));
this.versions = Arrays.stream(versions).mapToInt(Integer::parseInt).toArray();
}
@Override
public int compareTo(@NotNull Version other) {
int length = Math.max(this.versions.length, other.versions.length);
for (int i = 0; i < length; i++) {
int a = i < this.versions.length ? this.versions[i] : 0;
int b = i < other.versions.length ? other.versions[i] : 0;
if (a < b) return -1;
if (a > b) return 1;
}
return 0;
}
@Override
public String toString() {
final StringJoiner joiner = new StringJoiner(VERSION_SEPARATOR);
for (int version : this.versions) {
joiner.add(String.valueOf(version));
}
return joiner + ((!this.metadata.isEmpty()) ? (this.metaSeparator + this.metadata) : "");
}
}

@ -3,7 +3,7 @@ package net.william278.husksync.player;
import de.themoep.minedown.MineDown; import de.themoep.minedown.MineDown;
import net.william278.husksync.data.*; import net.william278.husksync.data.*;
import net.william278.husksync.editor.ItemEditorMenu; import net.william278.husksync.editor.ItemEditorMenu;
import net.william278.husksync.util.Version; import net.william278.desertwell.Version;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
@ -132,7 +132,7 @@ public class DummyPlayer extends OnlineUser {
@NotNull @NotNull
@Override @Override
public Version getMinecraftVersion() { public Version getMinecraftVersion() {
return Version.minecraftVersion("1.19-beta123456"); return Version.fromMinecraftVersionString("1.19-beta123456");
} }
@Override @Override

@ -3,7 +3,7 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.daemon=true org.gradle.daemon=true
javaVersion=16 javaVersion=16
plugin_version=2.0.2 plugin_version=2.1
plugin_archive=husksync plugin_archive=husksync
jedis_version=4.2.3 jedis_version=4.2.3

Loading…
Cancel
Save