forked from public-mirrors/HuskSync
Remove use MPDB & internal migrator;
TODO: create migration plugin;feat/data-edit-commands
parent
32a5004fc7
commit
50af023d41
@ -1,100 +0,0 @@
|
|||||||
package me.william278.husksync.bukkit.migrator;
|
|
||||||
|
|
||||||
import me.william278.husksync.HuskSyncBukkit;
|
|
||||||
import me.william278.husksync.PlayerData;
|
|
||||||
import me.william278.husksync.bukkit.util.PlayerSetter;
|
|
||||||
import me.william278.husksync.bukkit.data.DataSerializer;
|
|
||||||
import me.william278.husksync.migrator.MPDBPlayerData;
|
|
||||||
import net.craftersland.data.bridge.PD;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.event.inventory.InventoryType;
|
|
||||||
import org.bukkit.inventory.Inventory;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class MPDBDeserializer {
|
|
||||||
|
|
||||||
private static final HuskSyncBukkit plugin = HuskSyncBukkit.getInstance();
|
|
||||||
|
|
||||||
// Instance of MySqlPlayerDataBridge
|
|
||||||
private static PD mySqlPlayerDataBridge;
|
|
||||||
|
|
||||||
public static void setMySqlPlayerDataBridge() {
|
|
||||||
mySqlPlayerDataBridge = (PD) Bukkit.getPluginManager().getPlugin("MySqlPlayerDataBridge");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert MySqlPlayerDataBridge ({@link MPDBPlayerData}) data to HuskSync's {@link PlayerData}
|
|
||||||
*
|
|
||||||
* @param mpdbPlayerData The {@link MPDBPlayerData} to convert
|
|
||||||
* @return The converted {@link PlayerData}
|
|
||||||
*/
|
|
||||||
public static PlayerData convertMPDBData(MPDBPlayerData mpdbPlayerData) {
|
|
||||||
PlayerData playerData = PlayerData.DEFAULT_PLAYER_DATA(mpdbPlayerData.playerUUID);
|
|
||||||
playerData.useDefaultData = false;
|
|
||||||
if (!HuskSyncBukkit.isMySqlPlayerDataBridgeInstalled) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "MySqlPlayerDataBridge is not installed, failed to serialize data!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the data
|
|
||||||
try {
|
|
||||||
// Set inventory contents
|
|
||||||
Inventory inventory = Bukkit.createInventory(null, InventoryType.PLAYER);
|
|
||||||
if (!mpdbPlayerData.inventoryData.isEmpty() && !mpdbPlayerData.inventoryData.equalsIgnoreCase("none")) {
|
|
||||||
PlayerSetter.setInventory(inventory, getItemStackArrayFromMPDBBase64String(mpdbPlayerData.inventoryData));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set armor (if there is data; MPDB stores empty data with literally the word "none". Obviously.)
|
|
||||||
int armorSlot = 36;
|
|
||||||
if (!mpdbPlayerData.armorData.isEmpty() && !mpdbPlayerData.armorData.equalsIgnoreCase("none")) {
|
|
||||||
ItemStack[] armorItems = getItemStackArrayFromMPDBBase64String(mpdbPlayerData.armorData);
|
|
||||||
for (ItemStack armorPiece : armorItems) {
|
|
||||||
if (armorPiece != null) {
|
|
||||||
inventory.setItem(armorSlot, armorPiece);
|
|
||||||
}
|
|
||||||
armorSlot++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now apply the contents and clear the temporary inventory variable
|
|
||||||
playerData.setSerializedInventory(DataSerializer.serializeInventory(inventory.getContents()));
|
|
||||||
|
|
||||||
// Set ender chest (again, if there is data)
|
|
||||||
ItemStack[] enderChestData;
|
|
||||||
if (!mpdbPlayerData.enderChestData.isEmpty() && !mpdbPlayerData.enderChestData.equalsIgnoreCase("none")) {
|
|
||||||
enderChestData = getItemStackArrayFromMPDBBase64String(mpdbPlayerData.enderChestData);
|
|
||||||
} else {
|
|
||||||
enderChestData = new ItemStack[0];
|
|
||||||
}
|
|
||||||
playerData.setSerializedEnderChest(DataSerializer.serializeInventory(enderChestData));
|
|
||||||
|
|
||||||
// Set experience
|
|
||||||
playerData.setExpLevel(mpdbPlayerData.expLevel);
|
|
||||||
playerData.setExpProgress(mpdbPlayerData.expProgress);
|
|
||||||
playerData.setTotalExperience(mpdbPlayerData.totalExperience);
|
|
||||||
} catch (Exception e) {
|
|
||||||
plugin.getLogger().log(Level.WARNING, "Failed to convert MPDB data to HuskSync's format!");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return playerData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an ItemStack array from a decoded base 64 string in MySQLPlayerDataBridge's format
|
|
||||||
*
|
|
||||||
* @param data The encoded ItemStack[] string from MySQLPlayerDataBridge
|
|
||||||
* @return The {@link ItemStack[]} array
|
|
||||||
* @throws InvocationTargetException If an error occurs during decoding
|
|
||||||
* @throws IllegalAccessException If an error occurs during decoding
|
|
||||||
*/
|
|
||||||
public static ItemStack[] getItemStackArrayFromMPDBBase64String(String data) throws InvocationTargetException, IllegalAccessException {
|
|
||||||
if (data.isEmpty()) {
|
|
||||||
return new ItemStack[0];
|
|
||||||
}
|
|
||||||
return mySqlPlayerDataBridge.getItemStackSerializer().fromBase64(data);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,302 +0,0 @@
|
|||||||
package me.william278.husksync.bungeecord.migrator;
|
|
||||||
|
|
||||||
import me.william278.husksync.HuskSyncBungeeCord;
|
|
||||||
import me.william278.husksync.PlayerData;
|
|
||||||
import me.william278.husksync.Settings;
|
|
||||||
import me.william278.husksync.bungeecord.data.DataManager;
|
|
||||||
import me.william278.husksync.bungeecord.data.sql.Database;
|
|
||||||
import me.william278.husksync.bungeecord.data.sql.MySQL;
|
|
||||||
import me.william278.husksync.migrator.MPDBPlayerData;
|
|
||||||
import me.william278.husksync.redis.RedisMessage;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class to handle migration of data from MySQLPlayerDataBridge
|
|
||||||
* <p>
|
|
||||||
* The migrator accesses and decodes MPDB's format directly.
|
|
||||||
* It does this by establishing a connection
|
|
||||||
*/
|
|
||||||
public class MPDBMigrator {
|
|
||||||
|
|
||||||
public static int migratedDataSent = 0;
|
|
||||||
public static int playersMigrated = 0;
|
|
||||||
|
|
||||||
private static final HuskSyncBungeeCord plugin = HuskSyncBungeeCord.getInstance();
|
|
||||||
|
|
||||||
public static HashMap<PlayerData, String> incomingPlayerData;
|
|
||||||
|
|
||||||
public static MigrationSettings migrationSettings = new MigrationSettings();
|
|
||||||
private static Settings.SynchronisationCluster targetCluster;
|
|
||||||
private static Database sourceDatabase;
|
|
||||||
|
|
||||||
private static HashSet<MPDBPlayerData> mpdbPlayerData;
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
if (ProxyServer.getInstance().getPlayers().size() > 0) {
|
|
||||||
plugin.getLogger().log(Level.WARNING, "Failed to start migration because there are players online. " +
|
|
||||||
"Your network has to be empty to migrate data for safety reasons.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int synchronisedServersWithMpdb = 0;
|
|
||||||
for (HuskSyncBungeeCord.Server server : HuskSyncBungeeCord.synchronisedServers) {
|
|
||||||
if (server.hasMySqlPlayerDataBridge()) {
|
|
||||||
synchronisedServersWithMpdb++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (synchronisedServersWithMpdb < 1) {
|
|
||||||
plugin.getLogger().log(Level.WARNING, "Failed to start migration because at least one Spigot server with both HuskSync and MySqlPlayerDataBridge installed is not online. " +
|
|
||||||
"Please start one Spigot server with HuskSync installed to begin migration.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
|
||||||
if (migrationSettings.targetCluster.equals(cluster.clusterId())) {
|
|
||||||
targetCluster = cluster;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (targetCluster == null) {
|
|
||||||
plugin.getLogger().log(Level.WARNING, "Failed to start migration because the target cluster could not be found. " +
|
|
||||||
"Please ensure the target cluster is correct, configured in the proxy config file, then try again");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
migratedDataSent = 0;
|
|
||||||
playersMigrated = 0;
|
|
||||||
mpdbPlayerData = new HashSet<>();
|
|
||||||
incomingPlayerData = new HashMap<>();
|
|
||||||
final MigrationSettings settings = migrationSettings;
|
|
||||||
|
|
||||||
// Get connection to source database
|
|
||||||
sourceDatabase = new MigratorMySQL(plugin, settings.sourceHost, settings.sourcePort,
|
|
||||||
settings.sourceDatabase, settings.sourceUsername, settings.sourcePassword, targetCluster);
|
|
||||||
sourceDatabase.load();
|
|
||||||
if (sourceDatabase.isInactive()) {
|
|
||||||
plugin.getLogger().log(Level.WARNING, "Failed to establish connection to the origin MySQL database. " +
|
|
||||||
"Please check you have input the correct connection details and try again.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> {
|
|
||||||
prepareTargetDatabase();
|
|
||||||
|
|
||||||
getInventoryData();
|
|
||||||
|
|
||||||
getEnderChestData();
|
|
||||||
|
|
||||||
getExperienceData();
|
|
||||||
|
|
||||||
sendEncodedData();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the new database out of current data
|
|
||||||
private void prepareTargetDatabase() {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Preparing target database...");
|
|
||||||
try (Connection connection = HuskSyncBungeeCord.getConnection(targetCluster.clusterId())) {
|
|
||||||
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM " + targetCluster.playerTableName() + ";")) {
|
|
||||||
statement.executeUpdate();
|
|
||||||
}
|
|
||||||
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM " + targetCluster.dataTableName() + ";")) {
|
|
||||||
statement.executeUpdate();
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "An exception occurred preparing the target database", e);
|
|
||||||
} finally {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Finished preparing target database!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getInventoryData() {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Getting inventory data from MySQLPlayerDataBridge...");
|
|
||||||
try (Connection connection = sourceDatabase.getConnection()) {
|
|
||||||
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + migrationSettings.inventoryDataTable + ";")) {
|
|
||||||
ResultSet resultSet = statement.executeQuery();
|
|
||||||
while (resultSet.next()) {
|
|
||||||
final UUID playerUUID = UUID.fromString(resultSet.getString("player_uuid"));
|
|
||||||
final String playerName = resultSet.getString("player_name");
|
|
||||||
|
|
||||||
MPDBPlayerData data = new MPDBPlayerData(playerUUID, playerName);
|
|
||||||
data.inventoryData = resultSet.getString("inventory");
|
|
||||||
data.armorData = resultSet.getString("armor");
|
|
||||||
|
|
||||||
mpdbPlayerData.add(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "An exception occurred getting inventory data", e);
|
|
||||||
} finally {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Finished getting inventory data from MySQLPlayerDataBridge");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getEnderChestData() {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Getting ender chest data from MySQLPlayerDataBridge...");
|
|
||||||
try (Connection connection = sourceDatabase.getConnection()) {
|
|
||||||
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + migrationSettings.enderChestDataTable + ";")) {
|
|
||||||
ResultSet resultSet = statement.executeQuery();
|
|
||||||
while (resultSet.next()) {
|
|
||||||
final UUID playerUUID = UUID.fromString(resultSet.getString("player_uuid"));
|
|
||||||
|
|
||||||
for (MPDBPlayerData data : mpdbPlayerData) {
|
|
||||||
if (data.playerUUID.equals(playerUUID)) {
|
|
||||||
data.enderChestData = resultSet.getString("enderchest");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "An exception occurred getting ender chest data", e);
|
|
||||||
} finally {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Finished getting ender chest data from MySQLPlayerDataBridge");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getExperienceData() {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Getting experience data from MySQLPlayerDataBridge...");
|
|
||||||
try (Connection connection = sourceDatabase.getConnection()) {
|
|
||||||
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + migrationSettings.expDataTable + ";")) {
|
|
||||||
ResultSet resultSet = statement.executeQuery();
|
|
||||||
while (resultSet.next()) {
|
|
||||||
final UUID playerUUID = UUID.fromString(resultSet.getString("player_uuid"));
|
|
||||||
|
|
||||||
for (MPDBPlayerData data : mpdbPlayerData) {
|
|
||||||
if (data.playerUUID.equals(playerUUID)) {
|
|
||||||
data.expLevel = resultSet.getInt("exp_lvl");
|
|
||||||
data.expProgress = resultSet.getFloat("exp");
|
|
||||||
data.totalExperience = resultSet.getInt("total_exp");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "An exception occurred getting experience data", e);
|
|
||||||
} finally {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Finished getting experience data from MySQLPlayerDataBridge");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendEncodedData() {
|
|
||||||
for (HuskSyncBungeeCord.Server processingServer : HuskSyncBungeeCord.synchronisedServers) {
|
|
||||||
if (processingServer.hasMySqlPlayerDataBridge()) {
|
|
||||||
for (MPDBPlayerData data : mpdbPlayerData) {
|
|
||||||
try {
|
|
||||||
new RedisMessage(RedisMessage.MessageType.DECODE_MPDB_DATA,
|
|
||||||
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, null, null),
|
|
||||||
processingServer.serverUUID().toString(),
|
|
||||||
RedisMessage.serialize(data))
|
|
||||||
.send();
|
|
||||||
migratedDataSent++;
|
|
||||||
} catch (IOException e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to serialize encoded MPDB data", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plugin.getLogger().log(Level.INFO, "Finished dispatching encoded data for " + migratedDataSent + " players; please wait for conversion to finish");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads all incoming decoded MPDB data to the cache and database
|
|
||||||
*
|
|
||||||
* @param dataToLoad HashMap of the {@link PlayerData} to player Usernames that will be loaded
|
|
||||||
*/
|
|
||||||
public static void loadIncomingData(HashMap<PlayerData, String> dataToLoad) {
|
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> {
|
|
||||||
int playersSaved = 0;
|
|
||||||
plugin.getLogger().log(Level.INFO, "Saving data for " + playersMigrated + " players...");
|
|
||||||
|
|
||||||
for (PlayerData playerData : dataToLoad.keySet()) {
|
|
||||||
String playerName = dataToLoad.get(playerData);
|
|
||||||
|
|
||||||
// Add the player to the MySQL table
|
|
||||||
DataManager.ensurePlayerExists(playerData.getPlayerUUID(), playerName);
|
|
||||||
|
|
||||||
// Update the data in the cache and SQL
|
|
||||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
|
||||||
DataManager.updatePlayerData(playerData, cluster);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
playersSaved++;
|
|
||||||
plugin.getLogger().log(Level.INFO, "Saved data for " + playersSaved + "/" + playersMigrated + " players");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as done when done
|
|
||||||
plugin.getLogger().log(Level.INFO, """
|
|
||||||
=== MySQLPlayerDataBridge Migration Wizard ==========
|
|
||||||
|
|
||||||
Migration complete!
|
|
||||||
|
|
||||||
Successfully migrated data for %1%/%2% players.
|
|
||||||
|
|
||||||
You should now uninstall MySQLPlayerDataBridge from
|
|
||||||
the rest of the Spigot servers, then restart them.
|
|
||||||
""".replaceAll("%1%", Integer.toString(MPDBMigrator.playersMigrated))
|
|
||||||
.replaceAll("%2%", Integer.toString(MPDBMigrator.migratedDataSent)));
|
|
||||||
sourceDatabase.close(); // Close source database
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used to hold settings for the MPDB migration
|
|
||||||
*/
|
|
||||||
public static class MigrationSettings {
|
|
||||||
public String sourceHost;
|
|
||||||
public int sourcePort;
|
|
||||||
public String sourceDatabase;
|
|
||||||
public String sourceUsername;
|
|
||||||
public String sourcePassword;
|
|
||||||
|
|
||||||
public String inventoryDataTable;
|
|
||||||
public String enderChestDataTable;
|
|
||||||
public String expDataTable;
|
|
||||||
|
|
||||||
public String targetCluster;
|
|
||||||
|
|
||||||
public MigrationSettings() {
|
|
||||||
sourceHost = "localhost";
|
|
||||||
sourcePort = 3306;
|
|
||||||
sourceDatabase = "mpdb";
|
|
||||||
sourceUsername = "root";
|
|
||||||
sourcePassword = "pa55w0rd";
|
|
||||||
|
|
||||||
targetCluster = "main";
|
|
||||||
|
|
||||||
inventoryDataTable = "mpdb_inventory";
|
|
||||||
enderChestDataTable = "mpdb_enderchest";
|
|
||||||
expDataTable = "mpdb_experience";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MySQL class used for importing data from MPDB
|
|
||||||
*/
|
|
||||||
public static class MigratorMySQL extends MySQL {
|
|
||||||
public MigratorMySQL(HuskSyncBungeeCord instance, String host, int port, String database, String username, String password, Settings.SynchronisationCluster cluster) {
|
|
||||||
super(instance, cluster);
|
|
||||||
super.host = host;
|
|
||||||
super.port = port;
|
|
||||||
super.database = database;
|
|
||||||
super.username = username;
|
|
||||||
super.password = password;
|
|
||||||
super.params = "?useSSL=false";
|
|
||||||
super.dataPoolName = super.dataPoolName + "Migrator";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package me.william278.husksync.migrator;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class that stores player data taken from MPDB's database, that can then be converted into HuskSync's format
|
|
||||||
*/
|
|
||||||
public class MPDBPlayerData implements Serializable {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Player information
|
|
||||||
*/
|
|
||||||
public final UUID playerUUID;
|
|
||||||
public final String playerName;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Inventory, ender chest and armor data
|
|
||||||
*/
|
|
||||||
public String inventoryData;
|
|
||||||
public String armorData;
|
|
||||||
public String enderChestData;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Experience data
|
|
||||||
*/
|
|
||||||
public int expLevel;
|
|
||||||
public float expProgress;
|
|
||||||
public int totalExperience;
|
|
||||||
|
|
||||||
public MPDBPlayerData(UUID playerUUID, String playerName) {
|
|
||||||
this.playerUUID = playerUUID;
|
|
||||||
this.playerName = playerName;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue