From 96c6a878c4f77d0205fd1150aa93610282081d38 Mon Sep 17 00:00:00 2001
From: William
Date: Sat, 9 Jul 2022 00:29:39 +0100
Subject: [PATCH] Shrink built jar file size, work on MySQLPlayerDataBridge
migrator
---
api/build.gradle | 3 +-
bukkit/build.gradle | 7 +-
.../william278/husksync/BukkitHuskSync.java | 47 ++--
.../husksync/command/BukkitCommand.java | 4 +-
.../husksync/migrator/MpdbMigrator.java | 243 ++++++++++++++++++
bukkit/src/main/resources/plugin.yml | 2 +
common/build.gradle | 16 +-
.../net/william278/husksync/HuskSync.java | 4 +
.../husksync/command/EnderChestCommand.java | 2 +-
.../husksync/command/HuskSyncCommand.java | 43 +++-
.../husksync/command/InventoryCommand.java | 2 +-
.../husksync/data/DataSaveCause.java | 16 +-
.../husksync/editor/DataEditor.java | 11 +-
.../husksync/migrator/LegacyMigrator.java | 43 ++++
.../husksync/migrator/Migrator.java | 38 +++
common/src/main/resources/locales/en-gb.yml | 17 +-
16 files changed, 436 insertions(+), 62 deletions(-)
create mode 100644 bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java
create mode 100644 common/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java
create mode 100644 common/src/main/java/net/william278/husksync/migrator/Migrator.java
diff --git a/api/build.gradle b/api/build.gradle
index 95e32229..b8dd39f9 100644
--- a/api/build.gradle
+++ b/api/build.gradle
@@ -13,10 +13,9 @@ shadowJar {
relocate 'org.jetbrains', 'net.william278.husksync.libraries'
relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
- relocate 'org.slf4j', 'net.william278.husksync.libraries.slf4j'
relocate 'com.google', 'net.william278.husksync.libraries'
- //relocate 'org.xerial', 'net.william278.husksync.libraries'
relocate 'redis.clients', 'net.william278.husksync.libraries'
+ relocate 'org.json', 'net.william278.husksync.libraries.json'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
diff --git a/bukkit/build.gradle b/bukkit/build.gradle
index 3e1356d0..5323faf5 100644
--- a/bukkit/build.gradle
+++ b/bukkit/build.gradle
@@ -1,28 +1,25 @@
dependencies {
implementation project(path: ':common')
-
implementation 'org.bstats:bstats-bukkit:3.0.0'
implementation 'net.william278:mpdbdataconverter:1.0'
- implementation 'net.byteflux:libby-bukkit:1.1.5'
compileOnly 'redis.clients:jedis:4.2.3'
compileOnly 'commons-io:commons-io:2.11.0'
compileOnly 'de.themoep:minedown:1.7.1-SNAPSHOT'
compileOnly 'dev.dejvokep:boosted-yaml:1.2'
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
+ compileOnly 'com.zaxxer:HikariCP:5.0.1'
}
shadowJar {
relocate 'org.apache', 'net.william278.husksync.libraries'
- relocate 'dev.dejvokep', 'net.william278.husksync.libraries'
relocate 'de.themoep', 'net.william278.husksync.libraries'
relocate 'org.jetbrains', 'net.william278.husksync.libraries'
relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
- relocate 'org.slf4j', 'net.william278.husksync.libraries.slf4j'
relocate 'com.google', 'net.william278.husksync.libraries'
- //relocate 'org.xerial', 'net.william278.husksync.libraries'
relocate 'redis.clients', 'net.william278.husksync.libraries'
+ relocate 'org.json', 'net.william278.husksync.libraries.json'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
diff --git a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java
index e727d59f..7f8c205d 100644
--- a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java
+++ b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java
@@ -6,7 +6,9 @@ import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
-import net.william278.husksync.command.*;
+import net.william278.husksync.command.BukkitCommand;
+import net.william278.husksync.command.BukkitCommandType;
+import net.william278.husksync.command.Permission;
import net.william278.husksync.config.Locales;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.CompressedDataAdapter;
@@ -19,6 +21,9 @@ import net.william278.husksync.event.BukkitEventCannon;
import net.william278.husksync.event.EventCannon;
import net.william278.husksync.listener.BukkitEventListener;
import net.william278.husksync.listener.EventListener;
+import net.william278.husksync.migrator.LegacyMigrator;
+import net.william278.husksync.migrator.Migrator;
+import net.william278.husksync.migrator.MpdbMigrator;
import net.william278.husksync.player.BukkitPlayer;
import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.redis.RedisManager;
@@ -27,6 +32,7 @@ import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionDefault;
+import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@@ -40,24 +46,16 @@ import java.util.stream.Collectors;
public class BukkitHuskSync extends JavaPlugin implements HuskSync {
private Database database;
-
private RedisManager redisManager;
-
private Logger logger;
-
private ResourceReader resourceReader;
-
private EventListener eventListener;
-
private DataAdapter dataAdapter;
-
private DataEditor dataEditor;
-
private EventCannon eventCannon;
private Settings settings;
-
private Locales locales;
-
+ private List availableMigrators;
private static BukkitHuskSync instance;
/**
@@ -72,18 +70,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
@Override
public void onLoad() {
instance = this;
- /*getLogger().log(Level.INFO, "Loading runtime libraries...");
- final BukkitLibraryManager libraryManager = new BukkitLibraryManager(this);
- final Library[] libraries = new Library[]{
- Library.builder().groupId("redis{}clients")
- .artifactId("jedis")
- .version("4.2.3")
- .id("jedis")
- .build()
- };
- libraryManager.addMavenCentral();
- Arrays.stream(libraries).forEach(libraryManager::loadLibrary);
- getLogger().log(Level.INFO, "Successfully loaded runtime libraries.");*/
}
@Override
@@ -127,6 +113,17 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
dataEditor = new DataEditor(locales);
}
return succeeded;
+ }).thenApply(succeeded -> {
+ // Prepare migrators
+ if (succeeded) {
+ availableMigrators = new ArrayList<>();
+ availableMigrators.add(new LegacyMigrator(this));
+ final Plugin mySqlPlayerDataBridge = Bukkit.getPluginManager().getPlugin("MySqlPlayerDataBridge");
+ if (mySqlPlayerDataBridge != null) {
+ availableMigrators.add(new MpdbMigrator(this, mySqlPlayerDataBridge));
+ }
+ }
+ return succeeded;
}).thenApply(succeeded -> {
// Establish connection to the database
if (succeeded) {
@@ -254,6 +251,12 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
return eventCannon;
}
+ @NotNull
+ @Override
+ public List getAvailableMigrators() {
+ return null;
+ }
+
@Override
public @NotNull Settings getSettings() {
return settings;
diff --git a/bukkit/src/main/java/net/william278/husksync/command/BukkitCommand.java b/bukkit/src/main/java/net/william278/husksync/command/BukkitCommand.java
index 0a6962d2..ab64b8b1 100644
--- a/bukkit/src/main/java/net/william278/husksync/command/BukkitCommand.java
+++ b/bukkit/src/main/java/net/william278/husksync/command/BukkitCommand.java
@@ -48,10 +48,10 @@ public class BukkitCommand implements CommandExecutor, TabExecutor {
if (sender instanceof Player player) {
this.command.onExecute(BukkitPlayer.adapt(player), args);
} else {
- if (command instanceof ConsoleExecutable consoleExecutable) {
+ if (this.command instanceof ConsoleExecutable consoleExecutable) {
consoleExecutable.onConsoleExecute(args);
} else {
- plugin.getLocales().getLocale("error_in_game_only").
+ plugin.getLocales().getLocale("error_in_game_command_only").
ifPresent(locale -> sender.spigot().sendMessage(locale.toComponent()));
}
}
diff --git a/bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java b/bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java
new file mode 100644
index 00000000..444bdd65
--- /dev/null
+++ b/bukkit/src/main/java/net/william278/husksync/migrator/MpdbMigrator.java
@@ -0,0 +1,243 @@
+package net.william278.husksync.migrator;
+
+import com.zaxxer.hikari.HikariDataSource;
+import net.william278.husksync.BukkitHuskSync;
+import net.william278.husksync.config.Settings;
+import net.william278.husksync.data.*;
+import net.william278.husksync.player.User;
+import net.william278.mpdbconverter.MPDBConverter;
+import org.bukkit.Bukkit;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Level;
+import java.util.regex.Pattern;
+
+public class MpdbMigrator extends Migrator {
+
+ private final MPDBConverter mpdbConverter;
+ private String sourceHost;
+ private int sourcePort;
+ private String sourceUsername;
+ private String sourcePassword;
+ private String sourceDatabase;
+ private String sourceInventoryTable;
+ private String sourceEnderChestTable;
+ private String sourceExperienceTable;
+
+ public MpdbMigrator(@NotNull BukkitHuskSync plugin, @NotNull Plugin mySqlPlayerDataBridge) {
+ super(plugin);
+ this.mpdbConverter = MPDBConverter.getInstance(mySqlPlayerDataBridge);
+ this.sourceHost = plugin.getSettings().getStringValue(Settings.ConfigOption.DATABASE_HOST);
+ this.sourcePort = plugin.getSettings().getIntegerValue(Settings.ConfigOption.DATABASE_PORT);
+ this.sourceUsername = plugin.getSettings().getStringValue(Settings.ConfigOption.DATABASE_USERNAME);
+ this.sourcePassword = plugin.getSettings().getStringValue(Settings.ConfigOption.DATABASE_PASSWORD);
+ this.sourceDatabase = plugin.getSettings().getStringValue(Settings.ConfigOption.DATABASE_NAME);
+ this.sourceInventoryTable = "mpdb_inventory";
+ this.sourceEnderChestTable = "mpdb_enderchest";
+ this.sourceExperienceTable = "mpdb_experience";
+ }
+
+ @Override
+ public CompletableFuture start() {
+ plugin.getLoggingAdapter().log(Level.INFO, "Starting migration from MySQLPlayerDataBridge to HuskSync...");
+ final long startTime = System.currentTimeMillis();
+ return CompletableFuture.supplyAsync(() -> {
+ // Create jdbc driver connection url
+ final String jdbcUrl = "jdbc:mysql://" + sourceHost + ":" + sourcePort + "/" + sourceDatabase;
+
+ // Create a new data source for the mpdb converter
+ try (final HikariDataSource connectionPool = new HikariDataSource()) {
+ plugin.getLoggingAdapter().log(Level.INFO, "Establishing connection to MySQLPlayerDataBridge database...");
+ connectionPool.setJdbcUrl(jdbcUrl);
+ connectionPool.setUsername(sourceUsername);
+ connectionPool.setPassword(sourcePassword);
+ connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase());
+
+ plugin.getLoggingAdapter().log(Level.INFO, "Downloading raw data from the MySQLPlayerDataBridge database...");
+ final List dataToMigrate = new ArrayList<>();
+ try (final Connection connection = connectionPool.getConnection()) {
+ try (final PreparedStatement statement = connection.prepareStatement("""
+ SELECT `player_uuid`, `player_name`, `inventory`, `armor`, `enderchest`, `exp_lvl`, `exp`, `total_exp`
+ FROM `%source_inventory_table%`
+ INNER JOIN `%source_ender_chest_table%`
+ ON `%source_inventory_table%`.`player_uuid` = `%source_ender_chest_table%`.`player_uuid`
+ INNER JOIN `%source_experience_table%`
+ ON `%source_inventory_table%`.`player_uuid` = `%source_experience_table%`.`player_uuid`;
+ """.replaceAll(Pattern.quote("%source_inventory_table%"), sourceInventoryTable).replaceAll(Pattern.quote("%source_ender_chest_table%"), sourceEnderChestTable).replaceAll(Pattern.quote("%source_experience_table%"), sourceExperienceTable))) {
+ try (final ResultSet resultSet = statement.executeQuery()) {
+ int playersMigrated = 0;
+ while (resultSet.next()) {
+ dataToMigrate.add(new MpdbData(
+ new User(UUID.fromString(resultSet.getString("player_uuid")),
+ resultSet.getString("player_name")),
+ resultSet.getString("inventory"),
+ resultSet.getString("armor"),
+ resultSet.getString("enderchest"),
+ resultSet.getInt("exp_lvl"),
+ resultSet.getInt("exp"),
+ resultSet.getInt("total_exp")
+ ));
+ playersMigrated++;
+ if (playersMigrated % 25 == 0) {
+ plugin.getLoggingAdapter().log(Level.INFO, "Downloaded MySQLPlayerDataBridge data for " + playersMigrated + " players...");
+ }
+ }
+ }
+ }
+ }
+ plugin.getLoggingAdapter().log(Level.INFO, "Completed download of " + dataToMigrate.size() + " entries from the MySQLPlayerDataBridge database!");
+ plugin.getLoggingAdapter().log(Level.INFO, "Converting raw MySQLPlayerDataBridge data to HuskSync user data...");
+ dataToMigrate.forEach(data -> data.toUserData(mpdbConverter).thenAccept(convertedData ->
+ plugin.getDatabase().ensureUser(data.user()).thenRun(() ->
+ plugin.getDatabase().setUserData(data.user(), convertedData, DataSaveCause.MPDB_MIGRATION))));
+ plugin.getLoggingAdapter().log(Level.INFO, "Migration complete for " + dataToMigrate.size() + " users in " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds!");
+ return true;
+ } catch (Exception e) {
+ plugin.getLoggingAdapter().log(Level.SEVERE, "Error while migrating data: " + e.getMessage());
+ return false;
+ }
+ });
+ }
+
+ @Override
+ public void handleConfigurationCommand(@NotNull String[] args) {
+ if (args.length == 2) {
+ if (switch (args[0].toLowerCase()) {
+ case "host" -> {
+ this.sourceHost = args[1];
+ yield true;
+ }
+ case "port" -> {
+ try {
+ this.sourcePort = Integer.parseInt(args[1]);
+ yield true;
+ } catch (NumberFormatException e) {
+ yield false;
+ }
+ }
+ case "username" -> {
+ this.sourceUsername = args[1];
+ yield true;
+ }
+ case "password" -> {
+ this.sourcePassword = args[1];
+ yield true;
+ }
+ case "database" -> {
+ this.sourceDatabase = args[1];
+ yield true;
+ }
+ case "inventory_table" -> {
+ this.sourceInventoryTable = args[1];
+ yield true;
+ }
+ case "ender_chest_table" -> {
+ this.sourceEnderChestTable = args[1];
+ yield true;
+ }
+ case "experience_table" -> {
+ this.sourceExperienceTable = args[1];
+ yield true;
+ }
+ default -> false;
+ }) {
+ plugin.getLoggingAdapter().log(Level.INFO, getHelpMenu());
+ plugin.getLoggingAdapter().log(Level.INFO, "Successfully set " + args[0] + " to " + args[1]);
+ } else {
+ plugin.getLoggingAdapter().log(Level.INFO, "Invalid operation, could not set " + args[0] + " to " + args[1] + " (is it a valid option?)");
+ }
+ }
+ }
+
+ @NotNull
+ @Override
+ public String getIdentifier() {
+ return "mpdb";
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "MySQLPlayerDataBridge";
+ }
+
+ @NotNull
+ @Override
+ public String getHelpMenu() {
+ return """
+ === MySQLPlayerDataBridge Migration Wizard ==========
+ This will migrate inventories, ender chests and XP
+ from the MySQLPlayerDataBridge plugin to HuskSync.
+
+ To prevent excessive migration times, other non-vital
+ data will not be transferred.
+
+ STEP 1] Please ensure no players are on the server.
+
+ STEP 2] HuskSync will need to connect to the database
+ used to hold the source MySQLPlayerDataBridge data.
+ Please check these database parameters are OK:
+ - host: %source_host%
+ - port: %source_port%
+ - username: %source_username%
+ - password: %source_password%
+ - database: %source_database%
+ - inventory_table: %source_inventory_table%
+ - ender_chest_table: %source_ender_chest_table%
+ - experience_table: %source_xp_table%
+ If any of these are not correct, please correct them
+ using the command:
+ "husksync migrate mpdb set "
+ (e.g.: "husksync migrate mpdb set host 123.456.789")
+
+ STEP 3] HuskSync will migrate data into the database
+ tables configures in the config.yml file of this
+ server. Please make sure you're happy with this
+ before proceeding.
+
+ STEP 4] To start the migration, please run:
+ "husksync migrate mpdb start"
+ """;
+ }
+
+ private record MpdbData(@NotNull User user, @NotNull String serializedInventory,
+ @NotNull String serializedArmor, @NotNull String serializedEnderChest,
+ int expLevel, float expProgress, int totalExp) {
+ @NotNull
+ public CompletableFuture toUserData(@NotNull MPDBConverter converter) {
+ return CompletableFuture.supplyAsync(() -> {
+ // Combine inventory and armour
+ final Inventory inventory = Bukkit.createInventory(null, InventoryType.PLAYER);
+ inventory.setContents(converter.getItemStackFromSerializedData(serializedInventory));
+ final ItemStack[] armor = converter.getItemStackFromSerializedData(serializedArmor).clone();
+ for (int i = 36; i < 36 + armor.length; i++) {
+ inventory.setItem(i, armor[i - 36]);
+ }
+
+ // Create user data record
+ return new UserData(
+ new StatusData(20, 20, 0, 20, 10,
+ 1, 0, totalExp, expLevel, expProgress, "SURVIVAL",
+ false),
+ new ItemData(BukkitSerializer.serializeItemStackArray(inventory.getContents()).join()),
+ new ItemData(BukkitSerializer.serializeItemStackArray(converter
+ .getItemStackFromSerializedData(serializedEnderChest)).join()),
+ new PotionEffectData(""), new ArrayList<>(),
+ new StatisticsData(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>()),
+ new LocationData("world", UUID.randomUUID(), "NORMAL", 0, 0, 0,
+ 0f, 0f),
+ new PersistentDataContainerData(new HashMap<>()));
+ });
+ }
+ }
+
+}
diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml
index 7dedb160..b4b684e2 100644
--- a/bukkit/src/main/resources/plugin.yml
+++ b/bukkit/src/main/resources/plugin.yml
@@ -8,6 +8,8 @@ website: 'https://william278.net'
softdepend: [ MysqlPlayerDataBridge ]
libraries:
- 'mysql:mysql-connector-java:8.0.29'
+ - 'org.xerial.snappy:snappy-java:1.1.8.4'
+ - 'dev.dejvokep:boosted-yaml:1.2'
commands:
husksync:
usage: '/husksync '
diff --git a/common/build.gradle b/common/build.gradle
index 9e39baf9..acd05469 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -1,24 +1,26 @@
dependencies {
implementation 'commons-io:commons-io:2.11.0'
- implementation 'dev.dejvokep:boosted-yaml:1.2'
implementation 'de.themoep:minedown:1.7.1-SNAPSHOT'
- implementation 'com.zaxxer:HikariCP:5.0.1'
implementation 'com.google.code.gson:gson:2.9.0'
- implementation 'org.xerial.snappy:snappy-java:1.1.8.4'
- implementation 'redis.clients:jedis:4.2.3'
+ implementation('redis.clients:jedis:4.2.3') {
+ exclude module: 'slf4j-api'
+ }
+ implementation ('com.zaxxer:HikariCP:5.0.1') {
+ exclude module: 'slf4j-api'
+ }
+ compileOnly 'dev.dejvokep:boosted-yaml:1.2'
+ compileOnly 'org.xerial.snappy:snappy-java:1.1.8.4'
compileOnly 'org.jetbrains:annotations:23.0.0'
}
shadowJar {
relocate 'org.apache', 'net.william278.husksync.libraries'
- relocate 'dev.dejvokep', 'net.william278.husksync.libraries'
relocate 'de.themoep', 'net.william278.husksync.libraries'
relocate 'org.jetbrains', 'net.william278.husksync.libraries'
relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
- relocate 'org.slf4j', 'net.william278.husksync.libraries.slf4j'
relocate 'com.google', 'net.william278.husksync.libraries'
- //relocate 'org.xerial', 'net.william278.husksync.libraries'
relocate 'redis.clients', 'net.william278.husksync.libraries'
+ relocate 'org.json', 'net.william278.husksync.libraries.json'
}
\ No newline at end of file
diff --git a/common/src/main/java/net/william278/husksync/HuskSync.java b/common/src/main/java/net/william278/husksync/HuskSync.java
index 777be7aa..f51b77d5 100644
--- a/common/src/main/java/net/william278/husksync/HuskSync.java
+++ b/common/src/main/java/net/william278/husksync/HuskSync.java
@@ -6,11 +6,13 @@ import net.william278.husksync.data.DataAdapter;
import net.william278.husksync.editor.DataEditor;
import net.william278.husksync.database.Database;
import net.william278.husksync.event.EventCannon;
+import net.william278.husksync.migrator.Migrator;
import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.util.Logger;
import org.jetbrains.annotations.NotNull;
+import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@@ -32,6 +34,8 @@ public interface HuskSync {
@NotNull EventCannon getEventCannon();
+ @NotNull List getAvailableMigrators();
+
@NotNull Settings getSettings();
@NotNull Locales getLocales();
diff --git a/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java b/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java
index 5422aa82..d4614a9a 100644
--- a/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java
@@ -82,7 +82,7 @@ public class EnderChestCommand extends CommandBase implements TabCompletable {
@Override
public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) {
return plugin.getOnlineUsers().stream().map(user -> user.username)
- .filter(argument -> argument.startsWith(args.length >= 1 ? args[1] : ""))
+ .filter(argument -> argument.startsWith(args.length >= 1 ? args[0] : ""))
.sorted().collect(Collectors.toList());
}
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 416f309b..29ab8da0 100644
--- a/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java
@@ -3,12 +3,14 @@ package net.william278.husksync.command;
import de.themoep.minedown.MineDown;
import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Locales;
+import net.william278.husksync.migrator.Migrator;
import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.util.UpdateChecker;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -50,11 +52,13 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
return;
}
plugin.reload();
- player.sendMessage(new MineDown("[HuskSync](#00fb9a bold) [| Reloaded config & message files.](#00fb9a)"));
+ plugin.getLocales().getLocale("reload_complete").ifPresent(player::sendMessage);
}
+ case "migrate" ->
+ plugin.getLocales().getLocale("error_console_command_only").ifPresent(player::sendMessage);
default -> plugin.getLocales().getLocale("error_invalid_syntax",
"/husksync ")
- .ifPresent(player::sendMessage);
+ .ifPresent(player::sendMessage);
}
}
@@ -74,13 +78,46 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
plugin.getLoggingAdapter().log(Level.INFO, "Reloaded config & message files.");
}
case "migrate" -> {
- //todo - MPDB migrator
+ if (args.length < 2) {
+ plugin.getLoggingAdapter().log(Level.INFO,
+ "Please choose a migrator, then run \"husksync migrate \"");
+ logMigratorsList();
+ return;
+ }
+ final Optional selectedMigrator = plugin.getAvailableMigrators().stream().filter(availableMigrator ->
+ availableMigrator.getIdentifier().equalsIgnoreCase(args[1])).findFirst();
+ selectedMigrator.ifPresentOrElse(migrator -> {
+ if (args.length < 3) {
+ plugin.getLoggingAdapter().log(Level.INFO,
+ "Invalid syntax. Console usage: \"husksync migrate " + args[1] + "");
+ return;
+ }
+ switch (args[2]) {
+ case "start" -> migrator.start();
+ case "set" -> migrator.handleConfigurationCommand(Arrays.copyOfRange(args, 3, args.length));
+ default -> plugin.getLoggingAdapter().log(Level.INFO,
+ "Invalid syntax. Console usage: \"husksync migrate " + args[1] + "");
+ }
+ }, () -> {
+ plugin.getLoggingAdapter().log(Level.INFO,
+ "Please specify a valid migrator.\n" +
+ "If a migrator is not available, please verify that you meet the prerequisites to use it.");
+ logMigratorsList();
+ });
}
default -> plugin.getLoggingAdapter().log(Level.INFO,
"Invalid syntax. Console usage: \"husksync \"");
}
}
+ private void logMigratorsList() {
+ plugin.getLoggingAdapter().log(Level.INFO,
+ "List of available migrators:\nMigrator ID / Migrator Name:\n" +
+ plugin.getAvailableMigrators().stream()
+ .map(migrator -> migrator.getIdentifier() + " - " + migrator.getName())
+ .collect(Collectors.joining("\n")));
+ }
+
@Override
public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) {
return Arrays.stream(COMMAND_ARGUMENTS)
diff --git a/common/src/main/java/net/william278/husksync/command/InventoryCommand.java b/common/src/main/java/net/william278/husksync/command/InventoryCommand.java
index 23b0af90..9237b9e3 100644
--- a/common/src/main/java/net/william278/husksync/command/InventoryCommand.java
+++ b/common/src/main/java/net/william278/husksync/command/InventoryCommand.java
@@ -81,7 +81,7 @@ public class InventoryCommand extends CommandBase implements TabCompletable {
@Override
public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) {
return plugin.getOnlineUsers().stream().map(user -> user.username)
- .filter(argument -> argument.startsWith(args.length >= 1 ? args[1] : ""))
+ .filter(argument -> argument.startsWith(args.length >= 1 ? args[0] : ""))
.sorted().collect(Collectors.toList());
}
}
diff --git a/common/src/main/java/net/william278/husksync/data/DataSaveCause.java b/common/src/main/java/net/william278/husksync/data/DataSaveCause.java
index 1a760246..ac2ce0b4 100644
--- a/common/src/main/java/net/william278/husksync/data/DataSaveCause.java
+++ b/common/src/main/java/net/william278/husksync/data/DataSaveCause.java
@@ -58,10 +58,18 @@ public enum DataSaveCause {
* @since 2.0
*/
API,
-
- MPDB_IMPORT,
- LEGACY_IMPORT,
- MANUAL_IMPORT,
+ /**
+ * Indicates data was saved from being imported from MySQLPlayerDataBridge
+ *
+ * @since 2.0
+ */
+ MPDB_MIGRATION,
+ /**
+ * Indicates data was saved from being imported from a legacy version (v1.x)
+ *
+ * @since 2.0
+ */
+ LEGACY_MIGRATION,
/**
* Indicates data was saved by an unknown cause.
*
diff --git a/common/src/main/java/net/william278/husksync/editor/DataEditor.java b/common/src/main/java/net/william278/husksync/editor/DataEditor.java
index 4443083d..2fb51f91 100644
--- a/common/src/main/java/net/william278/husksync/editor/DataEditor.java
+++ b/common/src/main/java/net/william278/husksync/editor/DataEditor.java
@@ -13,6 +13,7 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
/**
* Provides methods for displaying and editing user data
@@ -121,16 +122,18 @@ public class DataEditor {
private @NotNull String generateAdvancementPreview(@NotNull List advancementData) {
final StringJoiner joiner = new StringJoiner("\n");
+ final List advancementsToPreview = advancementData.stream().filter(dataItem ->
+ !dataItem.key.startsWith("minecraft:recipes/")).toList();
final int PREVIEW_SIZE = 8;
- for (int i = 0; i < advancementData.size(); i++) {
- joiner.add(advancementData.get(i).key);
+ for (int i = 0; i < advancementsToPreview.size(); i++) {
+ joiner.add(advancementsToPreview.get(i).key);
if (i >= PREVIEW_SIZE) {
break;
}
}
- final int remainingAdvancements = advancementData.size() - PREVIEW_SIZE;
+ final int remainingAdvancements = advancementsToPreview.size() - PREVIEW_SIZE;
if (remainingAdvancements > 0) {
- joiner.add(locales.getRawLocale("data_manager_advancement_preview_remaining",
+ joiner.add(locales.getRawLocale("data_manager_advancements_preview_remaining",
Integer.toString(remainingAdvancements)).orElse("+" + remainingAdvancements + "…"));
}
return joiner.toString();
diff --git a/common/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java b/common/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java
new file mode 100644
index 00000000..9aaaa6d0
--- /dev/null
+++ b/common/src/main/java/net/william278/husksync/migrator/LegacyMigrator.java
@@ -0,0 +1,43 @@
+package net.william278.husksync.migrator;
+
+import net.william278.husksync.HuskSync;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.CompletableFuture;
+
+//todo: implement this
+public class LegacyMigrator extends Migrator {
+
+ public LegacyMigrator(@NotNull HuskSync plugin) {
+ super(plugin);
+ }
+
+ @Override
+ public CompletableFuture start() {
+ return null;
+ }
+
+ @Override
+ public void handleConfigurationCommand(@NotNull String[] args) {
+
+ }
+
+ @NotNull
+ @Override
+ public String getIdentifier() {
+ return "legacy";
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "HuskSync v1.x --> v2.x";
+ }
+
+ @NotNull
+ @Override
+ public String getHelpMenu() {
+ return null;
+ }
+
+}
diff --git a/common/src/main/java/net/william278/husksync/migrator/Migrator.java b/common/src/main/java/net/william278/husksync/migrator/Migrator.java
new file mode 100644
index 00000000..65d24233
--- /dev/null
+++ b/common/src/main/java/net/william278/husksync/migrator/Migrator.java
@@ -0,0 +1,38 @@
+package net.william278.husksync.migrator;
+
+import net.william278.husksync.HuskSync;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.CompletableFuture;
+
+public abstract class Migrator {
+
+ protected final HuskSync plugin;
+
+ protected Migrator(@NotNull HuskSync plugin) {
+ this.plugin = plugin;
+ }
+
+ /**
+ * Start the migrator
+ *
+ * @return A future that will be completed when the migrator is done
+ */
+ public abstract CompletableFuture start();
+
+ /**
+ * Handle a command that sets migrator configuration parameters
+ * @param args The command arguments
+ */
+ public abstract void handleConfigurationCommand(@NotNull String[] args);
+
+ @NotNull
+ public abstract String getIdentifier();
+
+ @NotNull
+ public abstract String getName();
+
+ @NotNull
+ public abstract String getHelpMenu();
+
+}
diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml
index 83bcfa95..6ca00306 100644
--- a/common/src/main/resources/locales/en-gb.yml
+++ b/common/src/main/resources/locales/en-gb.yml
@@ -1,15 +1,10 @@
synchronisation_complete: '[⏵ Data synchronised!](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Reloaded config and message files.](#00fb9a)'
error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage: %1%](#ff7e5e)'
-error_invalid_player: '[Error:](#ff3300) [Could not find that player.](#ff7e5e)'
+error_invalid_player: '[Error:](#ff3300) [Could not find a player by that name.](#ff7e5e)'
error_no_permission: '[Error:](#ff3300) [You do not have permission to execute this command](#ff7e5e)'
-error_cannot_view_inventory_online: '[Error:](#ff3300) [You can''t access the inventory of an online player through HuskSync](#ff7e5e)'
-error_cannot_view_ender_chest_online: '[Error:](#ff3300) [You can''t access the ender chest of an online player through HuskSync](#ff7e5e)'
-error_cannot_view_own_inventory: '[Error:](#ff3300) [You can''t access your own inventory!](#ff7e5e)'
-error_cannot_view_own_ender_chest: '[Error:](#ff3300) [You can''t access your own ender chest!](#ff7e5e)'
-error_console_command_only: '[Error:](#ff3300) [That command can only be run through the %1% console](#ff7e5e)'
-error_no_servers_proxied: '[Error:](#ff3300) [Failed to process operation; no servers are online that have HuskSync installed. Please ensure HuskSync is installed on both the Proxy server and all servers you wish to synchronise data between.](#ff7e5e)'
-error_invalid_cluster: '[Error:](#ff3300) [Please specify the ID of a valid cluster.](#ff7e5e)'
+error_console_command_only: '[Error:](#ff3300) [That command can only be run through console](#ff7e5e)'
+error_in_game_command_only: 'Error: That command can only be used in-game.'
error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to display.](#ff7e5e)'
error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)'
inventory_viewer_menu_title: '&0%1%''s Inventory'
@@ -23,9 +18,9 @@ data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Save cause:\\n&7What
data_manager_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Health points) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hunger points) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Game mode)'
data_manager_advancements_statistics: '[⭐ Advancements: %1%](color=#ffc43b-#f5c962 show_text=&7%2%) [⌛ Play Time: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7⚠ Based on in-game statistics)'
data_manager_item_buttons: '[[🪣 Inventory…]](color=#a17b5f-#f5b98c show_text=&7Click to view run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Click to view run_command=/enderchest %1% %2%)\\n'
-data_manager_management_buttons: '[Manage:](gray) [[❌ Delete…]](#ff3300 show_text=&7Click to delete this user data run_command=/userdata restore %1% %2%) [[⏪ Restore…]](#00fb9a show_text=&7Click to restore this user data.\\nff3300&⚠ Warning: %1%''s current data will be overwritten! run_command=/userdata delete %1% %2%)\\n'
-data_manager_advancement_preview_remaining: '&7+%1% more…'
+data_manager_management_buttons: '[Manage:](gray) [[❌ Delete…]](#ff3300 show_text=&7Click to delete this snapshot of user data.\\nThis will not affect the user''s current data.\\nff3300&⚠ This cannot be undone! run_command=/userdata delete %1% %2%) [[⏪ Restore…]](#00fb9a show_text=&7Click to restore this user data.\\nThis will set the user''s data to this snapshot.\\nff3300&⚠ %1%''s current data will be overwritten! run_command=/userdata restore %1% %2%)\\n'
+data_manager_advancements_preview_remaining: '&7and %1% more…'
data_list_title: '[List of](#00fb9a) [%1%](#00fb9a bold show_text=&7UUID: %2%)[''s user data snapshots:](#00fb9a)\\n'
-data_list_item: '[%1%](gray run_command=/userdata view %6% %4%) [%2%](color=#ffc43b-#f5c962 show_text=&7Version timestamp&7When the data was saved run_command=/userdata view %6% %4%) [⚡ %3%](color=#62a9f5-#7ab8fa show_text=&7Version UUID:&7%4% run_command=/userdata view %6% %4%) [⚑ %5%](#23a825-#36f539 show_text=&7Save cause&7What caused the data to be saved run_command=/userdata view %6% %4%)'
+data_list_item: '[%1%](gray show_text=&7Snapshot %3% run_command=/userdata view %6% %4%) [%2%](color=#ffc43b-#f5c962 show_text=&7Version timestamp&7\\n&8When the data was saved run_command=/userdata view %6% %4%) [⚡ %3%](color=#62a9f5-#7ab8fa show_text=&7Version UUID:&7\\n&8%4% run_command=/userdata view %6% %4%) [⚑ %5%](#23a825-#36f539 show_text=&7Save cause\\n&8What caused the data to be saved run_command=/userdata view %6% %4%)'
data_deleted: '[❌ Successfully deleted user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\\n&7%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\\n&7%4%)'
data_restored: '[⏪ Successfully restored](#00fb9a) [%1%](#00fb9a show_text=&7Player UUID:\\n&7%2%)[''s current user data from snapshot](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\\n&7%4%)'
\ No newline at end of file