mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-26 10:09:10 +00:00
Now fully reliable and added support for health, max health, etc
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package me.william278.crossserversync.bukkit;
|
||||
package me.william278.crossserversync;
|
||||
|
||||
import me.william278.crossserversync.bukkit.config.ConfigLoader;
|
||||
import me.william278.crossserversync.bukkit.data.LastDataUpdateUUIDCache;
|
||||
@@ -34,12 +34,12 @@ public final class CrossServerSyncBukkit extends JavaPlugin {
|
||||
// Initialize last data update UUID cache
|
||||
lastDataUpdateUUIDCache = new LastDataUpdateUUIDCache();
|
||||
|
||||
// Initialize the redis listener
|
||||
new BukkitRedisListener();
|
||||
|
||||
// Initialize event listener
|
||||
getServer().getPluginManager().registerEvents(new EventListener(), this);
|
||||
|
||||
// Initialize the redis listener
|
||||
new BukkitRedisListener();
|
||||
|
||||
// Log to console
|
||||
getLogger().info("Enabled CrossServerSync (" + getServer().getName() + ") v" + getDescription().getVersion());
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package me.william278.crossserversync.bukkit;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.util.io.BukkitObjectInputStream;
|
||||
import org.bukkit.util.io.BukkitObjectOutputStream;
|
||||
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
||||
@@ -19,8 +19,9 @@ import java.util.Map;
|
||||
*
|
||||
* @author efindus
|
||||
* @author graywolf336
|
||||
* @author William278
|
||||
*/
|
||||
public final class InventorySerializer {
|
||||
public final class DataSerializer {
|
||||
|
||||
/**
|
||||
* Converts the player inventory to a Base64 encoded string.
|
||||
@@ -46,35 +47,33 @@ public final class InventorySerializer {
|
||||
return itemStackArrayToBase64(player.getEnderChest().getContents());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player's inventory from a set of {@link ItemStack}s
|
||||
*
|
||||
* @param player The player to set the inventory of
|
||||
* @param items The array of {@link ItemStack}s to set
|
||||
*/
|
||||
public static void setPlayerItems(Player player, ItemStack[] items) {
|
||||
setInventoryItems(player.getInventory(), items);
|
||||
public static String getSerializedEffectData(Player player) {
|
||||
PotionEffect[] potionEffects = new PotionEffect[player.getActivePotionEffects().size()];
|
||||
int x = 0;
|
||||
for (PotionEffect effect : player.getActivePotionEffects()) {
|
||||
potionEffects[x] = effect;
|
||||
x++;
|
||||
}
|
||||
return effectArrayToBase64(potionEffects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player's ender chest from a set of {@link ItemStack}s
|
||||
*
|
||||
* @param player The player to set the inventory of
|
||||
* @param items The array of {@link ItemStack}s to set
|
||||
*/
|
||||
public static void setPlayerEnderChest(Player player, ItemStack[] items) {
|
||||
setInventoryItems(player.getEnderChest(), items);
|
||||
}
|
||||
public static String effectArrayToBase64(PotionEffect[] effects) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream)) {
|
||||
dataOutput.writeInt(effects.length);
|
||||
|
||||
// Clears, then fills an inventory's items correctly.
|
||||
private static void setInventoryItems(Inventory inventory, ItemStack[] items) {
|
||||
inventory.clear();
|
||||
int index = 0;
|
||||
for (ItemStack item : items) {
|
||||
if (item != null) {
|
||||
inventory.setItem(index, item);
|
||||
for (PotionEffect effect : effects) {
|
||||
if (effect != null) {
|
||||
dataOutput.writeObject(effect.serialize());
|
||||
} else {
|
||||
dataOutput.writeObject(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
return Base64Coder.encodeLines(outputStream.toByteArray());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to save potion effects.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,4 +136,30 @@ public final class InventorySerializer {
|
||||
throw new IOException("Unable to decode class type.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static PotionEffect[] potionEffectArrayFromBase64(String data) throws IOException {
|
||||
// Return an empty PotionEffect[] if the data is empty
|
||||
if (data.isEmpty()) {
|
||||
return new PotionEffect[0];
|
||||
}
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data))) {
|
||||
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
|
||||
PotionEffect[] items = new PotionEffect[dataInput.readInt()];
|
||||
|
||||
for (int Index = 0; Index < items.length; Index++) {
|
||||
@SuppressWarnings("unchecked") // Ignore the unchecked cast here
|
||||
Map<String, Object> effect = (Map<String, Object>) dataInput.readObject();
|
||||
|
||||
if (effect != null) {
|
||||
items[Index] = new PotionEffect(effect);
|
||||
} else {
|
||||
items[Index] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IOException("Unable to decode class type.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package me.william278.crossserversync.bukkit;
|
||||
|
||||
import me.william278.crossserversync.CrossServerSyncBukkit;
|
||||
import me.william278.crossserversync.PlayerData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class PlayerSetter {
|
||||
|
||||
private static final CrossServerSyncBukkit plugin = CrossServerSyncBukkit.getInstance();
|
||||
|
||||
/**
|
||||
* Set a player from their PlayerData
|
||||
*
|
||||
* @param player The {@link Player} to set
|
||||
* @param data The {@link PlayerData} to assign to the player
|
||||
*/
|
||||
public static void setPlayerFrom(Player player, PlayerData data) {
|
||||
try {
|
||||
setPlayerInventory(player, DataSerializer.itemStackArrayFromBase64(data.getSerializedInventory()));
|
||||
setPlayerEnderChest(player, DataSerializer.itemStackArrayFromBase64(data.getSerializedEnderChest()));
|
||||
player.setHealth(data.getHealth());
|
||||
player.setMaxHealth(data.getMaxHealth());
|
||||
player.setFoodLevel(data.getHunger());
|
||||
player.setSaturation(data.getSaturation());
|
||||
player.getInventory().setHeldItemSlot(data.getSelectedSlot());
|
||||
//todo potion effects not working
|
||||
setPlayerPotionEffects(player, DataSerializer.potionEffectArrayFromBase64(data.getSerializedEffectData()));
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to deserialize PlayerData", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player's ender chest from a set of {@link ItemStack}s
|
||||
*
|
||||
* @param player The player to set the inventory of
|
||||
* @param items The array of {@link ItemStack}s to set
|
||||
*/
|
||||
private static void setPlayerEnderChest(Player player, ItemStack[] items) {
|
||||
player.getEnderChest().clear();
|
||||
int index = 0;
|
||||
for (ItemStack item : items) {
|
||||
if (item != null) {
|
||||
player.getEnderChest().setItem(index, item);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player's inventory from a set of {@link ItemStack}s
|
||||
*
|
||||
* @param player The player to set the inventory of
|
||||
* @param items The array of {@link ItemStack}s to set
|
||||
*/
|
||||
private static void setPlayerInventory(Player player, ItemStack[] items) {
|
||||
player.getInventory().clear();
|
||||
int index = 0;
|
||||
for (ItemStack item : items) {
|
||||
if (item != null) {
|
||||
player.getInventory().setItem(index, item);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a player's current potion effects from a set of {@link PotionEffect[]}
|
||||
* @param player The player to set the potion effects of
|
||||
* @param effects The array of {@link PotionEffect}s to set
|
||||
*/
|
||||
private static void setPlayerPotionEffects(Player player, PotionEffect[] effects) {
|
||||
player.getActivePotionEffects().clear();
|
||||
for (PotionEffect effect : effects) {
|
||||
player.getActivePotionEffects().add(effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
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.CrossServerSyncBukkit;
|
||||
import me.william278.crossserversync.bukkit.PlayerSetter;
|
||||
import me.william278.crossserversync.redis.RedisListener;
|
||||
import me.william278.crossserversync.redis.RedisMessage;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -29,20 +29,19 @@ public class BukkitRedisListener extends RedisListener {
|
||||
@Override
|
||||
public void handleMessage(RedisMessage message) {
|
||||
// Ignore messages for proxy servers
|
||||
if (message.getMessageTarget().targetServerType() != Settings.ServerType.BUKKIT) {
|
||||
if (!message.getMessageTarget().targetServerType().equals(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) {
|
||||
if (player.getUniqueId().equals(message.getMessageTarget().targetPlayerUUID())) {
|
||||
if (message.getMessageType().equals(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()));
|
||||
// Set the player's data
|
||||
PlayerSetter.setPlayerFrom(player, data);
|
||||
|
||||
// Update last loaded data UUID
|
||||
CrossServerSyncBukkit.lastDataUpdateUUIDCache.setVersionUUID(player.getUniqueId(), data.getDataVersionUUID());
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package me.william278.crossserversync.bukkit.listener;
|
||||
|
||||
import me.william278.crossserversync.CrossServerSyncBukkit;
|
||||
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.bukkit.DataSerializer;
|
||||
import me.william278.crossserversync.redis.RedisMessage;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -27,8 +27,14 @@ public class EventListener implements Listener {
|
||||
*/
|
||||
private static String getNewSerializedPlayerData(Player player) throws IOException {
|
||||
return RedisMessage.serialize(new PlayerData(player.getUniqueId(),
|
||||
InventorySerializer.getSerializedInventoryContents(player),
|
||||
InventorySerializer.getSerializedEnderChestContents(player)));
|
||||
DataSerializer.getSerializedInventoryContents(player),
|
||||
DataSerializer.getSerializedEnderChestContents(player),
|
||||
player.getHealth(),
|
||||
player.getMaxHealth(),
|
||||
player.getFoodLevel(),
|
||||
player.getSaturation(),
|
||||
player.getInventory().getHeldItemSlot(),
|
||||
DataSerializer.getSerializedEffectData(player)));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -42,15 +48,16 @@ public class EventListener implements Listener {
|
||||
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
|
||||
final String serializedPlayerData = getNewSerializedPlayerData(player);
|
||||
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_UPDATE,
|
||||
new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null),
|
||||
lastUpdatedDataVersion.toString(), getNewSerializedPlayerData(player)).send();
|
||||
serializedPlayerData).send();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to send a PlayerData update to the proxy", e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
// When a player joins a Bukkit server
|
||||
final Player player = event.getPlayer();
|
||||
|
||||
Reference in New Issue
Block a user