9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-25 17:49:20 +00:00

Finish initial implementation of plan

This commit is contained in:
William
2021-10-20 16:59:02 +01:00
parent 2a7371be31
commit 861477f5ae
21 changed files with 921 additions and 169 deletions

View File

@@ -1,50 +0,0 @@
package me.william278.crossserversync.bukkit;
import me.william278.crossserversync.Settings;
import me.william278.crossserversync.redis.RedisListener;
import me.william278.crossserversync.redis.RedisMessage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.logging.Level;
public class BukkitRedisListener extends RedisListener {
private static final CrossServerSyncBukkit plugin = CrossServerSyncBukkit.getInstance();
// Initialize the listener on the bukkit server
public BukkitRedisListener() {
listen();
}
/**
* Handle an incoming {@link RedisMessage}
*
* @param message The {@link RedisMessage} to handle
*/
@Override
public void handleMessage(RedisMessage message) {
// Ignore messages for proxy servers
if (message.getMessageTarget().targetServerType() != Settings.ServerType.BUKKIT) {
return;
}
// Handle the message for the player
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.getUniqueId() == message.getMessageTarget().targetPlayerName()) {
return;
}
}
}
/**
* Log to console
*
* @param level The {@link Level} to log
* @param message Message to log
*/
@Override
public void log(Level level, String message) {
plugin.getLogger().log(level, message);
}
}

View File

@@ -1,6 +1,9 @@
package me.william278.crossserversync.bukkit;
import me.william278.crossserversync.bukkit.config.ConfigLoader;
import me.william278.crossserversync.bukkit.data.LastDataUpdateUUIDCache;
import me.william278.crossserversync.bukkit.listener.BukkitRedisListener;
import me.william278.crossserversync.bukkit.listener.EventListener;
import org.bukkit.plugin.java.JavaPlugin;
public final class CrossServerSyncBukkit extends JavaPlugin {
@@ -10,6 +13,8 @@ public final class CrossServerSyncBukkit extends JavaPlugin {
return instance;
}
public static LastDataUpdateUUIDCache lastDataUpdateUUIDCache;
@Override
public void onLoad() {
instance = this;
@@ -26,12 +31,22 @@ public final class CrossServerSyncBukkit extends JavaPlugin {
reloadConfig();
ConfigLoader.loadSettings(getConfig());
// Initialize last data update UUID cache
lastDataUpdateUUIDCache = new LastDataUpdateUUIDCache();
// Initialize the redis listener
new BukkitRedisListener();
// Initialize event listener
getServer().getPluginManager().registerEvents(new EventListener(), this);
// Log to console
getLogger().info("Enabled CrossServerSync (" + getServer().getName() + ") v" + getDescription().getVersion());
}
@Override
public void onDisable() {
// Plugin shutdown logic
getLogger().info("Disabled CrossServerSync (" + getServer().getName() + ") v" + getDescription().getVersion());
}
}

View File

@@ -3,7 +3,6 @@ package me.william278.crossserversync.bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
@@ -107,13 +106,17 @@ public final class InventorySerializer {
}
/**
* Gets an array of ItemStacks from Base64 string.
* Gets an array of ItemStacks from a Base64 string.
*
* @param data Base64 string to convert to ItemStack array.
* @return ItemStack array created from the Base64 string.
* @throws IOException in the event the class type cannot be decoded
*/
public static ItemStack[] itemStackArrayFromBase64(String data) throws IOException {
// Return an empty ItemStack[] if the data is empty
if (data.isEmpty()) {
return new ItemStack[0];
}
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data))) {
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
ItemStack[] items = new ItemStack[dataInput.readInt()];

View File

@@ -0,0 +1,25 @@
package me.william278.crossserversync.bukkit.data;
import java.util.HashMap;
import java.util.UUID;
public class LastDataUpdateUUIDCache {
/**
* Map of Player UUIDs to last-updated PlayerData version UUIDs
*/
private static HashMap<UUID, UUID> lastUpdatedPlayerDataUUIDs;
public LastDataUpdateUUIDCache() {
lastUpdatedPlayerDataUUIDs = new HashMap<>();
}
public UUID getVersionUUID(UUID playerUUID) {
return lastUpdatedPlayerDataUUIDs.get(playerUUID);
}
public void setVersionUUID(UUID playerUUID, UUID dataVersionUUID) {
lastUpdatedPlayerDataUUIDs.put(playerUUID, dataVersionUUID);
}
}

View File

@@ -0,0 +1,69 @@
package me.william278.crossserversync.bukkit.listener;
import me.william278.crossserversync.bukkit.InventorySerializer;
import me.william278.crossserversync.PlayerData;
import me.william278.crossserversync.Settings;
import me.william278.crossserversync.bukkit.CrossServerSyncBukkit;
import me.william278.crossserversync.redis.RedisListener;
import me.william278.crossserversync.redis.RedisMessage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.io.IOException;
import java.util.logging.Level;
public class BukkitRedisListener extends RedisListener {
private static final CrossServerSyncBukkit plugin = CrossServerSyncBukkit.getInstance();
// Initialize the listener on the bukkit server
public BukkitRedisListener() {
listen();
}
/**
* Handle an incoming {@link RedisMessage}
*
* @param message The {@link RedisMessage} to handle
*/
@Override
public void handleMessage(RedisMessage message) {
// Ignore messages for proxy servers
if (message.getMessageTarget().targetServerType() != Settings.ServerType.BUKKIT) {
return;
}
// Handle the message for the player
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.getUniqueId() == message.getMessageTarget().targetPlayerName()) {
if (message.getMessageType() == RedisMessage.MessageType.PLAYER_DATA_REPLY) {
try {
// Deserialize the received PlayerData
PlayerData data = (PlayerData) RedisMessage.deserialize(message.getMessageData());
// Set the player's data //todo do more stuff like health etc
InventorySerializer.setPlayerItems(player, InventorySerializer.itemStackArrayFromBase64(data.getSerializedInventory()));
InventorySerializer.setPlayerEnderChest(player, InventorySerializer.itemStackArrayFromBase64(data.getSerializedEnderChest()));
// Update last loaded data UUID
CrossServerSyncBukkit.lastDataUpdateUUIDCache.setVersionUUID(player.getUniqueId(), data.getDataVersionUUID());
} catch (IOException | ClassNotFoundException e) {
log(Level.SEVERE, "Failed to deserialize PlayerData when handling a reply from the proxy with PlayerData");
e.printStackTrace();
}
}
return;
}
}
}
/**
* Log to console
*
* @param level The {@link Level} to log
* @param message Message to log
*/
@Override
public void log(Level level, String message) {
plugin.getLogger().log(level, message);
}
}

View File

@@ -0,0 +1,67 @@
package me.william278.crossserversync.bukkit.listener;
import me.william278.crossserversync.PlayerData;
import me.william278.crossserversync.Settings;
import me.william278.crossserversync.bukkit.CrossServerSyncBukkit;
import me.william278.crossserversync.bukkit.InventorySerializer;
import me.william278.crossserversync.redis.RedisMessage;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Level;
public class EventListener implements Listener {
private static final CrossServerSyncBukkit plugin = CrossServerSyncBukkit.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(),
InventorySerializer.getSerializedInventoryContents(player),
InventorySerializer.getSerializedEnderChestContents(player)));
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
// When a player leaves a Bukkit server
final Player player = event.getPlayer();
try {
// Get the player's last updated PlayerData version UUID
final UUID lastUpdatedDataVersion = CrossServerSyncBukkit.lastDataUpdateUUIDCache.getVersionUUID(player.getUniqueId());
if (lastUpdatedDataVersion == null) return; // Return if the player has not been properly updated.
// Send a redis message with the player's last updated PlayerData version UUID and their new PlayerData
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_UPDATE,
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
lastUpdatedDataVersion.toString(), getNewSerializedPlayerData(player)).send();
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to send a PlayerData update to the proxy", e);
}
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
// When a player joins a Bukkit server
final Player player = event.getPlayer();
try {
// Send a redis message requesting the player data
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_REQUEST,
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
player.getUniqueId().toString()).send();
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to send a PlayerData fetch request", e);
}
}
}