diff --git a/README.md b/README.md index 62c2f9c7..4c18f860 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,20 @@ ItemStack[] inventoryItems = DataSerializer.serializeInventory(playerData.getSer ItemStack[] enderChestItems = DataSerializer.serializeInventory(playerData.getSerializedEnderChest()); ``` +#### Updating PlayerData +You can then update PlayerData back to the central cache using the `HuskSyncAPI#updatePlayerData(playerData)` method. For example: +``` +// Update a value in the player data object +playerData.setHealth(20); +try { + // Update the player data to the cache + huskSyncApi.updatePlayerData(playerData); +} catch (IOException e) { + Bukkit.getLogger().severe("An error occurred updating player data!"); +} +``` + + ### Contributing A code bounty program is in place for HuskSync, where developers making significant code contributions to HuskSync may be entitled to a discretionary license to use HuskSync in commercial contexts without having to purchase the resource, so please feel free to submit pull requests with improvements, fixes and features! @@ -207,4 +221,4 @@ You can turn metric collection off by navigating to `plugins/bStats/config.yml` ## Support * Report bugs: [Click here](https://github.com/WiIIiam278/HuskSync/issues) * Discord support: Join the [HuskHelp Discord](https://discord.gg/tVYhJfyDWG)! - * Proof of purchase is required for support. + * Proof of purchase is required for support. \ No newline at end of file diff --git a/api/src/main/java/me/william278/husksync/bukkit/api/HuskSyncAPI.java b/api/src/main/java/me/william278/husksync/bukkit/api/HuskSyncAPI.java index 3272e7df..c3dc8ade 100644 --- a/api/src/main/java/me/william278/husksync/bukkit/api/HuskSyncAPI.java +++ b/api/src/main/java/me/william278/husksync/bukkit/api/HuskSyncAPI.java @@ -57,4 +57,17 @@ public class HuskSyncAPI { return playerDataCompletableFuture; } + /** + * Updates a player's {@link PlayerData} to the central cache and database. If the player is online on the Proxy network, they will be updated and overwritten with this data. + * + * @param playerData The {@link PlayerData} (which contains the {@link UUID}) of the player data to update to the central cache and database + * @throws IOException If an exception occurs with serializing during processing of the update + */ + public void updatePlayerData(PlayerData playerData) throws IOException { + final String serializedPlayerData = RedisMessage.serialize(playerData); + new RedisMessage(RedisMessage.MessageType.PLAYER_DATA_UPDATE, + new RedisMessage.MessageTarget(Settings.ServerType.PROXY, null, Settings.cluster), + serializedPlayerData).send(); + } + } \ No newline at end of file diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/util/PlayerSetter.java b/bukkit/src/main/java/me/william278/husksync/bukkit/util/PlayerSetter.java index 9be2ffba..875417f7 100644 --- a/bukkit/src/main/java/me/william278/husksync/bukkit/util/PlayerSetter.java +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/util/PlayerSetter.java @@ -142,7 +142,7 @@ public class PlayerSetter { } // If the data is flagged as being default data, skip setting - if (data.isUseDefaultData()) { + if (data.useDefaultData) { HuskSyncBukkit.bukkitCache.removeAwaitingDataFetch(player.getUniqueId()); return; } diff --git a/common/src/main/java/me/william278/husksync/PlayerData.java b/common/src/main/java/me/william278/husksync/PlayerData.java index 030169b5..79817200 100644 --- a/common/src/main/java/me/william278/husksync/PlayerData.java +++ b/common/src/main/java/me/william278/husksync/PlayerData.java @@ -3,6 +3,9 @@ package me.william278.husksync; import java.io.*; import java.util.UUID; +/** + * Cross-platform class used to represent a player's data. Data from this can be deserialized using the DataSerializer class on Bukkit platforms. + */ public class PlayerData implements Serializable { /** @@ -15,10 +18,14 @@ public class PlayerData implements Serializable { */ private final UUID dataVersionUUID; - // Flag to indicate if the Bukkit server should use default data + /** + * A special flag that will be {@code true} if the player is new to the network and should not have their data set when joining the Bukkit + */ public boolean useDefaultData = false; - // Player data + /* + * Player data records + */ private String serializedInventory; private String serializedEnderChest; private double health; @@ -55,7 +62,7 @@ public class PlayerData implements Serializable { * @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 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, @@ -147,156 +154,344 @@ public class PlayerData implements Serializable { return data; } + /** + * Get the {@link UUID} of the player whose data this is + * + * @return the player's {@link UUID} + */ public UUID getPlayerUUID() { return playerUUID; } + /** + * Get the unique version {@link UUID} of the PlayerData + * + * @return The unique data version + */ public UUID getDataVersionUUID() { return dataVersionUUID; } + /** + * Returns the serialized player {@code ItemStack[]} inventory + * + * @return The player's serialized inventory + */ public String getSerializedInventory() { return serializedInventory; } + /** + * Returns the serialized player {@code ItemStack[]} ender chest + * + * @return The player's serialized ender chest + */ public String getSerializedEnderChest() { return serializedEnderChest; } + /** + * Returns the player's health value + * + * @return the player's health + */ public double getHealth() { return health; } + /** + * Returns the player's max health value + * + * @return the player's max health + */ public double getMaxHealth() { return maxHealth; } - public double getHealthScale() { return healthScale; } + /** + * Returns the player's health scale value {@see https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/Player.html#getHealthScale()} + * + * @return the player's health scaling value + */ + public double getHealthScale() { + return healthScale; + } + /** + * Returns the player's hunger points + * + * @return the player's hunger level + */ public int getHunger() { return hunger; } + /** + * Returns the player's saturation points + * + * @return the player's saturation level + */ public float getSaturation() { return saturation; } + /** + * Returns the player's saturation exhaustion value {@see https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/HumanEntity.html#getExhaustion()} + * + * @return the player's saturation exhaustion + */ public float getSaturationExhaustion() { return saturationExhaustion; } + /** + * Returns the number of the player's currently selected hotbar slot + * + * @return the player's selected hotbar slot + */ public int getSelectedSlot() { return selectedSlot; } + /** + * Returns a serialized {@link String} of the player's current status effects + * + * @return the player's serialized status effect data + */ public String getSerializedEffectData() { return serializedEffectData; } + /** + * Returns the player's total experience score (used for presenting the death screen score value) + * + * @return the player's total experience score + */ public int getTotalExperience() { return totalExperience; } + /** + * Returns a serialized {@link String} of the player's statistics + * + * @return the player's serialized statistic records + */ public String getSerializedStatistics() { return serializedStatistics; } + /** + * Returns the player's current experience level + * + * @return the player's exp level + */ public int getExpLevel() { return expLevel; } + /** + * Returns the player's progress to the next experience level + * + * @return the player's exp progress + */ public float getExpProgress() { return expProgress; } + /** + * Returns the player's current game mode as a string ({@code SURVIVAL}, {@code CREATIVE}, etc.) + * + * @return the player's game mode + */ public String getGameMode() { return gameMode; } + /** + * Returns if the player is currently flying + * + * @return {@code true} if the player is in flight; {@code false} otherwise + */ public boolean isFlying() { return isFlying; } + /** + * Returns a serialized {@link String} of the player's advancements + * + * @return the player's serialized advancement data + */ public String getSerializedAdvancements() { return serializedAdvancements; } + /** + * Returns a serialized {@link String} of the player's current location + * + * @return the player's serialized location + */ public String getSerializedLocation() { return serializedLocation; } - public boolean isUseDefaultData() { - return useDefaultData; - } - + /** + * Update the player's inventory data + * + * @param serializedInventory A serialized {@code String}; new inventory data + */ public void setSerializedInventory(String serializedInventory) { this.serializedInventory = serializedInventory; } + /** + * Update the player's ender chest data + * + * @param serializedEnderChest A serialized {@code String}; new ender chest inventory data + */ public void setSerializedEnderChest(String serializedEnderChest) { this.serializedEnderChest = serializedEnderChest; } + /** + * Update the player's health + * + * @param health new health value + */ public void setHealth(double health) { this.health = health; } + /** + * Update the player's max health + * + * @param maxHealth new maximum health value + */ public void setMaxHealth(double maxHealth) { this.maxHealth = maxHealth; } + /** + * Update the player's health scale + * + * @param healthScale new health scaling value + */ public void setHealthScale(double healthScale) { this.healthScale = healthScale; } + /** + * Update the player's hunger meter + * + * @param hunger new hunger value + */ public void setHunger(int hunger) { this.hunger = hunger; } + /** + * Update the player's saturation level + * + * @param saturation new saturation value + */ public void setSaturation(float saturation) { this.saturation = saturation; } + /** + * Update the player's saturation exhaustion value + * + * @param saturationExhaustion new exhaustion value + */ public void setSaturationExhaustion(float saturationExhaustion) { this.saturationExhaustion = saturationExhaustion; } + /** + * Update the player's selected hotbar slot + * + * @param selectedSlot new hotbar slot number (0-9) + */ public void setSelectedSlot(int selectedSlot) { this.selectedSlot = selectedSlot; } + /** + * Update the player's status effect data + * + * @param serializedEffectData A serialized {@code String} of the player's new status effect data + */ public void setSerializedEffectData(String serializedEffectData) { this.serializedEffectData = serializedEffectData; } + /** + * Set the player's total experience points (used to display score on death screen) + * + * @param totalExperience the player's new total experience score + */ public void setTotalExperience(int totalExperience) { this.totalExperience = totalExperience; } + /** + * Set the player's exp level + * + * @param expLevel the player's new exp level + */ public void setExpLevel(int expLevel) { this.expLevel = expLevel; } + /** + * Set the player's progress to their next exp level + * + * @param expProgress the player's new experience progress + */ public void setExpProgress(float expProgress) { this.expProgress = expProgress; } + /** + * Set the player's game mode + * + * @param gameMode the player's new game mode ({@code SURVIVAL}, {@code CREATIVE}, etc.) + */ public void setGameMode(String gameMode) { this.gameMode = gameMode; } + /** + * Update the player's statistics data + * + * @param serializedStatistics A serialized {@code String}; new statistic data + */ public void setSerializedStatistics(String serializedStatistics) { this.serializedStatistics = serializedStatistics; } + /** + * Set if the player is flying + * + * @param flying whether the player is flying + */ public void setFlying(boolean flying) { isFlying = flying; } + /** + * Update the player's advancement data + * + * @param serializedAdvancements A serialized {@code String}; new advancement data + */ public void setSerializedAdvancements(String serializedAdvancements) { this.serializedAdvancements = serializedAdvancements; } + /** + * Update the player's location data + * + * @param serializedLocation A serialized {@code String}; new location data + */ public void setSerializedLocation(String serializedLocation) { this.serializedLocation = serializedLocation; } diff --git a/common/src/main/java/me/william278/husksync/redis/RedisMessage.java b/common/src/main/java/me/william278/husksync/redis/RedisMessage.java index 8f7d7df5..951bbbb9 100644 --- a/common/src/main/java/me/william278/husksync/redis/RedisMessage.java +++ b/common/src/main/java/me/william278/husksync/redis/RedisMessage.java @@ -90,7 +90,7 @@ public class RedisMessage { */ public enum MessageType implements Serializable { /** - * Sent by Bukkit servers to proxy when a player disconnects with a player's updated data, alongside the UUID of the last loaded {@link PlayerData} for the user + * Sent by Bukkit servers to proxy when a user disconnects with that player's updated {@link PlayerData}. */ PLAYER_DATA_UPDATE, @@ -105,12 +105,12 @@ public class RedisMessage { PLAYER_DATA_SET, /** - * Sent by Bukkit servers to proxy to request {@link PlayerData} from the proxy via the API + * Sent by Bukkit servers to proxy to request {@link PlayerData} from the proxy via the API. */ API_DATA_REQUEST, /** - * Sent by the Proxy to fulfill an {@code MessageType.API_DATA_REQUEST}, containing the latest {@link PlayerData} for the requested UUID + * Sent by the Proxy to fulfill an {@code MessageType.API_DATA_REQUEST}, containing the latest {@link PlayerData} for the requested UUID. */ API_DATA_RETURN, @@ -120,47 +120,47 @@ public class RedisMessage { API_DATA_CANCEL, /** - * Sent by the proxy to a Bukkit server to have them request data on join; contains no data otherwise + * Sent by the proxy to a Bukkit server to have them request data on join; contains no data otherwise. */ REQUEST_DATA_ON_JOIN, /** - * Sent by the proxy to ask the Bukkit server to send the full plugin information, contains information about the proxy brand and version + * Sent by the proxy to ask the Bukkit server to send the full plugin information, contains information about the proxy brand and version. */ SEND_PLUGIN_INFORMATION, /** - * Sent by the proxy to show a player the contents of another player's inventory, contains their username and {@link PlayerData} + * Sent by the proxy to show a player the contents of another player's inventory, contains their username and {@link PlayerData}. */ OPEN_INVENTORY, /** - * Sent by the proxy to show a player the contents of another player's ender chest, contains their username and {@link PlayerData} + * Sent by the proxy to show a player the contents of another player's ender chest, contains their username and {@link PlayerData}. */ OPEN_ENDER_CHEST, /** - * Sent by both the proxy and bukkit servers to confirm cross-server communication has been established + * Sent by both the proxy and bukkit servers to confirm cross-server communication has been established. */ CONNECTION_HANDSHAKE, /** - * Sent by both the proxy and bukkit servers to terminate communications (if a bukkit / the proxy goes offline) + * Sent by both the proxy and bukkit servers to terminate communications (if a bukkit / the proxy goes offline). */ TERMINATE_HANDSHAKE, /** - * Sent by a proxy to a bukkit server to decode MPDB data + * Sent by a proxy to a bukkit server to decode MPDB data. */ DECODE_MPDB_DATA, /** - * Sent by a bukkit server back to the proxy with the correctly decoded MPDB data + * Sent by a bukkit server back to the proxy with the correctly decoded MPDB data. */ DECODED_MPDB_DATA_SET, /** - * Sent by the proxy to a bukkit server to initiate a reload + * Sent by the proxy to a bukkit server to initiate a reload. */ RELOAD_CONFIG } @@ -171,8 +171,7 @@ public class RedisMessage { } /** - * A record that defines the target of a plugin message; a spigot server or the proxy server(s). - * For Bukkit servers, the name of the server must also be specified + * A record that defines the target of a plugin message; a spigot server or the proxy server(s). For Bukkit servers, the name of the server must also be specified */ public record MessageTarget(Settings.ServerType targetServerType, UUID targetPlayerUUID, String targetClusterId) implements Serializable { }