mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-19 14:59:21 +00:00
MPDB Migration Fixes, server shutdown synchronisation fixes and polish
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package me.william278.husksync.api.events;
|
||||
|
||||
import me.william278.husksync.PlayerData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents an event that will be fired when a {@link Player} has finished
|
||||
* being synchronised with the correct {@link PlayerData}.
|
||||
*/
|
||||
public class SyncCompleteEvent extends PlayerEvent {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
private final PlayerData data;
|
||||
|
||||
public SyncCompleteEvent(Player player, PlayerData data) {
|
||||
super(player);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PlayerData} which has just been set on the {@link Player}
|
||||
* @return The {@link PlayerData} that has been set
|
||||
*/
|
||||
public PlayerData getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package me.william278.husksync.api.events;
|
||||
|
||||
import me.william278.husksync.PlayerData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents an event that will be fired before a {@link Player} is about
|
||||
* to be synchronised with their {@link PlayerData}.
|
||||
*/
|
||||
public class SyncEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private boolean cancelled;
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
private PlayerData data;
|
||||
|
||||
public SyncEvent(Player player, PlayerData data) {
|
||||
super(player);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PlayerData} which has just been set on the {@link Player}
|
||||
* @return The {@link PlayerData} that has been set
|
||||
*/
|
||||
public PlayerData getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PlayerData} to be synchronised to this player
|
||||
* @param data The {@link PlayerData} to set to the player
|
||||
*/
|
||||
public void setData(PlayerData data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cancellation state of this event. A cancelled event will not
|
||||
* be executed in the server, but will still pass to other plugins
|
||||
*
|
||||
* @return true if this event is cancelled
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cancellation state of this event. A cancelled event will not
|
||||
* be executed in the server, but will still pass to other plugins.
|
||||
*
|
||||
* @param cancel true if you wish to cancel this event
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
dependencies {
|
||||
compileOnly project(':common')
|
||||
compileOnly project(':api')
|
||||
implementation project(path: ':common', configuration: 'shadow')
|
||||
|
||||
compileOnly 'redis.clients:jedis:3.7.0'
|
||||
implementation 'org.bstats:bstats-bukkit:2.2.1'
|
||||
implementation 'redis.clients:jedis:3.7.0'
|
||||
implementation 'de.themoep:minedown:1.7.1-SNAPSHOT'
|
||||
|
||||
compileOnly 'net.craftersland.data:bridge:3.36.3'
|
||||
@@ -11,7 +12,6 @@ dependencies {
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
relocate 'redis.clients', 'me.William278.husksync.libraries.jedis'
|
||||
relocate 'org.bstats', 'me.William278.husksync.libraries.plan'
|
||||
relocate 'de.themoep', 'me.William278.husksync.libraries.minedown'
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package me.william278.husksync;
|
||||
|
||||
import me.william278.husksync.bukkit.PlayerSetter;
|
||||
import me.william278.husksync.bukkit.config.ConfigLoader;
|
||||
import me.william278.husksync.bukkit.data.BukkitDataCache;
|
||||
import me.william278.husksync.bukkit.listener.BukkitRedisListener;
|
||||
import me.william278.husksync.bukkit.listener.EventListener;
|
||||
import me.william278.husksync.bukkit.migrator.MPDBDeserializer;
|
||||
import me.william278.husksync.redis.RedisMessage;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
@@ -17,8 +20,9 @@ import java.util.logging.Level;
|
||||
|
||||
public final class HuskSyncBukkit extends JavaPlugin {
|
||||
|
||||
private static HuskSyncBukkit instance;
|
||||
private static final int METRICS_ID = 13140;
|
||||
|
||||
private static HuskSyncBukkit instance;
|
||||
public static HuskSyncBukkit getInstance() {
|
||||
return instance;
|
||||
}
|
||||
@@ -52,7 +56,8 @@ public final class HuskSyncBukkit extends JavaPlugin {
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
serverUUID.toString(),
|
||||
Boolean.toString(isMySqlPlayerDataBridgeInstalled),
|
||||
Bukkit.getName()).send();
|
||||
Bukkit.getName())
|
||||
.send();
|
||||
attempts[0]++;
|
||||
if (attempts[0] == 10) {
|
||||
getInstance().getLogger().log(Level.WARNING, "Failed to complete handshake with the Proxy server; Please make sure your Proxy server is online and has HuskSync installed in its' /plugins/ folder. HuskSync will continue to try and establish a connection.");
|
||||
@@ -64,6 +69,7 @@ public final class HuskSyncBukkit extends JavaPlugin {
|
||||
}
|
||||
|
||||
private void closeRedisHandshake() {
|
||||
if (!handshakeCompleted) return;
|
||||
try {
|
||||
new RedisMessage(RedisMessage.MessageType.TERMINATE_HANDSHAKE,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
@@ -114,12 +120,29 @@ public final class HuskSyncBukkit extends JavaPlugin {
|
||||
// Ensure redis is connected; establish a handshake
|
||||
establishRedisHandshake();
|
||||
|
||||
// Initialize bStats metrics
|
||||
try {
|
||||
new Metrics(this, METRICS_ID);
|
||||
} catch (Exception e) {
|
||||
getLogger().info("Skipped metrics initialization");
|
||||
}
|
||||
|
||||
// Log to console
|
||||
getLogger().info("Enabled HuskSync (" + getServer().getName() + ") v" + getDescription().getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// Update player data for disconnecting players
|
||||
if (HuskSyncBukkit.handshakeCompleted && !HuskSyncBukkit.isMySqlPlayerDataBridgeInstalled && Bukkit.getOnlinePlayers().size() > 0) {
|
||||
getLogger().info("Saving data for remaining online players...");
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
PlayerSetter.updatePlayerData(player);
|
||||
}
|
||||
getLogger().info("Data save complete!");
|
||||
}
|
||||
|
||||
|
||||
// Send termination handshake to proxy
|
||||
closeRedisHandshake();
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package me.william278.husksync.bukkit;
|
||||
import me.william278.husksync.HuskSyncBukkit;
|
||||
import me.william278.husksync.PlayerData;
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.api.events.SyncCompleteEvent;
|
||||
import me.william278.husksync.api.events.SyncEvent;
|
||||
import me.william278.husksync.bukkit.data.DataSerializer;
|
||||
import me.william278.husksync.redis.RedisMessage;
|
||||
import org.bukkit.*;
|
||||
@@ -26,6 +28,60 @@ public class PlayerSetter {
|
||||
|
||||
private static final HuskSyncBukkit plugin = HuskSyncBukkit.getInstance();
|
||||
|
||||
/**
|
||||
* Returns the new serialized PlayerData for a player.
|
||||
*
|
||||
* @param player The {@link Player} to get the new serialized PlayerData for
|
||||
* @return The {@link PlayerData}, serialized as a {@link String}
|
||||
* @throws IOException If the serialization fails
|
||||
*/
|
||||
private static String getNewSerializedPlayerData(Player player) throws IOException {
|
||||
return RedisMessage.serialize(new PlayerData(player.getUniqueId(),
|
||||
DataSerializer.getSerializedInventoryContents(player.getInventory()),
|
||||
DataSerializer.getSerializedEnderChestContents(player),
|
||||
player.getHealth(),
|
||||
Objects.requireNonNull(player.getAttribute(Attribute.GENERIC_MAX_HEALTH)).getBaseValue(),
|
||||
player.getHealthScale(),
|
||||
player.getFoodLevel(),
|
||||
player.getSaturation(),
|
||||
player.getExhaustion(),
|
||||
player.getInventory().getHeldItemSlot(),
|
||||
DataSerializer.getSerializedEffectData(player),
|
||||
player.getTotalExperience(),
|
||||
player.getLevel(),
|
||||
player.getExp(),
|
||||
player.getGameMode().toString(),
|
||||
DataSerializer.getSerializedStatisticData(player),
|
||||
player.isFlying(),
|
||||
DataSerializer.getSerializedAdvancements(player),
|
||||
DataSerializer.getSerializedLocation(player)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a {@link Player}'s data, sending it to the proxy
|
||||
* @param player {@link Player} to send data to proxy
|
||||
*/
|
||||
public static void updatePlayerData(Player player) {
|
||||
// Send a redis message with the player's last updated PlayerData version UUID and their new PlayerData
|
||||
try {
|
||||
final String serializedPlayerData = getNewSerializedPlayerData(player);
|
||||
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_UPDATE,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
serializedPlayerData).send();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to send a PlayerData update to the proxy", e);
|
||||
}
|
||||
|
||||
// Clear player inventory and ender chest
|
||||
player.getInventory().clear();
|
||||
player.getEnderChest().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a {@link Player}'s data from the proxy
|
||||
* @param playerUUID The {@link UUID} of the {@link Player} to fetch PlayerData from
|
||||
* @throws IOException If the request Redis message data fails to serialize
|
||||
*/
|
||||
public static void requestPlayerData(UUID playerUUID) throws IOException {
|
||||
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_REQUEST,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
@@ -35,26 +91,34 @@ public class PlayerSetter {
|
||||
/**
|
||||
* Set a player from their PlayerData, based on settings
|
||||
*
|
||||
* @param player The {@link Player} to set
|
||||
* @param data The {@link PlayerData} to assign to the player
|
||||
* @param player The {@link Player} to set
|
||||
* @param dataToSet The {@link PlayerData} to assign to the player
|
||||
*/
|
||||
public static void setPlayerFrom(Player player, PlayerData data) {
|
||||
// If the data is flagged as being default data, skip setting
|
||||
if (data.isUseDefaultData()) {
|
||||
HuskSyncBukkit.bukkitCache.removeAwaitingDataFetch(player.getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear player
|
||||
player.getInventory().clear();
|
||||
player.getEnderChest().clear();
|
||||
player.setExp(0);
|
||||
player.setLevel(0);
|
||||
|
||||
HuskSyncBukkit.bukkitCache.removeAwaitingDataFetch(player.getUniqueId());
|
||||
|
||||
// Set the player's data from the PlayerData
|
||||
public static void setPlayerFrom(Player player, PlayerData dataToSet) {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
// Handle the SyncEvent
|
||||
SyncEvent syncEvent = new SyncEvent(player, dataToSet);
|
||||
Bukkit.getPluginManager().callEvent(syncEvent);
|
||||
final PlayerData data = syncEvent.getData();
|
||||
if (syncEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the data is flagged as being default data, skip setting
|
||||
if (data.isUseDefaultData()) {
|
||||
HuskSyncBukkit.bukkitCache.removeAwaitingDataFetch(player.getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear player
|
||||
player.getInventory().clear();
|
||||
player.getEnderChest().clear();
|
||||
player.setExp(0);
|
||||
player.setLevel(0);
|
||||
|
||||
HuskSyncBukkit.bukkitCache.removeAwaitingDataFetch(player.getUniqueId());
|
||||
|
||||
// Set the player's data from the PlayerData
|
||||
try {
|
||||
if (Settings.syncAdvancements) {
|
||||
setPlayerAdvancements(player, DataSerializer.deserializeAdvancementData(data.getSerializedAdvancements()), data);
|
||||
@@ -67,7 +131,7 @@ public class PlayerSetter {
|
||||
setPlayerEnderChest(player, DataSerializer.itemStackArrayFromBase64(data.getSerializedEnderChest()));
|
||||
}
|
||||
if (Settings.syncHealth) {
|
||||
player.setHealthScale(data.getHealthScale() > 0 ? data.getHealthScale() : 0D);
|
||||
player.setHealthScale(data.getHealthScale() <= 0 ? data.getHealthScale() : 20D);
|
||||
Objects.requireNonNull(player.getAttribute(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(data.getMaxHealth());
|
||||
player.setHealth(data.getHealth());
|
||||
}
|
||||
@@ -93,6 +157,9 @@ public class PlayerSetter {
|
||||
player.setFlying(player.getAllowFlight() && data.isFlying());
|
||||
setPlayerLocation(player, DataSerializer.deserializePlayerLocationData(data.getSerializedLocation()));
|
||||
}
|
||||
|
||||
// Handle the SyncCompleteEvent
|
||||
Bukkit.getPluginManager().callEvent(new SyncCompleteEvent(player, data));
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to deserialize PlayerData", e);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class BukkitRedisListener extends RedisListener {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the message for the player
|
||||
// Handle the incoming redis message; either for a specific player or the system
|
||||
if (message.getMessageTarget().targetPlayerUUID() == null) {
|
||||
switch (message.getMessageType()) {
|
||||
case REQUEST_DATA_ON_JOIN -> {
|
||||
@@ -81,18 +81,20 @@ public class BukkitRedisListener extends RedisListener {
|
||||
case DECODE_MPDB_DATA -> {
|
||||
UUID serverUUID = UUID.fromString(message.getMessageDataElements()[0]);
|
||||
String encodedData = message.getMessageDataElements()[1];
|
||||
if (serverUUID.equals(HuskSyncBukkit.serverUUID)) {
|
||||
try {
|
||||
MPDBPlayerData data = (MPDBPlayerData) RedisMessage.deserialize(encodedData);
|
||||
new RedisMessage(RedisMessage.MessageType.DECODED_MPDB_DATA_SET,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
RedisMessage.serialize(MPDBDeserializer.convertMPDBData(data)),
|
||||
data.playerName)
|
||||
.send();
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
log(Level.SEVERE, "Failed to serialize encoded MPDB data");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (serverUUID.equals(HuskSyncBukkit.serverUUID)) {
|
||||
try {
|
||||
MPDBPlayerData data = (MPDBPlayerData) RedisMessage.deserialize(encodedData);
|
||||
new RedisMessage(RedisMessage.MessageType.DECODED_MPDB_DATA_SET,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
RedisMessage.serialize(MPDBDeserializer.convertMPDBData(data)),
|
||||
data.playerName)
|
||||
.send();
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
log(Level.SEVERE, "Failed to serialize encoded MPDB data");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
case RELOAD_CONFIG -> {
|
||||
plugin.reloadConfig();
|
||||
|
||||
@@ -28,35 +28,6 @@ public class EventListener implements Listener {
|
||||
|
||||
private static final HuskSyncBukkit plugin = HuskSyncBukkit.getInstance();
|
||||
|
||||
/**
|
||||
* Returns the new serialized PlayerData for a player.
|
||||
*
|
||||
* @param player The {@link Player} to get the new serialized PlayerData for
|
||||
* @return The {@link PlayerData}, serialized as a {@link String}
|
||||
* @throws IOException If the serialization fails
|
||||
*/
|
||||
private static String getNewSerializedPlayerData(Player player) throws IOException {
|
||||
return RedisMessage.serialize(new PlayerData(player.getUniqueId(),
|
||||
DataSerializer.getSerializedInventoryContents(player.getInventory()),
|
||||
DataSerializer.getSerializedEnderChestContents(player),
|
||||
player.getHealth(),
|
||||
Objects.requireNonNull(player.getAttribute(Attribute.GENERIC_MAX_HEALTH)).getBaseValue(),
|
||||
player.getHealthScale(),
|
||||
player.getFoodLevel(),
|
||||
player.getSaturation(),
|
||||
player.getExhaustion(),
|
||||
player.getInventory().getHeldItemSlot(),
|
||||
DataSerializer.getSerializedEffectData(player),
|
||||
player.getTotalExperience(),
|
||||
player.getLevel(),
|
||||
player.getExp(),
|
||||
player.getGameMode().toString(),
|
||||
DataSerializer.getSerializedStatisticData(player),
|
||||
player.isFlying(),
|
||||
DataSerializer.getSerializedAdvancements(player),
|
||||
DataSerializer.getSerializedLocation(player)));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
// When a player leaves a Bukkit server
|
||||
@@ -70,19 +41,8 @@ public class EventListener implements Listener {
|
||||
|
||||
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.isMySqlPlayerDataBridgeInstalled) return; // If the plugin has not been initialized correctly
|
||||
|
||||
// Send a redis message with the player's last updated PlayerData version UUID and their new PlayerData
|
||||
try {
|
||||
final String serializedPlayerData = getNewSerializedPlayerData(player);
|
||||
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_UPDATE,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
serializedPlayerData).send();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to send a PlayerData update to the proxy", e);
|
||||
}
|
||||
|
||||
// Clear player inventory and ender chest
|
||||
player.getInventory().clear();
|
||||
player.getEnderChest().clear();
|
||||
// Update the player's data
|
||||
PlayerSetter.updatePlayerData(player);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
||||
@@ -21,6 +21,7 @@ public class MPDBDeserializer {
|
||||
|
||||
// Instance of MySqlPlayerDataBridge
|
||||
private static PD mySqlPlayerDataBridge;
|
||||
|
||||
public static void setMySqlPlayerDataBridge() {
|
||||
mySqlPlayerDataBridge = (PD) Bukkit.getPluginManager().getPlugin("MySqlPlayerDataBridge");
|
||||
}
|
||||
@@ -41,16 +42,36 @@ public class MPDBDeserializer {
|
||||
|
||||
// Convert the data
|
||||
try {
|
||||
// Set inventory
|
||||
// Set inventory contents
|
||||
Inventory inventory = Bukkit.createInventory(null, InventoryType.PLAYER);
|
||||
PlayerSetter.setInventory(inventory, getItemStackArrayFromMPDBBase64String(mpdbPlayerData.inventoryData));
|
||||
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.getSerializedInventoryContents(inventory));
|
||||
inventory.clear();
|
||||
|
||||
// Set ender chest
|
||||
playerData.setSerializedEnderChest(DataSerializer.itemStackArrayToBase64(
|
||||
getItemStackArrayFromMPDBBase64String(mpdbPlayerData.enderChestData)));
|
||||
// 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.itemStackArrayToBase64(enderChestData));
|
||||
|
||||
// Set experience
|
||||
playerData.setExpLevel(mpdbPlayerData.expLevel);
|
||||
|
||||
@@ -2,7 +2,8 @@ dependencies {
|
||||
compileOnly project(':common')
|
||||
implementation project(path: ':common', configuration: 'shadow')
|
||||
|
||||
implementation 'redis.clients:jedis:3.7.0'
|
||||
compileOnly 'redis.clients:jedis:3.7.0'
|
||||
implementation 'org.bstats:bstats-bungeecord:2.2.1'
|
||||
implementation 'com.zaxxer:HikariCP:5.0.0'
|
||||
implementation 'de.themoep:minedown:1.7.1-SNAPSHOT'
|
||||
|
||||
@@ -10,7 +11,6 @@ dependencies {
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
relocate 'redis.clients', 'me.William278.husksync.libraries.jedis'
|
||||
relocate 'com.zaxxer', 'me.William278.husksync.libraries.hikari'
|
||||
relocate 'org.bstats', 'me.William278.husksync.libraries.plan'
|
||||
relocate 'de.themoep', 'me.William278.husksync.libraries.minedown'
|
||||
|
||||
@@ -13,6 +13,7 @@ import me.william278.husksync.bungeecord.migrator.MPDBMigrator;
|
||||
import me.william278.husksync.redis.RedisMessage;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
@@ -24,6 +25,8 @@ import java.util.logging.Level;
|
||||
|
||||
public final class HuskSyncBungeeCord extends Plugin {
|
||||
|
||||
private static final int METRICS_ID = 13141;
|
||||
|
||||
private static HuskSyncBungeeCord instance;
|
||||
public static HuskSyncBungeeCord getInstance() {
|
||||
return instance;
|
||||
@@ -58,10 +61,10 @@ public final class HuskSyncBungeeCord extends Plugin {
|
||||
ConfigLoader.loadSettings(Objects.requireNonNull(ConfigManager.getConfig()));
|
||||
|
||||
// Load messages
|
||||
ConfigManager.loadMessages(Settings.language);
|
||||
ConfigManager.loadMessages();
|
||||
|
||||
// Load locales from messages
|
||||
ConfigLoader.loadMessages(Objects.requireNonNull(ConfigManager.getMessages(Settings.language)));
|
||||
ConfigLoader.loadMessageStrings(Objects.requireNonNull(ConfigManager.getMessages()));
|
||||
|
||||
// Initialize the database
|
||||
database = switch (Settings.dataStorageType) {
|
||||
@@ -95,6 +98,13 @@ public final class HuskSyncBungeeCord extends Plugin {
|
||||
// Prepare the migrator for use if needed
|
||||
mpdbMigrator = new MPDBMigrator();
|
||||
|
||||
// Initialize bStats metrics
|
||||
try {
|
||||
new Metrics(this, METRICS_ID);
|
||||
} catch (Exception e) {
|
||||
getLogger().info("Skipped metrics initialization");
|
||||
}
|
||||
|
||||
// Log to console
|
||||
getLogger().info("Enabled HuskSync (" + getProxy().getName() + ") v" + getDescription().getVersion());
|
||||
}
|
||||
|
||||
@@ -97,8 +97,9 @@ public class HuskSyncCommand extends Command implements TabExecutor {
|
||||
}
|
||||
ConfigManager.loadConfig();
|
||||
ConfigLoader.loadSettings(Objects.requireNonNull(ConfigManager.getConfig()));
|
||||
ConfigManager.loadMessages(Settings.language);
|
||||
ConfigLoader.loadMessages(Objects.requireNonNull(ConfigManager.getMessages(Settings.language)));
|
||||
|
||||
ConfigManager.loadMessages();
|
||||
ConfigLoader.loadMessageStrings(Objects.requireNonNull(ConfigManager.getMessages()));
|
||||
|
||||
// Send reload request to all bukkit servers
|
||||
try {
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.util.HashMap;
|
||||
public class ConfigLoader {
|
||||
|
||||
public static void loadSettings(Configuration config) throws IllegalArgumentException {
|
||||
Settings.language = config.getString("language", "en-gb");
|
||||
|
||||
Settings.serverType = Settings.ServerType.BUNGEECORD;
|
||||
Settings.redisHost = config.getString("redis_settings.host", "localhost");
|
||||
Settings.redisPort = config.getInt("redis_settings.port", 6379);
|
||||
@@ -31,7 +33,7 @@ public class ConfigLoader {
|
||||
Settings.hikariConnectionTimeOut = config.getLong("data_storage_settings.hikari_pool_settings.connection_timeout", 5000);
|
||||
}
|
||||
|
||||
public static void loadMessages(Configuration config) {
|
||||
public static void loadMessageStrings(Configuration config) {
|
||||
final HashMap<String,String> messages = new HashMap<>();
|
||||
for (String messageId : config.getKeys()) {
|
||||
messages.put(messageId, config.getString(messageId));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.william278.husksync.bungeecord.config;
|
||||
|
||||
import me.william278.husksync.HuskSyncBungeeCord;
|
||||
import me.william278.husksync.Settings;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
@@ -31,16 +32,16 @@ public class ConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadMessages(String language) {
|
||||
public static void loadMessages() {
|
||||
try {
|
||||
if (!plugin.getDataFolder().exists()) {
|
||||
if (plugin.getDataFolder().mkdir()) {
|
||||
plugin.getLogger().info("Created HuskSync data folder");
|
||||
}
|
||||
}
|
||||
File messagesFile = new File(plugin.getDataFolder(), "messages_ " + language + ".yml");
|
||||
File messagesFile = new File(plugin.getDataFolder(), "messages_" + Settings.language + ".yml");
|
||||
if (!messagesFile.exists()) {
|
||||
Files.copy(plugin.getResourceAsStream("languages" + File.separator + language + ".yml"), messagesFile.toPath());
|
||||
Files.copy(plugin.getResourceAsStream("languages/" + Settings.language + ".yml"), messagesFile.toPath());
|
||||
plugin.getLogger().info("Created HuskSync messages file");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -58,9 +59,9 @@ public class ConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static Configuration getMessages(String language) {
|
||||
public static Configuration getMessages() {
|
||||
try {
|
||||
File configFile = new File(plugin.getDataFolder(), "messages-" + language + ".yml");
|
||||
File configFile = new File(plugin.getDataFolder(), "messages_" + Settings.language + ".yml");
|
||||
return ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.CONFIG, "An IOException occurred fetching the messages file", e);
|
||||
|
||||
@@ -9,6 +9,7 @@ import me.william278.husksync.bungeecord.data.DataManager;
|
||||
import me.william278.husksync.bungeecord.migrator.MPDBMigrator;
|
||||
import me.william278.husksync.redis.RedisListener;
|
||||
import me.william278.husksync.redis.RedisMessage;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
@@ -70,7 +71,7 @@ public class BungeeRedisListener extends RedisListener {
|
||||
// Send synchronisation complete message
|
||||
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(requestingPlayerUUID);
|
||||
if (player.isConnected()) {
|
||||
player.sendMessage(new MineDown(MessageManager.getMessage("synchronisation_complete")).toComponent());
|
||||
player.sendMessage(ChatMessageType.ACTION_BAR, new MineDown(MessageManager.getMessage("synchronisation_complete")).toComponent());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log(Level.SEVERE, "Failed to serialize data when replying to a data request");
|
||||
@@ -104,7 +105,7 @@ public class BungeeRedisListener extends RedisListener {
|
||||
.send();
|
||||
|
||||
// Send synchronisation complete message
|
||||
player.sendMessage(new MineDown(MessageManager.getMessage("synchronisation_complete")).toComponent());
|
||||
player.sendMessage(ChatMessageType.ACTION_BAR, new MineDown(MessageManager.getMessage("synchronisation_complete")).toComponent());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -54,8 +54,9 @@ public class MPDBMigrator {
|
||||
}
|
||||
}
|
||||
if (synchronisedServersWithMpdb < 1) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to start migration because at least one Spigot server must be online and have both HuskSync and MySqlPlayerDataBridge installed. " +
|
||||
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;
|
||||
}
|
||||
|
||||
migratedDataSent = 0;
|
||||
@@ -144,7 +145,7 @@ public class MPDBMigrator {
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "An exception occurred getting ender chest", 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");
|
||||
}
|
||||
@@ -161,7 +162,7 @@ public class MPDBMigrator {
|
||||
for (MPDBPlayerData data : mpdbPlayerData) {
|
||||
if (data.playerUUID.equals(playerUUID)) {
|
||||
data.expLevel = resultSet.getInt("exp_lvl");
|
||||
data.expProgress = resultSet.getInt("exp");
|
||||
data.expProgress = resultSet.getFloat("exp");
|
||||
data.totalExperience = resultSet.getInt("total_exp");
|
||||
break;
|
||||
}
|
||||
@@ -169,7 +170,7 @@ public class MPDBMigrator {
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "An exception occurred getting ender chest", 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");
|
||||
}
|
||||
@@ -229,6 +230,7 @@ public class MPDBMigrator {
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
language: 'en-gb'
|
||||
redis_settings:
|
||||
host: 'localhost'
|
||||
port: 6379
|
||||
@@ -16,4 +17,5 @@ data_storage_settings:
|
||||
minimum_idle: 10
|
||||
maximum_lifetime: 1800000
|
||||
keepalive_time: 0
|
||||
connection_timeout: 5000
|
||||
connection_timeout: 5000
|
||||
config_file_version: 1.0
|
||||
@@ -24,7 +24,4 @@ shadowJar {
|
||||
// Exclude some unnecessary files
|
||||
exclude "**/module-info.class"
|
||||
exclude "module-info.class"
|
||||
|
||||
// Relocations
|
||||
relocate 'redis.clients', 'me.William278.husksync.libraries.jedis'
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class PlayerData implements Serializable {
|
||||
*/
|
||||
public static PlayerData DEFAULT_PLAYER_DATA(UUID playerUUID) {
|
||||
PlayerData data = new PlayerData(playerUUID, "", "", 20,
|
||||
20, 0, 20, 10, 1, 0,
|
||||
20, 20, 20, 10, 1, 0,
|
||||
"", 0, 0, 0, "SURVIVAL",
|
||||
"", false, "", "");
|
||||
data.useDefaultData = true;
|
||||
|
||||
@@ -2,7 +2,7 @@ name: HuskSync
|
||||
version: @version@
|
||||
main: me.william278.husksync.HuskSyncBungeeCord
|
||||
author: William278
|
||||
description: 'A modern, cross-server player data synchronisation system'
|
||||
description: 'A modern, cross-server player data synchronization system'
|
||||
libraries:
|
||||
- mysql:mysql-connector-java:8.0.25
|
||||
- org.xerial:sqlite-jdbc:3.36.0.3
|
||||
@@ -3,5 +3,5 @@ version: @version@
|
||||
main: me.william278.husksync.HuskSyncBukkit
|
||||
api-version: 1.16
|
||||
author: William278
|
||||
description: 'A modern, cross-server player data synchronisation system'
|
||||
description: 'A modern, cross-server player data synchronization system'
|
||||
softdepend: [MysqlPlayerDataBridge]
|
||||
@@ -1,10 +1,14 @@
|
||||
dependencies {
|
||||
implementation project(path: ":common", configuration: 'shadow')
|
||||
implementation project(path: ":api", configuration: 'shadow')
|
||||
implementation project(path: ":bukkit", configuration: 'shadow')
|
||||
implementation project(path: ":bungeecord", configuration: 'shadow')
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
// Relocations
|
||||
relocate 'redis.clients', 'me.William278.husksync.libraries.jedis'
|
||||
|
||||
destinationDirectory.set(file("$rootDir/target/"))
|
||||
archiveBaseName.set('HuskSync')
|
||||
archiveClassifier.set('')
|
||||
|
||||
@@ -7,6 +7,7 @@ pluginManagement {
|
||||
rootProject.name = 'HuskSync'
|
||||
|
||||
include 'common'
|
||||
include 'api'
|
||||
include 'bukkit'
|
||||
include 'bungeecord'
|
||||
include 'plugin'
|
||||
Reference in New Issue
Block a user