9
0
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:
William
2021-10-21 02:18:26 +01:00
parent ec6f85250d
commit ba8e4ee175
21 changed files with 281 additions and 155 deletions

View File

@@ -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());
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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());

View File

@@ -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();