forked from public-mirrors/HuskSync
Start work on Velocity support
parent
32a5004fc7
commit
725bf2c315
@ -0,0 +1,33 @@
|
||||
package me.william278.husksync.bungeecord.util;
|
||||
|
||||
import me.william278.husksync.util.Logger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public record BungeeLogger(java.util.logging.Logger parent) implements Logger {
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message, Exception e) {
|
||||
parent.log(level, message, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
parent.log(level, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String message) {
|
||||
parent.info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
parent.severe(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void config(String message) {
|
||||
parent.config(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package me.william278.husksync;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A record representing a server synchronised on the network and whether it has MySqlPlayerDataBridge installed
|
||||
*/
|
||||
public record Server(UUID serverUUID, boolean hasMySqlPlayerDataBridge, String huskSyncVersion, String serverBrand,
|
||||
String clusterId) {
|
||||
}
|
@ -1,21 +1,21 @@
|
||||
package me.william278.husksync.bungeecord.data.sql;
|
||||
package me.william278.husksync.proxy.data.sql;
|
||||
|
||||
import me.william278.husksync.HuskSyncBungeeCord;
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.util.Logger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public abstract class Database {
|
||||
protected HuskSyncBungeeCord plugin;
|
||||
|
||||
public String dataPoolName;
|
||||
public Settings.SynchronisationCluster cluster;
|
||||
public final Logger logger;
|
||||
|
||||
public Database(HuskSyncBungeeCord instance, Settings.SynchronisationCluster cluster) {
|
||||
this.plugin = instance;
|
||||
public Database(Settings.SynchronisationCluster cluster, Logger logger) {
|
||||
this.cluster = cluster;
|
||||
this.dataPoolName = cluster != null ? "HuskSyncHikariPool-" + cluster.clusterId() : "HuskSyncMigratorPool";
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public abstract Connection getConnection() throws SQLException;
|
@ -0,0 +1,19 @@
|
||||
package me.william278.husksync.util;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Logger interface to allow for implementation of different logger platforms used by Bungee and Velocity
|
||||
*/
|
||||
public interface Logger {
|
||||
|
||||
void log(Level level, String message, Exception e);
|
||||
|
||||
void log(Level level, String message);
|
||||
|
||||
void info(String message);
|
||||
|
||||
void severe(String message);
|
||||
|
||||
void config(String message);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
dependencies {
|
||||
compileOnly project(':common')
|
||||
implementation project(path: ':common', configuration: 'shadow')
|
||||
|
||||
compileOnly 'redis.clients:jedis:3.7.0'
|
||||
implementation 'org.bstats:bstats-velocity:2.2.1'
|
||||
implementation 'com.zaxxer:HikariCP:5.0.0'
|
||||
implementation 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT'
|
||||
|
||||
compileOnly 'com.velocitypowered:velocity-api:3.1.0'
|
||||
annotationProcessor 'com.velocitypowered:velocity-api:3.1.0'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
relocate 'com.zaxxer', 'me.William278.husksync.libraries.hikari'
|
||||
relocate 'org.bstats', 'me.William278.husksync.libraries.plan'
|
||||
relocate 'de.themoep', 'me.William278.husksync.libraries.minedown'
|
||||
}
|
||||
|
||||
tasks.register('prepareKotlinBuildScriptModel'){}
|
@ -0,0 +1,187 @@
|
||||
package me.william278.husksync;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.command.CommandManager;
|
||||
import com.velocitypowered.api.command.CommandMeta;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
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.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.config.ConfigLoader;
|
||||
import me.william278.husksync.velocity.config.ConfigManager;
|
||||
import me.william278.husksync.velocity.listener.VelocityEventListener;
|
||||
import me.william278.husksync.velocity.listener.VelocityRedisListener;
|
||||
import me.william278.husksync.velocity.util.VelocityLogger;
|
||||
import org.bstats.velocity.Metrics;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static me.william278.husksync.HuskSyncVelocity.VERSION;
|
||||
|
||||
@Plugin(
|
||||
id = "velocity",
|
||||
name = "HuskSync",
|
||||
version = VERSION,
|
||||
description = "HuskSync for velocity",
|
||||
authors = {"William278"}
|
||||
)
|
||||
public class HuskSyncVelocity {
|
||||
|
||||
// Plugin version
|
||||
public static final String VERSION = "1.2-dev";
|
||||
|
||||
// Velocity bStats ID (different from Bukkit and BungeeCord)
|
||||
private static final int METRICS_ID = 13489;
|
||||
private final Metrics.Factory metricsFactory;
|
||||
|
||||
private static HuskSyncVelocity instance;
|
||||
|
||||
public static HuskSyncVelocity getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Whether the plugin is ready to accept redis messages
|
||||
public static boolean readyForRedis = false;
|
||||
|
||||
// Whether the plugin is in the process of disabling and should skip responding to handshake confirmations
|
||||
public static boolean isDisabling = false;
|
||||
|
||||
/**
|
||||
* Set of all the {@link Server}s that have completed the synchronisation handshake with HuskSync on the proxy
|
||||
*/
|
||||
public static HashSet<Server> synchronisedServers;
|
||||
|
||||
public static DataManager dataManager;
|
||||
|
||||
//public static MPDBMigrator mpdbMigrator;
|
||||
|
||||
private final Logger logger;
|
||||
private final ProxyServer server;
|
||||
private final Path dataDirectory;
|
||||
|
||||
// Get the data folder
|
||||
public File getDataFolder() {
|
||||
return dataDirectory.toFile();
|
||||
}
|
||||
|
||||
// Get the proxy server
|
||||
public ProxyServer getProxyServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
// Velocity logger handling
|
||||
private VelocityLogger velocityLogger;
|
||||
|
||||
public VelocityLogger getVelocityLogger() {
|
||||
return velocityLogger;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public HuskSyncVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory, Metrics.Factory metricsFactory) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
this.dataDirectory = dataDirectory;
|
||||
this.metricsFactory = metricsFactory;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
// Set instance
|
||||
instance = this;
|
||||
|
||||
// Setup logger
|
||||
velocityLogger = new VelocityLogger(logger);
|
||||
|
||||
// Load config
|
||||
ConfigManager.loadConfig();
|
||||
|
||||
// Load settings from config
|
||||
ConfigLoader.loadSettings(ConfigManager.getConfig());
|
||||
|
||||
// Load messages
|
||||
ConfigManager.loadMessages();
|
||||
|
||||
// Load locales from messages
|
||||
ConfigLoader.loadMessageStrings(ConfigManager.getMessages());
|
||||
|
||||
// Do update checker
|
||||
if (Settings.automaticUpdateChecks) {
|
||||
new VelocityUpdateChecker(VERSION).logToConsole();
|
||||
}
|
||||
|
||||
// Setup data manager
|
||||
dataManager = new DataManager(getVelocityLogger(), getDataFolder());
|
||||
|
||||
// Setup player data cache
|
||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
||||
dataManager.playerDataCache.put(cluster, new DataManager.PlayerDataCache());
|
||||
}
|
||||
|
||||
// Initialize the redis listener
|
||||
if (!new VelocityRedisListener().isActiveAndEnabled) {
|
||||
getVelocityLogger().severe("Failed to initialize Redis; HuskSync will now abort loading itself (Velocity) v" + VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register listener
|
||||
server.getEventManager().register(this, new VelocityEventListener());
|
||||
|
||||
// Register command
|
||||
CommandManager commandManager = getProxyServer().getCommandManager();
|
||||
CommandMeta meta = commandManager.metaBuilder("husksync")
|
||||
.aliases("hs")
|
||||
.build();
|
||||
commandManager.register(meta, new HuskSyncCommand());
|
||||
|
||||
// Prepare the migrator for use if needed
|
||||
//todo migrator
|
||||
|
||||
// Initialize bStats metrics
|
||||
try {
|
||||
metricsFactory.make(this, METRICS_ID);
|
||||
} catch (Exception e) {
|
||||
getVelocityLogger().info("Skipped metrics initialization");
|
||||
}
|
||||
|
||||
// Log to console
|
||||
getVelocityLogger().info("Enabled HuskSync (Velocity) v" + VERSION);
|
||||
|
||||
// Mark as ready for redis message processing
|
||||
readyForRedis = true;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyShutdown(ProxyShutdownEvent event) {
|
||||
// Plugin shutdown logic
|
||||
isDisabling = true;
|
||||
|
||||
// Send terminating handshake message
|
||||
for (Server server : synchronisedServers) {
|
||||
try {
|
||||
new RedisMessage(RedisMessage.MessageType.TERMINATE_HANDSHAKE,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, null, server.clusterId()),
|
||||
server.serverUUID().toString(),
|
||||
"Velocity").send();
|
||||
} catch (IOException e) {
|
||||
getVelocityLogger().log(Level.SEVERE, "Failed to serialize Redis message for handshake termination", e);
|
||||
}
|
||||
}
|
||||
|
||||
dataManager.closeDatabases();
|
||||
|
||||
// Log to console
|
||||
getVelocityLogger().info("Disabled HuskSync (Velocity) v" + VERSION);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package me.william278.husksync.velocity;
|
||||
|
||||
import me.william278.husksync.HuskSyncVelocity;
|
||||
import me.william278.husksync.util.UpdateChecker;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class VelocityUpdateChecker extends UpdateChecker {
|
||||
|
||||
private static final HuskSyncVelocity plugin = HuskSyncVelocity.getInstance();
|
||||
|
||||
public VelocityUpdateChecker(String versionToCheck) {
|
||||
super(versionToCheck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
plugin.getVelocityLogger().log(level, message);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
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<>();
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package me.william278.husksync.velocity.config;
|
||||
|
||||
import me.william278.husksync.HuskSyncVelocity;
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.util.MessageManager;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ConfigLoader {
|
||||
|
||||
private static ConfigurationNode copyDefaults(ConfigurationNode configRoot) {
|
||||
// Get the config version and update if needed
|
||||
String configVersion = getConfigString(configRoot, "1.0", "config_file_version");
|
||||
if (configVersion.contains("-dev")) {
|
||||
configVersion = configVersion.replaceAll("-dev", "");
|
||||
}
|
||||
if (!configVersion.equals(HuskSyncVelocity.VERSION)) {
|
||||
if (configVersion.equalsIgnoreCase("1.0")) {
|
||||
configRoot.getNode("check_for_updates").setValue(true);
|
||||
}
|
||||
if (configVersion.equalsIgnoreCase("1.0") || configVersion.equalsIgnoreCase("1.0.1") || configVersion.equalsIgnoreCase("1.0.2") || configVersion.equalsIgnoreCase("1.0.3")) {
|
||||
configRoot.getNode("clusters.main.player_table").setValue("husksync_players");
|
||||
configRoot.getNode("clusters.main.data_table").setValue("husksync_data");
|
||||
}
|
||||
configRoot.getNode("config_file_version").setValue(HuskSyncVelocity.VERSION);
|
||||
}
|
||||
// Save the config back
|
||||
ConfigManager.saveConfig(configRoot);
|
||||
return configRoot;
|
||||
}
|
||||
|
||||
private static String getConfigString(ConfigurationNode rootNode, String defaultValue, String... nodePath) {
|
||||
return !rootNode.getNode(nodePath).isVirtual() ? rootNode.getNode(nodePath).getString() : defaultValue;
|
||||
}
|
||||
|
||||
private static boolean getConfigBoolean(ConfigurationNode rootNode, boolean defaultValue, String... nodePath) {
|
||||
return !rootNode.getNode(nodePath).isVirtual() ? rootNode.getNode(nodePath).getBoolean() : defaultValue;
|
||||
}
|
||||
|
||||
private static int getConfigInt(ConfigurationNode rootNode, int defaultValue, String... nodePath) {
|
||||
return !rootNode.getNode(nodePath).isVirtual() ? rootNode.getNode(nodePath).getInt() : defaultValue;
|
||||
}
|
||||
|
||||
private static long getConfigLong(ConfigurationNode rootNode, long defaultValue, String... nodePath) {
|
||||
return !rootNode.getNode(nodePath).isVirtual() ? rootNode.getNode(nodePath).getLong() : defaultValue;
|
||||
}
|
||||
|
||||
public static void loadSettings(ConfigurationNode loadedConfig) throws IllegalArgumentException {
|
||||
ConfigurationNode config = copyDefaults(loadedConfig);
|
||||
|
||||
Settings.language = getConfigString(config, "en-gb", "language");
|
||||
|
||||
Settings.serverType = Settings.ServerType.PROXY;
|
||||
Settings.automaticUpdateChecks = getConfigBoolean(config, true, "check_for_updates");
|
||||
Settings.redisHost = getConfigString(config, "localhost", "redis_settings", "host");
|
||||
Settings.redisPort = getConfigInt(config, 6379, "redis_settings", "port");
|
||||
Settings.redisPassword = getConfigString(config, "", "redis_settings", "password");
|
||||
|
||||
Settings.dataStorageType = Settings.DataStorageType.valueOf(getConfigString(config, "sqlite", "data_storage_settings", "database_type").toUpperCase());
|
||||
if (Settings.dataStorageType == Settings.DataStorageType.MYSQL) {
|
||||
Settings.mySQLHost = getConfigString(config, "localhost", "data_storage_settings", "mysql_settings", "host");
|
||||
Settings.mySQLPort = getConfigInt(config, 3306, "data_storage_settings", "mysql_settings", "port");
|
||||
Settings.mySQLDatabase = getConfigString(config, "HuskSync", "data_storage_settings", "mysql_settings", "database");
|
||||
Settings.mySQLUsername = getConfigString(config, "root", "data_storage_settings", "mysql_settings", "username");
|
||||
Settings.mySQLPassword = getConfigString(config, "pa55w0rd", "data_storage_settings", "mysql_settings", "password");
|
||||
Settings.mySQLParams = getConfigString(config, "?autoReconnect=true&useSSL=false", "data_storage_settings", "mysql_settings", "params");
|
||||
}
|
||||
|
||||
Settings.hikariMaximumPoolSize = getConfigInt(config, 10, "data_storage_settings", "hikari_pool_settings", "maximum_pool_size");
|
||||
Settings.hikariMinimumIdle = getConfigInt(config, 10, "data_storage_settings", "hikari_pool_settings", "minimum_idle");
|
||||
Settings.hikariMaximumLifetime = getConfigLong(config, 1800000, "data_storage_settings", "hikari_pool_settings", "maximum_lifetime");
|
||||
Settings.hikariKeepAliveTime = getConfigLong(config, 0, "data_storage_settings", "hikari_pool_settings", "keepalive_time");
|
||||
Settings.hikariConnectionTimeOut = getConfigLong(config, 5000, "data_storage_settings", "hikari_pool_settings", "connection_timeout");
|
||||
|
||||
// Read cluster data
|
||||
ConfigurationNode clusterSection = config.getNode("clusters");
|
||||
final String settingDatabaseName = Settings.mySQLDatabase != null ? Settings.mySQLDatabase : "HuskSync";
|
||||
for (ConfigurationNode cluster : clusterSection.getChildrenList()) {
|
||||
final String clusterId = (String) cluster.getKey();
|
||||
final String playerTableName = getConfigString(config, "husksync_players", "clusters", clusterId, "player_table");
|
||||
final String dataTableName = getConfigString(config, "husksync_data", "clusters", clusterId, "data_table");
|
||||
final String databaseName = getConfigString(config, settingDatabaseName, "clusters", clusterId, "database");
|
||||
Settings.clusters.add(new Settings.SynchronisationCluster(clusterId, databaseName, playerTableName, dataTableName));
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadMessageStrings(ConfigurationNode config) {
|
||||
final HashMap<String, String> messages = new HashMap<>();
|
||||
for (ConfigurationNode message : config.getChildrenList()) {
|
||||
final String messageId = (String) message.getKey();
|
||||
messages.put(messageId, getConfigString(config, "", messageId));
|
||||
}
|
||||
MessageManager.setMessages(messages);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
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;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private static final HuskSyncVelocity plugin = HuskSyncVelocity.getInstance();
|
||||
|
||||
public static void loadConfig() {
|
||||
try {
|
||||
if (!plugin.getDataFolder().exists()) {
|
||||
if (plugin.getDataFolder().mkdir()) {
|
||||
plugin.getVelocityLogger().info("Created HuskSync data folder");
|
||||
}
|
||||
}
|
||||
File configFile = new File(plugin.getDataFolder(), "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
Files.copy(Objects.requireNonNull(plugin.getClass().getResourceAsStream("proxy-config.yml")), configFile.toPath());
|
||||
plugin.getVelocityLogger().info("Created HuskSync config file");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getVelocityLogger().log(Level.CONFIG, "An exception occurred loading the configuration file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveConfig(ConfigurationNode rootNode) {
|
||||
try {
|
||||
getConfigLoader().save(rootNode);
|
||||
} catch (IOException e) {
|
||||
plugin.getVelocityLogger().log(Level.CONFIG, "An exception occurred loading the configuration file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadMessages() {
|
||||
try {
|
||||
if (!plugin.getDataFolder().exists()) {
|
||||
if (plugin.getDataFolder().mkdir()) {
|
||||
plugin.getVelocityLogger().info("Created HuskSync data folder");
|
||||
}
|
||||
}
|
||||
File messagesFile = new File(plugin.getDataFolder(), "messages_" + Settings.language + ".yml");
|
||||
if (!messagesFile.exists()) {
|
||||
Files.copy(Objects.requireNonNull(plugin.getClass().getResourceAsStream("languages/" + Settings.language + ".yml")),
|
||||
messagesFile.toPath());
|
||||
plugin.getVelocityLogger().info("Created HuskSync messages file");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getVelocityLogger().log(Level.CONFIG, "An exception occurred loading the messages file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static YAMLConfigurationLoader getConfigLoader() {
|
||||
File configFile = new File(plugin.getDataFolder(), "config.yml");
|
||||
return YAMLConfigurationLoader.builder()
|
||||
.setPath(configFile.toPath())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationNode getConfig() {
|
||||
try {
|
||||
return getConfigLoader().load();
|
||||
} catch (IOException e) {
|
||||
plugin.getVelocityLogger().log(Level.CONFIG, "An IOException has occurred loading the plugin config.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigurationNode getMessages() {
|
||||
try {
|
||||
File configFile = new File(plugin.getDataFolder(), "messages_" + Settings.language + ".yml");
|
||||
return YAMLConfigurationLoader.builder()
|
||||
.setPath(configFile.toPath())
|
||||
.build()
|
||||
.load();
|
||||
} catch (IOException e) {
|
||||
plugin.getVelocityLogger().log(Level.CONFIG, "An IOException has occurred loading the plugin messages.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package me.william278.husksync.velocity.listener;
|
||||
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PostLoginEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import me.william278.husksync.HuskSyncVelocity;
|
||||
import me.william278.husksync.PlayerData;
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.redis.RedisMessage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class VelocityEventListener {
|
||||
|
||||
private static final HuskSyncVelocity plugin = HuskSyncVelocity.getInstance();
|
||||
|
||||
@Subscribe
|
||||
public void onPostLogin(PostLoginEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
plugin.getProxyServer().getScheduler().buildTask(plugin, () -> {
|
||||
// Ensure the player has data on SQL and that it is up-to-date
|
||||
HuskSyncVelocity.dataManager.ensurePlayerExists(player.getUniqueId(), player.getUsername());
|
||||
|
||||
// Get the player's data from SQL
|
||||
final Map<Settings.SynchronisationCluster, PlayerData> data = HuskSyncVelocity.dataManager.getPlayerData(player.getUniqueId());
|
||||
|
||||
// Update the player's data from SQL onto the cache
|
||||
assert data != null;
|
||||
for (Settings.SynchronisationCluster cluster : data.keySet()) {
|
||||
HuskSyncVelocity.dataManager.playerDataCache.get(cluster).updatePlayer(data.get(cluster));
|
||||
}
|
||||
|
||||
// Send a message asking the bukkit to request data on join
|
||||
try {
|
||||
new RedisMessage(RedisMessage.MessageType.REQUEST_DATA_ON_JOIN,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, null, null),
|
||||
RedisMessage.RequestOnJoinUpdateType.ADD_REQUESTER.toString(), player.getUniqueId().toString()).send();
|
||||
} catch (IOException e) {
|
||||
plugin.getVelocityLogger().log(Level.SEVERE, "Failed to serialize request data on join message data");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
package me.william278.husksync.velocity.listener;
|
||||
|
||||
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.redis.RedisListener;
|
||||
import me.william278.husksync.redis.RedisMessage;
|
||||
import me.william278.husksync.util.MessageManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class VelocityRedisListener extends RedisListener {
|
||||
|
||||
private static final HuskSyncVelocity plugin = HuskSyncVelocity.getInstance();
|
||||
|
||||
// Initialize the listener on the bungee
|
||||
public VelocityRedisListener() {
|
||||
listen();
|
||||
}
|
||||
|
||||
private PlayerData getPlayerCachedData(UUID uuid, String clusterId) {
|
||||
PlayerData data = null;
|
||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
||||
if (cluster.clusterId().equals(clusterId)) {
|
||||
// Get the player data from the cache
|
||||
PlayerData cachedData = HuskSyncVelocity.dataManager.playerDataCache.get(cluster).getPlayer(uuid);
|
||||
if (cachedData != null) {
|
||||
return cachedData;
|
||||
}
|
||||
|
||||
data = Objects.requireNonNull(HuskSyncVelocity.dataManager.getPlayerData(uuid)).get(cluster); // Get their player data from MySQL
|
||||
HuskSyncVelocity.dataManager.playerDataCache.get(cluster).updatePlayer(data); // Update the cache
|
||||
break;
|
||||
}
|
||||
}
|
||||
return data; // Return the data
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming {@link RedisMessage}
|
||||
*
|
||||
* @param message The {@link RedisMessage} to handle
|
||||
*/
|
||||
@Override
|
||||
public void handleMessage(RedisMessage message) {
|
||||
// Ignore messages destined for Bukkit servers
|
||||
if (message.getMessageTarget().targetServerType() != Settings.ServerType.PROXY) {
|
||||
return;
|
||||
}
|
||||
// Only process redis messages when ready
|
||||
if (!HuskSyncVelocity.readyForRedis) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.getMessageType()) {
|
||||
case PLAYER_DATA_REQUEST -> {
|
||||
// Get the UUID of the requesting player
|
||||
final UUID requestingPlayerUUID = UUID.fromString(message.getMessageData());
|
||||
plugin.getProxyServer().getScheduler().buildTask(plugin, () -> {
|
||||
try {
|
||||
// Send the reply, serializing the message data
|
||||
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_SET,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, requestingPlayerUUID, message.getMessageTarget().targetClusterId()),
|
||||
RedisMessage.serialize(getPlayerCachedData(requestingPlayerUUID, message.getMessageTarget().targetClusterId())))
|
||||
.send();
|
||||
|
||||
// Send an update to all bukkit servers removing the player from the requester cache
|
||||
new RedisMessage(RedisMessage.MessageType.REQUEST_DATA_ON_JOIN,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, null, message.getMessageTarget().targetClusterId()),
|
||||
RedisMessage.RequestOnJoinUpdateType.REMOVE_REQUESTER.toString(), requestingPlayerUUID.toString())
|
||||
.send();
|
||||
|
||||
// Send synchronisation complete message
|
||||
Optional<Player> player = plugin.getProxyServer().getPlayer(requestingPlayerUUID);
|
||||
player.ifPresent(value -> value.sendActionBar(new MineDown(MessageManager.getMessage("synchronisation_complete")).toComponent()));
|
||||
} catch (IOException e) {
|
||||
log(Level.SEVERE, "Failed to serialize data when replying to a data request");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
case PLAYER_DATA_UPDATE -> {
|
||||
// Deserialize the PlayerData received
|
||||
PlayerData playerData;
|
||||
final String serializedPlayerData = message.getMessageData();
|
||||
try {
|
||||
playerData = (PlayerData) RedisMessage.deserialize(serializedPlayerData);
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
log(Level.SEVERE, "Failed to deserialize PlayerData when handling a player update request");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the data in the cache and SQL
|
||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
||||
if (cluster.clusterId().equals(message.getMessageTarget().targetClusterId())) {
|
||||
HuskSyncVelocity.dataManager.updatePlayerData(playerData, cluster);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reply with the player data if they are still online (switching server)
|
||||
Optional<Player> updatingPlayer = plugin.getProxyServer().getPlayer(playerData.getPlayerUUID());
|
||||
updatingPlayer.ifPresent(player -> {
|
||||
try {
|
||||
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_SET,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, playerData.getPlayerUUID(), message.getMessageTarget().targetClusterId()),
|
||||
RedisMessage.serialize(playerData))
|
||||
.send();
|
||||
|
||||
// Send synchronisation complete message
|
||||
player.sendActionBar(new MineDown(MessageManager.getMessage("synchronisation_complete")).toComponent());
|
||||
} catch (IOException e) {
|
||||
log(Level.SEVERE, "Failed to re-serialize PlayerData when handling a player update request");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
case CONNECTION_HANDSHAKE -> {
|
||||
// Reply to a Bukkit server's connection handshake to complete the process
|
||||
if (HuskSyncVelocity.isDisabling) return; // Return if the Proxy is disabling
|
||||
final UUID serverUUID = UUID.fromString(message.getMessageDataElements()[0]);
|
||||
final boolean hasMySqlPlayerDataBridge = Boolean.parseBoolean(message.getMessageDataElements()[1]);
|
||||
final String bukkitBrand = message.getMessageDataElements()[2];
|
||||
final String huskSyncVersion = message.getMessageDataElements()[3];
|
||||
try {
|
||||
new RedisMessage(RedisMessage.MessageType.CONNECTION_HANDSHAKE,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, null, message.getMessageTarget().targetClusterId()),
|
||||
serverUUID.toString(), "Velocity")
|
||||
.send();
|
||||
HuskSyncVelocity.synchronisedServers.add(
|
||||
new Server(serverUUID, hasMySqlPlayerDataBridge,
|
||||
huskSyncVersion, bukkitBrand, message.getMessageTarget().targetClusterId()));
|
||||
log(Level.INFO, "Completed handshake with " + bukkitBrand + " server (" + serverUUID + ")");
|
||||
} catch (IOException e) {
|
||||
log(Level.SEVERE, "Failed to serialize handshake message data");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
case TERMINATE_HANDSHAKE -> {
|
||||
// Terminate the handshake with a Bukkit server
|
||||
final UUID serverUUID = UUID.fromString(message.getMessageDataElements()[0]);
|
||||
final String bukkitBrand = message.getMessageDataElements()[1];
|
||||
|
||||
// Remove a server from the synchronised server list
|
||||
Server serverToRemove = null;
|
||||
for (Server server : HuskSyncVelocity.synchronisedServers) {
|
||||
if (server.serverUUID().equals(serverUUID)) {
|
||||
serverToRemove = server;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HuskSyncVelocity.synchronisedServers.remove(serverToRemove);
|
||||
log(Level.INFO, "Terminated the handshake with " + bukkitBrand + " server (" + serverUUID + ")");
|
||||
}
|
||||
case DECODED_MPDB_DATA_SET -> {
|
||||
// Deserialize the PlayerData received
|
||||
PlayerData playerData;
|
||||
final String serializedPlayerData = message.getMessageDataElements()[0];
|
||||
final String playerName = message.getMessageDataElements()[1];
|
||||
try {
|
||||
playerData = (PlayerData) RedisMessage.deserialize(serializedPlayerData);
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
log(Level.SEVERE, "Failed to deserialize PlayerData when handling incoming decoded MPDB data");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
//todo Migrator
|
||||
/*// Add the incoming data to the data to be saved
|
||||
MPDBMigrator.incomingPlayerData.put(playerData, playerName);
|
||||
|
||||
// Increment players migrated
|
||||
MPDBMigrator.playersMigrated++;
|
||||
plugin.getBungeeLogger().log(Level.INFO, "Migrated " + MPDBMigrator.playersMigrated + "/" + MPDBMigrator.migratedDataSent + " players.");
|
||||
|
||||
// When all the data has been received, save it
|
||||
if (MPDBMigrator.migratedDataSent == MPDBMigrator.playersMigrated) {
|
||||
MPDBMigrator.loadIncomingData(MPDBMigrator.incomingPlayerData);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log to console
|
||||
*
|
||||
* @param level The {@link Level} to log
|
||||
* @param message Message to log
|
||||
*/
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
plugin.getVelocityLogger().log(level, message);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package me.william278.husksync.velocity.util;
|
||||
|
||||
import me.william278.husksync.util.Logger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public record VelocityLogger(org.slf4j.Logger parent) implements Logger {
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message, Exception e) {
|
||||
logMessage(level, message);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
logMessage(level, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String message) {
|
||||
logMessage(Level.INFO, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
logMessage(Level.SEVERE, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void config(String message) {
|
||||
logMessage(Level.CONFIG, message);
|
||||
}
|
||||
|
||||
// Logs the message using SLF4J
|
||||
private void logMessage(Level level, String message) {
|
||||
switch (level.intValue()) {
|
||||
case 1000 -> parent.error(message); // Severe
|
||||
case 900 -> parent.warn(message); // Warning
|
||||
case 70 -> parent.warn("[Config] " + message);
|
||||
default -> parent.info(message);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue