Add advancement, location and flight syncing, fix an issue that sometimes led to inconsistent syncs

feat/data-edit-commands
William 3 years ago
parent bd316c0b8c
commit 520f1ea1d7

@ -1,8 +1,9 @@
package me.william278.crossserversync.bukkit;
import me.william278.crossserversync.redis.RedisMessage;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@ -15,10 +16,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -120,6 +118,7 @@ public final class DataSerializer {
* @return ItemStack array created from the Base64 string.
* @throws IOException in the event the class type cannot be decoded
*/
@SuppressWarnings("unchecked") // Ignore the unchecked cast here
public static ItemStack[] itemStackArrayFromBase64(String data) throws IOException {
// Return an empty ItemStack[] if the data is empty
if (data.isEmpty()) {
@ -130,7 +129,6 @@ public final class DataSerializer {
ItemStack[] items = new ItemStack[dataInput.readInt()];
for (int Index = 0; Index < items.length; Index++) {
@SuppressWarnings("unchecked") // Ignore the unchecked cast here
Map<String, Object> stack = (Map<String, Object>) dataInput.readObject();
if (stack != null) {
@ -146,6 +144,7 @@ public final class DataSerializer {
}
}
@SuppressWarnings("unchecked") // Ignore the unchecked cast here
public static PotionEffect[] potionEffectArrayFromBase64(String data) throws IOException {
// Return an empty PotionEffect[] if the data is empty
if (data.isEmpty()) {
@ -156,7 +155,6 @@ public final class DataSerializer {
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) {
@ -172,6 +170,57 @@ public final class DataSerializer {
}
}
public static PlayerLocation deserializePlayerLocationData(String serializedLocationData) throws IOException {
if (serializedLocationData.isEmpty()) {
return null;
}
try {
return (PlayerLocation) RedisMessage.deserialize(serializedLocationData);
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
}
public static String getSerializedLocation(Player player) throws IOException {
final Location playerLocation = player.getLocation();
return RedisMessage.serialize(new PlayerLocation(playerLocation.getX(), playerLocation.getY(), playerLocation.getZ(),
playerLocation.getYaw(), playerLocation.getPitch(), player.getWorld().getName(), player.getWorld().getEnvironment()));
}
public record PlayerLocation(double x, double y, double z, float yaw, float pitch,
String worldName, World.Environment environment) implements Serializable {
}
@SuppressWarnings("unchecked") // Ignore the unchecked cast here
public static ArrayList<AdvancementRecord> deserializeAdvancementData(String serializedAdvancementData) throws IOException {
if (serializedAdvancementData.isEmpty()) {
return new ArrayList<>();
}
try {
return (ArrayList<AdvancementRecord>) RedisMessage.deserialize(serializedAdvancementData);
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
}
public static String getSerializedAdvancements(Player player) throws IOException {
Iterator<Advancement> serverAdvancements = Bukkit.getServer().advancementIterator();
ArrayList<AdvancementRecord> advancementData = new ArrayList<>();
while (serverAdvancements.hasNext()) {
final AdvancementProgress progress = player.getAdvancementProgress(serverAdvancements.next());
final NamespacedKey advancementKey = progress.getAdvancement().getKey();
final ArrayList<String> awardedCriteria = new ArrayList<>(progress.getAwardedCriteria());
advancementData.add(new AdvancementRecord(advancementKey.getNamespace() + ":" + advancementKey.getKey(), awardedCriteria));
}
return RedisMessage.serialize(advancementData);
}
public record AdvancementRecord(String advancementKey,
ArrayList<String> awardedAdvancementCriteria) implements Serializable {
}
public static StatisticData deserializeStatisticData(String serializedStatisticData) throws IOException {
if (serializedStatisticData.isEmpty()) {
return new StatisticData(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
@ -184,28 +233,28 @@ public final class DataSerializer {
}
public static String getSerializedStatisticData(Player player) throws IOException {
HashMap<Statistic,Integer> untypedStatisticValues = new HashMap<>();
HashMap<Statistic,HashMap<Material,Integer>> blockStatisticValues = new HashMap<>();
HashMap<Statistic,HashMap<Material,Integer>> itemStatisticValues = new HashMap<>();
HashMap<Statistic,HashMap<EntityType,Integer>> entityStatisticValues = new HashMap<>();
HashMap<Statistic, Integer> untypedStatisticValues = new HashMap<>();
HashMap<Statistic, HashMap<Material, Integer>> blockStatisticValues = new HashMap<>();
HashMap<Statistic, HashMap<Material, Integer>> itemStatisticValues = new HashMap<>();
HashMap<Statistic, HashMap<EntityType, Integer>> entityStatisticValues = new HashMap<>();
for (Statistic statistic : Statistic.values()) {
switch (statistic.getType()) {
case ITEM -> {
HashMap<Material,Integer> itemValues = new HashMap<>();
HashMap<Material, Integer> itemValues = new HashMap<>();
for (Material itemMaterial : Arrays.stream(Material.values()).filter(Material::isItem).collect(Collectors.toList())) {
itemValues.put(itemMaterial, player.getStatistic(statistic, itemMaterial));
}
itemStatisticValues.put(statistic, itemValues);
}
case BLOCK -> {
HashMap<Material,Integer> blockValues = new HashMap<>();
HashMap<Material, Integer> blockValues = new HashMap<>();
for (Material blockMaterial : Arrays.stream(Material.values()).filter(Material::isBlock).collect(Collectors.toList())) {
blockValues.put(blockMaterial, player.getStatistic(statistic, blockMaterial));
}
blockStatisticValues.put(statistic, blockValues);
}
case ENTITY -> {
HashMap<EntityType,Integer> entityValues = new HashMap<>();
HashMap<EntityType, Integer> entityValues = new HashMap<>();
for (EntityType type : Arrays.stream(EntityType.values()).filter(EntityType::isAlive).collect(Collectors.toList())) {
entityValues.put(type, player.getStatistic(statistic, type));
}
@ -219,8 +268,9 @@ public final class DataSerializer {
return RedisMessage.serialize(statisticData);
}
public record StatisticData(HashMap<Statistic,Integer> untypedStatisticValues,
HashMap<Statistic,HashMap<Material,Integer>> blockStatisticValues,
HashMap<Statistic,HashMap<Material,Integer>> itemStatisticValues,
HashMap<Statistic,HashMap<EntityType,Integer>> entityStatisticValues) implements Serializable { }
public record StatisticData(HashMap<Statistic, Integer> untypedStatisticValues,
HashMap<Statistic, HashMap<Material, Integer>> blockStatisticValues,
HashMap<Statistic, HashMap<Material, Integer>> itemStatisticValues,
HashMap<Statistic, HashMap<EntityType, Integer>> entityStatisticValues) implements Serializable {
}
}

@ -6,10 +6,9 @@ import me.william278.crossserversync.MessageStrings;
import me.william278.crossserversync.PlayerData;
import me.william278.crossserversync.Settings;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
@ -17,6 +16,8 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;
import java.util.logging.Level;
@ -39,6 +40,10 @@ public class PlayerSetter {
// Set the player's data from the PlayerData
Bukkit.getScheduler().runTask(plugin, () -> {
try {
if (Settings.syncAdvancements) {
// Sync advancements first so that any rewards will be overridden
setPlayerAdvancements(player, DataSerializer.deserializeAdvancementData(data.getSerializedAdvancements()));
}
if (Settings.syncInventories) {
setPlayerInventory(player, DataSerializer.itemStackArrayFromBase64(data.getSerializedInventory()));
player.getInventory().setHeldItemSlot(data.getSelectedSlot());
@ -69,6 +74,10 @@ public class PlayerSetter {
if (Settings.syncGameMode) {
player.setGameMode(GameMode.valueOf(data.getGameMode()));
}
if (Settings.syncLocation) {
player.setFlying(player.getAllowFlight() && data.isFlying());
setPlayerLocation(player, DataSerializer.deserializePlayerLocationData(data.getSerializedLocation()));
}
// Send action bar synchronisation message
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new MineDown(MessageStrings.SYNCHRONISATION_COMPLETE).toComponent());
@ -114,7 +123,8 @@ public class PlayerSetter {
/**
* Set a player's current potion effects from a set of {@link PotionEffect[]}
* @param player The player to set the potion effects of
*
* @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) {
@ -126,9 +136,70 @@ public class PlayerSetter {
}
}
/**
* Update a player's advancements and progress to match the advancementData
*
* @param player The player to set the advancements of
* @param advancementData The ArrayList of {@link DataSerializer.AdvancementRecord}s to set
*/
private static void setPlayerAdvancements(Player player, ArrayList<DataSerializer.AdvancementRecord> advancementData) {
// Temporarily disable advancement announcing if needed
boolean announceAdvancementUpdate = false;
if (Boolean.TRUE.equals(player.getWorld().getGameRuleValue(GameRule.ANNOUNCE_ADVANCEMENTS))) {
player.getWorld().setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false);
announceAdvancementUpdate = true;
}
final boolean finalAnnounceAdvancementUpdate = announceAdvancementUpdate;
// Run async because advancement loading is very slow
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
// Apply the advancements to the player
Iterator<Advancement> serverAdvancements = Bukkit.getServer().advancementIterator();
while (serverAdvancements.hasNext()) {
Advancement advancement = serverAdvancements.next();
AdvancementProgress playerProgress = player.getAdvancementProgress(advancement);
boolean hasAdvancement = false;
for (DataSerializer.AdvancementRecord record : advancementData) {
if (record.advancementKey().equals(advancement.getKey().getNamespace() + ":" + advancement.getKey().getKey())) {
hasAdvancement = true;
// Save the experience before granting the advancement
final int expLevel = player.getLevel();
final float expProgress = player.getExp();
// Grant advancement criteria if the player does not have it
for (String awardCriteria : record.awardedAdvancementCriteria()) {
if (!playerProgress.getAwardedCriteria().contains(awardCriteria)) {
Bukkit.getScheduler().runTask(plugin, () -> player.getAdvancementProgress(advancement).awardCriteria(awardCriteria));
}
}
// Set experience back to before granting advancement; nullify exp gained from it
player.setLevel(expLevel);
player.setExp(expProgress);
break;
}
}
if (!hasAdvancement) {
for (String awardCriteria : playerProgress.getAwardedCriteria()) {
Bukkit.getScheduler().runTask(plugin, () -> player.getAdvancementProgress(advancement).revokeCriteria(awardCriteria));
}
}
}
// Re-enable announcing advancements (back on main thread again)
Bukkit.getScheduler().runTask(plugin, () -> {
if (finalAnnounceAdvancementUpdate) {
player.getWorld().setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, true);
}
});
});
}
/**
* Set a player's statistics (in the Statistic menu)
* @param player The player to set the statistics of
*
* @param player The player to set the statistics of
* @param statisticData The {@link DataSerializer.StatisticData} to set
*/
private static void setPlayerStatistics(Player player, DataSerializer.StatisticData statisticData) {
@ -158,4 +229,37 @@ public class PlayerSetter {
}
}
}
/**
* Set a player's location from {@link DataSerializer.PlayerLocation} data
*
* @param player The {@link Player} to teleport
* @param location The {@link DataSerializer.PlayerLocation}
*/
private static void setPlayerLocation(Player player, DataSerializer.PlayerLocation location) {
// Don't teleport if the location is invalid
if (location == null) {
return;
}
// Determine the world; if the names match, use that
World world = Bukkit.getWorld(location.worldName());
if (world == null) {
// If the names don't match, find the corresponding world with the same dimension environment
for (World worldOnServer : Bukkit.getWorlds()) {
if (worldOnServer.getEnvironment().equals(location.environment())) {
world = worldOnServer;
}
}
// If that still fails, return
if (world == null) {
return;
}
}
// Teleport the player
player.teleport(new Location(world, location.x(), location.y(), location.z(), location.yaw(), location.pitch()));
}
}

@ -19,7 +19,8 @@ public class ConfigLoader {
Settings.syncPotionEffects = config.getBoolean("synchronisation_settings.potion_effects", true);
Settings.syncStatistics = config.getBoolean("synchronisation_settings.statistics", true);
Settings.syncGameMode = config.getBoolean("synchronisation_settings.game_mode", true);
Settings.syncAdvancements = config.getBoolean("synchronisation_settings.advancements", true);
Settings.syncLocation = config.getBoolean("synchronisation_settings.location", false);
}
}

@ -35,6 +35,8 @@ public class BukkitRedisListener extends RedisListener {
if (!message.getMessageTarget().targetServerType().equals(Settings.ServerType.BUKKIT)) {
return;
}
// Handle the message for the player
if (message.getMessageTarget().targetPlayerUUID() == null) {
if (message.getMessageType() == RedisMessage.MessageType.REQUEST_DATA_ON_JOIN) {

@ -14,7 +14,6 @@ import org.bukkit.event.player.PlayerQuitEvent;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;
import java.util.logging.Level;
public class EventListener implements Listener {
@ -23,6 +22,7 @@ public class EventListener implements Listener {
/**
* 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
@ -42,7 +42,10 @@ public class EventListener implements Listener {
player.getLevel(),
player.getExp(),
player.getGameMode().toString(),
DataSerializer.getSerializedStatisticData(player)));
DataSerializer.getSerializedStatisticData(player),
player.isFlying(),
DataSerializer.getSerializedAdvancements(player),
DataSerializer.getSerializedLocation(player)));
}
@EventHandler
@ -52,8 +55,8 @@ public class EventListener implements Listener {
try {
// Get the player's last updated PlayerData version UUID
final UUID lastUpdatedDataVersion = CrossServerSyncBukkit.bukkitCache.getVersionUUID(player.getUniqueId());
if (lastUpdatedDataVersion == null) return; // Return if the player has not been properly updated.
//final UUID lastUpdatedDataVersion = CrossServerSyncBukkit.bukkitCache.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
final String serializedPlayerData = getNewSerializedPlayerData(player);
@ -63,13 +66,24 @@ public class EventListener implements Listener {
} 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();
// Set data version ID to null
CrossServerSyncBukkit.bukkitCache.setVersionUUID(player.getUniqueId(), null);
}
@EventHandler
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
// When a player joins a Bukkit server
final Player player = event.getPlayer();
// Clear player inventory and ender chest
player.getInventory().clear();
player.getEnderChest().clear();
if (CrossServerSyncBukkit.bukkitCache.isPlayerRequestingOnJoin(player.getUniqueId())) {
try {
// Send a redis message requesting the player data

@ -10,4 +10,6 @@ synchronisation_settings:
experience: true
potion_effects: true
statistics: true
game_mode: true
game_mode: true
advancements: true
location: false

@ -80,10 +80,16 @@ public class DataManager {
final int expLevel = resultSet.getInt("exp_level");
final float expProgress = resultSet.getInt("exp_progress");
final String gameMode = resultSet.getString("game_mode");
final boolean isFlying = resultSet.getBoolean("is_flying");
final String serializedAdvancementData = resultSet.getString("advancements");
final String serializedLocationData = resultSet.getString( "location");
final String serializedStatisticData = resultSet.getString("statistics");
return new PlayerData(playerUUID, dataVersionUUID, serializedInventory, serializedEnderChest, health, maxHealth, hunger, saturation, saturationExhaustion, selectedSlot, serializedStatusEffects, totalExperience, expLevel, expProgress, gameMode, serializedStatisticData);
return new PlayerData(playerUUID, dataVersionUUID, serializedInventory, serializedEnderChest,
health, maxHealth, hunger, saturation, saturationExhaustion, selectedSlot, serializedStatusEffects,
totalExperience, expLevel, expProgress, gameMode, serializedStatisticData, isFlying,
serializedAdvancementData, serializedLocationData);
} else {
return PlayerData.DEFAULT_PLAYER_DATA(playerUUID);
}
@ -111,7 +117,7 @@ public class DataManager {
private static void updatePlayerSQLData(PlayerData playerData) {
try (Connection connection = CrossServerSyncBungeeCord.getConnection()) {
try (PreparedStatement statement = connection.prepareStatement(
"UPDATE " + Database.DATA_TABLE_NAME + " SET `version_uuid`=?, `timestamp`=?, `inventory`=?, `ender_chest`=?, `health`=?, `max_health`=?, `hunger`=?, `saturation`=?, `saturation_exhaustion`=?, `selected_slot`=?, `status_effects`=?, `total_experience`=?, `exp_level`=?, `exp_progress`=?, `game_mode`=?, `statistics`=? WHERE `player_id`=(SELECT `id` FROM " + Database.PLAYER_TABLE_NAME + " WHERE `uuid`=?);")) {
"UPDATE " + Database.DATA_TABLE_NAME + " SET `version_uuid`=?, `timestamp`=?, `inventory`=?, `ender_chest`=?, `health`=?, `max_health`=?, `hunger`=?, `saturation`=?, `saturation_exhaustion`=?, `selected_slot`=?, `status_effects`=?, `total_experience`=?, `exp_level`=?, `exp_progress`=?, `game_mode`=?, `statistics`=?, `is_flying`=?, `advancements`=?, `location`=? WHERE `player_id`=(SELECT `id` FROM " + Database.PLAYER_TABLE_NAME + " WHERE `uuid`=?);")) {
statement.setString(1, playerData.getDataVersionUUID().toString());
statement.setTimestamp(2, new Timestamp(Instant.now().getEpochSecond()));
statement.setString(3, playerData.getSerializedInventory());
@ -128,8 +134,11 @@ public class DataManager {
statement.setFloat(14, playerData.getExpProgress()); // Exp progress
statement.setString(15, playerData.getGameMode()); // GameMode
statement.setString(16, playerData.getSerializedStatistics()); // Statistics
statement.setBoolean(17, playerData.isFlying()); // Is flying
statement.setString(18, playerData.getSerializedAdvancements()); // Advancements
statement.setString(19, playerData.getSerializedLocation()); // Location
statement.setString(17, playerData.getPlayerUUID().toString());
statement.setString(20, playerData.getPlayerUUID().toString());
statement.executeUpdate();
}
} catch (SQLException e) {
@ -140,7 +149,7 @@ public class DataManager {
private static void insertPlayerData(PlayerData playerData) {
try (Connection connection = CrossServerSyncBungeeCord.getConnection()) {
try (PreparedStatement statement = connection.prepareStatement(
"INSERT INTO " + Database.DATA_TABLE_NAME + " (`player_id`,`version_uuid`,`timestamp`,`inventory`,`ender_chest`,`health`,`max_health`,`hunger`,`saturation`,`saturation_exhaustion`,`selected_slot`,`status_effects`,`total_experience`,`exp_level`,`exp_progress`,`game_mode`,`statistics`) VALUES((SELECT `id` FROM " + Database.PLAYER_TABLE_NAME + " WHERE `uuid`=?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);")) {
"INSERT INTO " + Database.DATA_TABLE_NAME + " (`player_id`,`version_uuid`,`timestamp`,`inventory`,`ender_chest`,`health`,`max_health`,`hunger`,`saturation`,`saturation_exhaustion`,`selected_slot`,`status_effects`,`total_experience`,`exp_level`,`exp_progress`,`game_mode`,`statistics`,`is_flying`,`advancements`,`location`) VALUES((SELECT `id` FROM " + Database.PLAYER_TABLE_NAME + " WHERE `uuid`=?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);")) {
statement.setString(1, playerData.getPlayerUUID().toString());
statement.setString(2, playerData.getDataVersionUUID().toString());
statement.setTimestamp(3, new Timestamp(Instant.now().getEpochSecond()));
@ -157,8 +166,10 @@ public class DataManager {
statement.setInt(14, playerData.getExpLevel()); // Exp level
statement.setFloat(15, playerData.getExpProgress()); // Exp progress
statement.setString(16, playerData.getGameMode()); // GameMode
statement.setString(17, playerData.getSerializedStatistics()); // Statistics
statement.setBoolean(18, playerData.isFlying()); // Is flying
statement.setString(19, playerData.getSerializedAdvancements()); // Advancements
statement.setString(20, playerData.getSerializedLocation()); // Location
statement.executeUpdate();
}
@ -234,22 +245,5 @@ public class DataManager {
return null;
}
/**
* Remove a player's {@link PlayerData} from the cache
* @param playerUUID The UUID of the player to remove
*/
public void removePlayer(UUID playerUUID) {
PlayerData dataToRemove = null;
for (PlayerData data : playerData) {
if (data.getPlayerUUID() == playerUUID) {
dataToRemove = data;
break;
}
}
if (dataToRemove != null) {
playerData.remove(dataToRemove);
}
}
}
}

@ -37,6 +37,9 @@ public class MySQL extends Database {
"`exp_progress` float NOT NULL," +
"`game_mode` tinytext NOT NULL," +
"`statistics` longtext NOT NULL," +
"`is_flying` boolean NOT NULL," +
"`advancements` longtext NOT NULL," +
"`location` text NOT NULL," +
"PRIMARY KEY (`player_id`,`uuid`)," +
"FOREIGN KEY (`player_id`) REFERENCES " + PLAYER_TABLE_NAME + " (`id`)" +

@ -45,6 +45,9 @@ public class SQLite extends Database {
"`exp_progress` float NOT NULL," +
"`game_mode` tinytext NOT NULL," +
"`statistics` longtext NOT NULL," +
"`is_flying` boolean NOT NULL," +
"`advancements` longtext NOT NULL," +
"`location` text NOT NULL," +
"PRIMARY KEY (`player_id`,`version_uuid`)" +
");"

@ -1,6 +1,5 @@
package me.william278.crossserversync.bungeecord.listener;
import de.themoep.minedown.MineDown;
import me.william278.crossserversync.CrossServerSyncBungeeCord;
import me.william278.crossserversync.PlayerData;
import me.william278.crossserversync.Settings;
@ -88,20 +87,20 @@ public class BungeeRedisListener extends RedisListener {
// Update the data in the cache and SQL
DataManager.updatePlayerData(playerData);
// Reply to set the player's data if they moved servers
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(playerData.getPlayerUUID());
if (player != null) {
if (player.isConnected()) {
// Send the reply, serializing the message data
try {
// Reply with the player data if they are still online (switching server)
try {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(playerData.getPlayerUUID());
if (player != null) {
if (player.isConnected()) {
new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_SET,
new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, playerData.getPlayerUUID()),
RedisMessage.serialize(playerData)).send();
} catch (IOException e) {
log(Level.SEVERE, "Failed to serialize data when replying to a data update");
e.printStackTrace();
RedisMessage.serialize(playerData))
.send();
}
}
} catch (IOException e) {
log(Level.SEVERE, "Failed to re-serialize PlayerData when handling a player update request");
e.printStackTrace();
}
}
}

@ -33,26 +33,33 @@ public class PlayerData implements Serializable {
private final float expProgress;
private final String gameMode;
private final String serializedStatistics;
private final boolean isFlying;
private final String serializedAdvancements;
private final String serializedLocation;
/**
* Constructor to create new PlayerData from a bukkit {@code Player}'s data
* @param playerUUID The Player's UUID
* @param serializedInventory Their serialized inventory
* @param serializedEnderChest Their serialized ender chest
* @param health Their health
* @param maxHealth Their max health
* @param hunger Their hunger
* @param saturation Their saturation
* @param saturationExhaustion Their saturation exhaustion
* @param selectedSlot Their selected hot bar slot
*
* @param playerUUID The Player's UUID
* @param serializedInventory Their serialized inventory
* @param serializedEnderChest Their serialized ender chest
* @param health Their health
* @param maxHealth Their max health
* @param hunger Their hunger
* @param saturation Their saturation
* @param saturationExhaustion Their saturation exhaustion
* @param selectedSlot Their selected hot bar slot
* @param serializedStatusEffects Their serialized status effects
* @param totalExperience Their total experience points ("Score")
* @param expLevel Their exp level
* @param expProgress Their exp progress to the next level
* @param gameMode Their game mode ({@code SURVIVAL}, {@code CREATIVE}, etc)
* @param serializedStatistics Their serialized statistics data (Displayed in Statistics menu in ESC menu)
* @param totalExperience Their total experience points ("Score")
* @param expLevel Their exp level
* @param expProgress Their exp progress to the next level
* @param gameMode Their game mode ({@code SURVIVAL}, {@code CREATIVE}, etc)
* @param serializedStatistics Their serialized statistics data (Displayed in Statistics menu in ESC menu)
*/
public PlayerData(UUID playerUUID, String serializedInventory, String serializedEnderChest, double health, double maxHealth, int hunger, float saturation, float saturationExhaustion, int selectedSlot, String serializedStatusEffects, int totalExperience, int expLevel, float expProgress, String gameMode, String serializedStatistics) {
public PlayerData(UUID playerUUID, String serializedInventory, String serializedEnderChest, double health,
double maxHealth, int hunger, float saturation, float saturationExhaustion, int selectedSlot,
String serializedStatusEffects, int totalExperience, int expLevel, float expProgress, String gameMode,
String serializedStatistics, boolean isFlying, String serializedAdvancements, String serializedLocation) {
this.dataVersionUUID = UUID.randomUUID();
this.playerUUID = playerUUID;
this.serializedInventory = serializedInventory;
@ -69,28 +76,36 @@ public class PlayerData implements Serializable {
this.expProgress = expProgress;
this.gameMode = gameMode;
this.serializedStatistics = serializedStatistics;
this.isFlying = isFlying;
this.serializedAdvancements = serializedAdvancements;
this.serializedLocation = serializedLocation;
}
/**
* Constructor for a PlayerData object from an existing object that was stored in SQL
* @param playerUUID The player whose data this is' UUID
* @param dataVersionUUID The PlayerData version UUID
* @param serializedInventory Their serialized inventory
* @param serializedEnderChest Their serialized ender chest
* @param health Their health
* @param maxHealth Their max health
* @param hunger Their hunger
* @param saturation Their saturation
* @param saturationExhaustion Their saturation exhaustion
* @param selectedSlot Their selected hot bar slot
*
* @param playerUUID The player whose data this is' UUID
* @param dataVersionUUID The PlayerData version UUID
* @param serializedInventory Their serialized inventory
* @param serializedEnderChest Their serialized ender chest
* @param health Their health
* @param maxHealth Their max health
* @param hunger Their hunger
* @param saturation Their saturation
* @param saturationExhaustion Their saturation exhaustion
* @param selectedSlot Their selected hot bar slot
* @param serializedStatusEffects Their serialized status effects
* @param totalExperience Their total experience points ("Score")
* @param expLevel Their exp level
* @param expProgress Their exp progress to the next level
* @param gameMode Their game mode ({@code SURVIVAL}, {@code CREATIVE}, etc)
* @param serializedStatistics Their serialized statistics data (Displayed in Statistics menu in ESC menu)
* @param totalExperience Their total experience points ("Score")
* @param expLevel Their exp level
* @param expProgress Their exp progress to the next level
* @param gameMode Their game mode ({@code SURVIVAL}, {@code CREATIVE}, etc)
* @param serializedStatistics Their serialized statistics data (Displayed in Statistics menu in ESC menu)
*/
public PlayerData(UUID playerUUID, UUID dataVersionUUID, String serializedInventory, String serializedEnderChest, double health, double maxHealth, int hunger, float saturation, float saturationExhaustion, int selectedSlot, String serializedStatusEffects, int totalExperience, int expLevel, float expProgress, String gameMode, String serializedStatistics) {
public PlayerData(UUID playerUUID, UUID dataVersionUUID, String serializedInventory, String serializedEnderChest,
double health, double maxHealth, int hunger, float saturation, float saturationExhaustion,
int selectedSlot, String serializedStatusEffects, int totalExperience, int expLevel, float expProgress,
String gameMode, String serializedStatistics, boolean isFlying, String serializedAdvancements,
String serializedLocation) {
this.playerUUID = playerUUID;
this.dataVersionUUID = dataVersionUUID;
this.serializedInventory = serializedInventory;
@ -107,17 +122,22 @@ public class PlayerData implements Serializable {
this.expProgress = expProgress;
this.gameMode = gameMode;
this.serializedStatistics = serializedStatistics;
this.isFlying = isFlying;
this.serializedAdvancements = serializedAdvancements;
this.serializedLocation = serializedLocation;
}
/**
* Get default PlayerData for a new user
*
* @param playerUUID The bukkit Player's UUID
* @return Default {@link PlayerData}
*/
public static PlayerData DEFAULT_PLAYER_DATA(UUID playerUUID) {
PlayerData data = new PlayerData(playerUUID, "", "", 20,
20, 20, 10, 1, 0,
"", 0, 0, 0, "SURVIVAL", "");
"", 0, 0, 0, "SURVIVAL",
"", false, "", "");
data.useDefaultData = true;
return data;
}
@ -186,7 +206,20 @@ public class PlayerData implements Serializable {
return gameMode;
}
public boolean isFlying() {
return isFlying;
}
public String getSerializedAdvancements() {
return serializedAdvancements;
}
public String getSerializedLocation() {
return serializedLocation;
}
public boolean isUseDefaultData() {
return useDefaultData;
}
}

@ -49,6 +49,8 @@ public class Settings {
public static boolean syncPotionEffects;
public static boolean syncStatistics;
public static boolean syncGameMode;
public static boolean syncAdvancements;
public static boolean syncLocation;
/*
* Enum definitions

Loading…
Cancel
Save