diff --git a/build.gradle b/build.gradle index 2a474069..3adbf1cb 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ plugins { allprojects { group 'me.William278' - version '1.0.3' + version '1.0.4' compileJava { options.encoding = 'UTF-8' } tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } 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 5f945972..d7b2df49 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 @@ -10,15 +10,18 @@ import me.william278.husksync.redis.RedisMessage; 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; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; +import java.util.Objects; import java.util.UUID; import java.util.logging.Level; @@ -34,12 +37,13 @@ public class PlayerSetter { * @throws IOException If the serialization fails */ private static String getNewSerializedPlayerData(Player player) throws IOException { + final double maxHealth = getMaxHealth(player); // Get the player's max health (used to determine health as well) return RedisMessage.serialize(new PlayerData(player.getUniqueId(), DataSerializer.serializeInventory(player.getInventory().getContents()), DataSerializer.serializeInventory(player.getEnderChest().getContents()), - player.getHealth(), - player.getMaxHealth(), - player.getHealthScale(), + Math.min(player.getHealth(), maxHealth), + maxHealth, + player.isHealthScaled() ? player.getHealthScale() : 0D, player.getFoodLevel(), player.getSaturation(), player.getExhaustion(), @@ -55,6 +59,25 @@ public class PlayerSetter { DataSerializer.getSerializedLocation(player))); } + /** + * Returns a {@link Player}'s maximum health, minus any health boost effects + * + * @param player The {@link Player} to get the maximum health of + * @return The {@link Player}'s max health + */ + private static double getMaxHealth(Player player) { + double maxHealth = Objects.requireNonNull(player.getAttribute(Attribute.GENERIC_MAX_HEALTH)).getBaseValue(); + + // If the player has additional health bonuses from synchronised potion effects, subtract these from this number as they are synchronised seperately + if (player.hasPotionEffect(PotionEffectType.HEALTH_BOOST) && maxHealth > 20D) { + PotionEffect healthBoostEffect = player.getPotionEffect(PotionEffectType.HEALTH_BOOST); + assert healthBoostEffect != null; + double healthBoostBonus = 4 * (healthBoostEffect.getAmplifier() + 1); + maxHealth -= healthBoostBonus; + } + return maxHealth; + } + /** * Returns a {@link Player}'s active potion effects in a {@link PotionEffect} array * @@ -147,9 +170,7 @@ public class PlayerSetter { setPlayerEnderChest(player, DataSerializer.deserializeInventory(data.getSerializedEnderChest())); } if (Settings.syncHealth) { - player.setMaxHealth(data.getMaxHealth()); - player.setHealthScale(data.getHealthScale()); - player.setHealth(data.getHealth()); + setPlayerHealth(player, data.getHealth(), data.getMaxHealth(), data.getHealthScale()); } if (Settings.syncHunger) { player.setFoodLevel(data.getHunger()); @@ -375,4 +396,30 @@ public class PlayerSetter { // Teleport the player player.teleport(new Location(world, location.x(), location.y(), location.z(), location.yaw(), location.pitch())); } + + /** + * Correctly set a {@link Player}'s health data + * + * @param player The {@link Player} to set + * @param health Health to set to the player + * @param maxHealth Max health to set to the player + * @param healthScale Health scaling to apply to the player + */ + private static void setPlayerHealth(Player player, double health, double maxHealth, double healthScale) { + // Set max health + if (maxHealth != 0.0D) { + Objects.requireNonNull(player.getAttribute(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(maxHealth); + } + + // Set health + player.setHealth(player.getHealth() > maxHealth ? maxHealth : health); + + // Set health scaling if needed + if (healthScale != 0D) { + player.setHealthScale(healthScale); + } else { + player.setHealthScale(maxHealth); + } + player.setHealthScaled(healthScale != 0D); + } }