9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-28 11:09:11 +00:00

Add command and migrator

This commit is contained in:
William
2021-12-08 00:53:35 +00:00
parent e7822baa99
commit 635fefba3d
9 changed files with 586 additions and 181 deletions

View File

@@ -9,10 +9,11 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import me.william278.husksync.migrator.MPDBMigrator;
import me.william278.husksync.proxy.data.DataManager;
import me.william278.husksync.redis.RedisMessage;
import me.william278.husksync.velocity.VelocityUpdateChecker;
import me.william278.husksync.velocity.command.HuskSyncCommand;
import me.william278.husksync.velocity.command.VelocityCommand;
import me.william278.husksync.velocity.config.ConfigLoader;
import me.william278.husksync.velocity.config.ConfigManager;
import me.william278.husksync.velocity.listener.VelocityEventListener;
@@ -65,7 +66,7 @@ public class HuskSyncVelocity {
public static DataManager dataManager;
//public static MPDBMigrator mpdbMigrator;
public static MPDBMigrator mpdbMigrator;
private final Logger logger;
private final ProxyServer server;
@@ -108,13 +109,13 @@ public class HuskSyncVelocity {
ConfigManager.loadConfig();
// Load settings from config
ConfigLoader.loadSettings(ConfigManager.getConfig());
ConfigLoader.loadSettings(Objects.requireNonNull(ConfigManager.getConfig()));
// Load messages
ConfigManager.loadMessages();
// Load locales from messages
ConfigLoader.loadMessageStrings(ConfigManager.getMessages());
ConfigLoader.loadMessageStrings(Objects.requireNonNull(ConfigManager.getMessages()));
// Do update checker
if (Settings.automaticUpdateChecks) {
@@ -143,10 +144,10 @@ public class HuskSyncVelocity {
CommandMeta meta = commandManager.metaBuilder("husksync")
.aliases("hs")
.build();
commandManager.register(meta, new HuskSyncCommand());
commandManager.register(meta, new VelocityCommand());
// Prepare the migrator for use if needed
//todo migrator
mpdbMigrator = new MPDBMigrator(getVelocityLogger());
// Initialize bStats metrics
try {

View File

@@ -1,34 +0,0 @@
package me.william278.husksync.velocity.command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class HuskSyncCommand implements SimpleCommand {
/**
* Executes the command for the specified invocation.
*
* @param invocation the invocation context
*/
@Override
public void execute(Invocation invocation) {
final String[] args = invocation.arguments();
final CommandSource source = invocation.source();
}
/**
* Provides tab complete suggestions for the specified invocation.
*
* @param invocation the invocation context
* @return the tab complete suggestions
*/
@Override
public List<String> suggest(Invocation invocation) {
return new ArrayList<>();
}
}

View File

@@ -0,0 +1,418 @@
package me.william278.husksync.velocity.command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.Player;
import de.themoep.minedown.adventure.MineDown;
import me.william278.husksync.HuskSyncVelocity;
import me.william278.husksync.PlayerData;
import me.william278.husksync.Server;
import me.william278.husksync.Settings;
import me.william278.husksync.migrator.MPDBMigrator;
import me.william278.husksync.proxy.command.HuskSyncCommand;
import me.william278.husksync.redis.RedisMessage;
import me.william278.husksync.util.MessageManager;
import me.william278.husksync.velocity.VelocityUpdateChecker;
import me.william278.husksync.velocity.config.ConfigLoader;
import me.william278.husksync.velocity.config.ConfigManager;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class VelocityCommand implements SimpleCommand, HuskSyncCommand {
private static final HuskSyncVelocity plugin = HuskSyncVelocity.getInstance();
@Override
public void execute(Invocation invocation) {
final String[] args = invocation.arguments();
final CommandSource sender = invocation.source();
if (sender instanceof Player player) {
if (HuskSyncVelocity.synchronisedServers.size() == 0) {
player.sendMessage(new MineDown(MessageManager.getMessage("error_no_servers_proxied")).toComponent());
return;
}
if (args.length >= 1) {
switch (args[0].toLowerCase(Locale.ROOT)) {
case "about", "info" -> sendAboutInformation(player);
case "update" -> {
if (!player.hasPermission("husksync.command.inventory")) {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent());
return;
}
sender.sendMessage(new MineDown("[Checking for HuskSync updates...](gray)").toComponent());
plugin.getProxyServer().getScheduler().buildTask(plugin, () -> {
// Check Bukkit servers needing updates
int updatesNeeded = 0;
String bukkitBrand = "Spigot";
String bukkitVersion = "1.0";
for (Server server : HuskSyncVelocity.synchronisedServers) {
VelocityUpdateChecker updateChecker = new VelocityUpdateChecker(server.huskSyncVersion());
if (!updateChecker.isUpToDate()) {
updatesNeeded++;
bukkitBrand = server.serverBrand();
bukkitVersion = server.huskSyncVersion();
}
}
// Check Velocity servers needing updates and send message
VelocityUpdateChecker proxyUpdateChecker = new VelocityUpdateChecker(HuskSyncVelocity.VERSION);
if (proxyUpdateChecker.isUpToDate() && updatesNeeded == 0) {
sender.sendMessage(new MineDown("[HuskSync](#00fb9a bold) [| HuskSync is up-to-date, running Version " + proxyUpdateChecker.getLatestVersion() + "](#00fb9a)").toComponent());
} else {
sender.sendMessage(new MineDown("[HuskSync](#00fb9a bold) [| Your server(s) are not up-to-date:](#00fb9a)").toComponent());
if (!proxyUpdateChecker.isUpToDate()) {
sender.sendMessage(new MineDown("[•](white) [HuskSync on the Velocity proxy is outdated (Latest: " + proxyUpdateChecker.getLatestVersion() + ", Running: " + proxyUpdateChecker.getCurrentVersion() + ")](#00fb9a)").toComponent());
}
if (updatesNeeded > 0) {
sender.sendMessage(new MineDown("[•](white) [HuskSync on " + updatesNeeded + " connected " + bukkitBrand + " server(s) are outdated (Latest: " + proxyUpdateChecker.getLatestVersion() + ", Running: " + bukkitVersion + ")](#00fb9a)").toComponent());
}
sender.sendMessage(new MineDown("[•](white) [Download links:](#00fb9a) [[⏩ Spigot]](gray open_url=https://www.spigotmc.org/resources/husktowns.92672/updates) [•](#262626) [[⏩ Polymart]](gray open_url=https://polymart.org/resource/husktowns.1056/updates)").toComponent());
}
});
}
case "invsee", "openinv", "inventory" -> {
if (!player.hasPermission("husksync.command.inventory")) {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent());
return;
}
String clusterId;
if (Settings.clusters.size() > 1) {
if (args.length == 3) {
clusterId = args[2];
} else {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_cluster")).toComponent());
return;
}
} else {
clusterId = "main";
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
clusterId = cluster.clusterId();
break;
}
}
if (args.length == 2 || args.length == 3) {
String playerName = args[1];
openInventory(player, playerName, clusterId);
} else {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_syntax").replaceAll("%1%",
"/husksync invsee <player>")).toComponent());
}
}
case "echest", "enderchest" -> {
if (!player.hasPermission("husksync.command.ender_chest")) {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent());
return;
}
String clusterId;
if (Settings.clusters.size() > 1) {
if (args.length == 3) {
clusterId = args[2];
} else {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_cluster")).toComponent());
return;
}
} else {
clusterId = "main";
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
clusterId = cluster.clusterId();
break;
}
}
if (args.length == 2 || args.length == 3) {
String playerName = args[1];
openEnderChest(player, playerName, clusterId);
} else {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_syntax")
.replaceAll("%1%", "/husksync echest <player>")).toComponent());
}
}
case "migrate" -> {
if (!player.hasPermission("husksync.command.admin")) {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent());
return;
}
sender.sendMessage(new MineDown(MessageManager.getMessage("error_console_command_only")
.replaceAll("%1%", "Velocity")).toComponent());
}
case "status" -> {
if (!player.hasPermission("husksync.command.admin")) {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent());
return;
}
int playerDataSize = 0;
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
playerDataSize += HuskSyncVelocity.dataManager.playerDataCache.get(cluster).playerData.size();
}
sender.sendMessage(new MineDown(MessageManager.PLUGIN_STATUS.toString()
.replaceAll("%1%", String.valueOf(HuskSyncVelocity.synchronisedServers.size()))
.replaceAll("%2%", String.valueOf(playerDataSize))).toComponent());
}
case "reload" -> {
if (!player.hasPermission("husksync.command.admin")) {
sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent());
return;
}
ConfigManager.loadConfig();
ConfigLoader.loadSettings(Objects.requireNonNull(ConfigManager.getConfig()));
ConfigManager.loadMessages();
ConfigLoader.loadMessageStrings(Objects.requireNonNull(ConfigManager.getMessages()));
// Send reload request to all bukkit servers
try {
new RedisMessage(RedisMessage.MessageType.RELOAD_CONFIG,
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, null, null),
"reload")
.send();
} catch (IOException e) {
plugin.getVelocityLogger().log(Level.WARNING, "Failed to serialize reload notification message data");
}
sender.sendMessage(new MineDown(MessageManager.getMessage("reload_complete")).toComponent());
}
default -> sender.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_syntax").replaceAll("%1%",
"/husksync <about/status/invsee/echest>")).toComponent());
}
} else {
sendAboutInformation(player);
}
} else {
// Database migration wizard
if (args.length >= 1) {
if (args[0].equalsIgnoreCase("migrate")) {
MPDBMigrator migrator = HuskSyncVelocity.mpdbMigrator;
if (args.length == 1) {
sender.sendMessage(new MineDown(
"""
=== MySQLPlayerDataBridge Migration Wizard ==========
This will migrate data from the MySQLPlayerDataBridge
plugin to HuskSync.
Data that will be migrated:
- Inventories
- Ender Chests
- Experience points
Other non-vital data, such as current health, hunger
& potion effects will not be migrated to ensure that
migration does not take an excessive amount of time.
To do this, you need to have MySqlPlayerDataBridge
and HuskSync installed on one Spigot server as well
as HuskSync installed on the proxy (which you have)
>To proceed, type: husksync migrate setup""").toComponent());
} else {
switch (args[1].toLowerCase()) {
case "setup" -> sender.sendMessage(new MineDown(
"""
=== MySQLPlayerDataBridge Migration Wizard ==========
The following database settings will be used.
Please make sure they match the correct settings to
access your MySQLPlayerDataBridge Data
sourceHost: %1%
sourcePort: %2%
sourceDatabase: %3%
sourceUsername: %4%
sourcePassword: %5%
sourceInventoryTableName: %6%
sourceEnderChestTableName: %7%
sourceExperienceTableName: %8%
targetCluster: %9%
To change a setting, type:
husksync migrate setting <settingName> <value>
Please ensure no players are logged in to the network
and that at least one Spigot server is online with
both HuskSync AND MySqlPlayerDataBridge installed AND
that the server has been configured with the correct
Redis credentials.
Warning: Data will be saved to your configured data
source, which is currently a %10% database.
Please make sure you are happy with this, or stop
the proxy server and edit this in config.yml
Warning: Migration will overwrite any current data
saved by HuskSync. It will not, however, delete any
data from the source MySQLPlayerDataBridge database.
>When done, type: husksync migrate start"""
.replaceAll("%1%", migrator.migrationSettings.sourceHost)
.replaceAll("%2%", String.valueOf(migrator.migrationSettings.sourcePort))
.replaceAll("%3%", migrator.migrationSettings.sourceDatabase)
.replaceAll("%4%", migrator.migrationSettings.sourceUsername)
.replaceAll("%5%", migrator.migrationSettings.sourcePassword)
.replaceAll("%6%", migrator.migrationSettings.inventoryDataTable)
.replaceAll("%7%", migrator.migrationSettings.enderChestDataTable)
.replaceAll("%8%", migrator.migrationSettings.expDataTable)
.replaceAll("%9%", migrator.migrationSettings.targetCluster)
.replaceAll("%10%", Settings.dataStorageType.toString())
).toComponent());
case "setting" -> {
if (args.length == 4) {
String value = args[3];
switch (args[2]) {
case "sourceHost", "host" -> migrator.migrationSettings.sourceHost = value;
case "sourcePort", "port" -> {
try {
migrator.migrationSettings.sourcePort = Integer.parseInt(value);
} catch (NumberFormatException e) {
sender.sendMessage(new MineDown("Error: Invalid value; port must be a number").toComponent());
return;
}
}
case "sourceDatabase", "database" -> migrator.migrationSettings.sourceDatabase = value;
case "sourceUsername", "username" -> migrator.migrationSettings.sourceUsername = value;
case "sourcePassword", "password" -> migrator.migrationSettings.sourcePassword = value;
case "sourceInventoryTableName", "inventoryTableName", "inventoryTable" -> migrator.migrationSettings.inventoryDataTable = value;
case "sourceEnderChestTableName", "enderChestTableName", "enderChestTable" -> migrator.migrationSettings.enderChestDataTable = value;
case "sourceExperienceTableName", "experienceTableName", "experienceTable" -> migrator.migrationSettings.expDataTable = value;
case "targetCluster", "cluster" -> migrator.migrationSettings.targetCluster = value;
default -> {
sender.sendMessage(new MineDown("Error: Invalid setting; please use \"husksync migrate setup\" to view a list").toComponent());
return;
}
}
sender.sendMessage(new MineDown("Successfully updated setting: \"" + args[2] + "\" --> \"" + value + "\"").toComponent());
} else {
sender.sendMessage(new MineDown("Error: Invalid usage. Syntax: husksync migrate setting <settingName> <value>").toComponent());
}
}
case "start" -> {
sender.sendMessage(new MineDown("Starting MySQLPlayerDataBridge migration!...").toComponent());
// If the migrator is ready, execute the migration asynchronously
if (HuskSyncVelocity.mpdbMigrator.readyToMigrate(plugin.getProxyServer().getPlayerCount(),
HuskSyncVelocity.synchronisedServers)) {
plugin.getProxyServer().getScheduler().buildTask(plugin, () ->
HuskSyncVelocity.mpdbMigrator.executeMigrationOperations(HuskSyncVelocity.dataManager,
HuskSyncVelocity.synchronisedServers));
}
}
default -> sender.sendMessage(new MineDown("Error: Invalid argument for migration. Use \"husksync migrate\" to start the process").toComponent());
}
}
return;
}
}
sender.sendMessage(new MineDown("Error: Invalid syntax. Usage: husksync migrate <args>").toComponent());
}
}
// View the inventory of a player specified by their name
private void openInventory(Player viewer, String targetPlayerName, String clusterId) {
if (viewer.getUsername().equalsIgnoreCase(targetPlayerName)) {
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_cannot_view_own_ender_chest")).toComponent());
return;
}
if (plugin.getProxyServer().getPlayer(targetPlayerName).isPresent()) {
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_cannot_view_inventory_online")).toComponent());
return;
}
plugin.getProxyServer().getScheduler().buildTask(plugin, () -> {
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
if (!cluster.clusterId().equals(clusterId)) continue;
PlayerData playerData = HuskSyncVelocity.dataManager.getPlayerDataByName(targetPlayerName, cluster.clusterId());
if (playerData == null) {
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_player")).toComponent());
return;
}
try {
new RedisMessage(RedisMessage.MessageType.OPEN_INVENTORY,
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, viewer.getUniqueId(), null),
targetPlayerName, RedisMessage.serialize(playerData))
.send();
viewer.sendMessage(new MineDown(MessageManager.getMessage("viewing_inventory_of").replaceAll("%1%",
targetPlayerName)).toComponent());
} catch (IOException e) {
plugin.getVelocityLogger().log(Level.WARNING, "Failed to serialize inventory-see player data", e);
}
return;
}
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_cluster")).toComponent());
});
}
// View the ender chest of a player specified by their name
public void openEnderChest(Player viewer, String targetPlayerName, String clusterId) {
if (viewer.getUsername().equalsIgnoreCase(targetPlayerName)) {
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_cannot_view_own_ender_chest")).toComponent());
return;
}
if (plugin.getProxyServer().getPlayer(targetPlayerName).isPresent()) {
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_cannot_view_ender_chest_online")).toComponent());
return;
}
plugin.getProxyServer().getScheduler().buildTask(plugin, () -> {
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
if (!cluster.clusterId().equals(clusterId)) continue;
PlayerData playerData = HuskSyncVelocity.dataManager.getPlayerDataByName(targetPlayerName, cluster.clusterId());
if (playerData == null) {
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_player")).toComponent());
return;
}
try {
new RedisMessage(RedisMessage.MessageType.OPEN_ENDER_CHEST,
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, viewer.getUniqueId(), null),
targetPlayerName, RedisMessage.serialize(playerData))
.send();
viewer.sendMessage(new MineDown(MessageManager.getMessage("viewing_ender_chest_of").replaceAll("%1%",
targetPlayerName)).toComponent());
} catch (IOException e) {
plugin.getVelocityLogger().log(Level.WARNING, "Failed to serialize inventory-see player data", e);
}
return;
}
viewer.sendMessage(new MineDown(MessageManager.getMessage("error_invalid_cluster")).toComponent());
});
}
/**
* Send information about the plugin
*
* @param player The player to send it to
*/
private void sendAboutInformation(Player player) {
try {
new RedisMessage(RedisMessage.MessageType.SEND_PLUGIN_INFORMATION,
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, player.getUniqueId(), null),
"Velocity", HuskSyncVelocity.VERSION).send();
} catch (IOException e) {
plugin.getVelocityLogger().log(Level.WARNING, "Failed to serialize plugin information to send", e);
}
}
@Override
public List<String> suggest(Invocation invocation) {
final CommandSource sender = invocation.source();
final String[] args = invocation.arguments();
if (sender instanceof Player player) {
if (args.length == 1) {
final ArrayList<String> subCommands = new ArrayList<>();
for (SubCommand subCommand : SUB_COMMANDS) {
if (subCommand.permission() != null) {
if (!player.hasPermission(subCommand.permission())) {
continue;
}
}
subCommands.add(subCommand.command());
}
// Automatically filter the sub commands' order in tab completion by what the player has typed
return subCommands.stream().filter(val -> val.startsWith(args[0]))
.sorted().collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}
return Collections.emptyList();
}
}

View File

@@ -3,7 +3,6 @@ package me.william278.husksync.velocity.config;
import me.william278.husksync.HuskSyncVelocity;
import me.william278.husksync.Settings;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import java.io.File;