Skip to content

Commit

Permalink
fix: Improve accuracy of max health syncing #148
Browse files Browse the repository at this point in the history
  • Loading branch information
WiIIiam278 committed Dec 21, 2023
1 parent 972fee1 commit 2aa33b2
Showing 1 changed file with 83 additions and 83 deletions.
166 changes: 83 additions & 83 deletions bukkit/src/main/java/net/william278/husksync/data/BukkitData.java
Original file line number Diff line number Diff line change
Expand Up @@ -593,54 +593,54 @@ public static BukkitData.Statistics adapt(@NotNull Player player) {
@NotNull
public static BukkitData.Statistics from(@NotNull StatisticsMap stats) {
return new BukkitData.Statistics(
stats.genericStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)),
stats.blockStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.flatMap(blockEntry -> {
Material material = Material.matchMaterial(blockEntry.getKey());
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, blockEntry.getValue())) : Stream.empty();
stats.genericStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
)),
stats.itemStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.flatMap(itemEntry -> {
Material material = Material.matchMaterial(itemEntry.getKey());
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, itemEntry.getValue())) : Stream.empty();
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)),
stats.blockStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
)),
stats.entityStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.flatMap(itemEntry -> {
EntityType entityType = matchEntityType(itemEntry.getKey());
return entityType != null ? Stream.of(new AbstractMap.SimpleEntry<>(entityType, itemEntry.getValue())) : Stream.empty();
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.flatMap(blockEntry -> {
Material material = Material.matchMaterial(blockEntry.getKey());
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, blockEntry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
)),
stats.itemStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
))
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.flatMap(itemEntry -> {
Material material = Material.matchMaterial(itemEntry.getKey());
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, itemEntry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
)),
stats.entityStats().entrySet().stream()
.flatMap(entry -> {
Statistic statistic = matchStatistic(entry.getKey());
return statistic != null ? Stream.of(new AbstractMap.SimpleEntry<>(statistic, entry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.flatMap(itemEntry -> {
EntityType entityType = matchEntityType(itemEntry.getKey());
return entityType != null ? Stream.of(new AbstractMap.SimpleEntry<>(entityType, itemEntry.getValue())) : Stream.empty();
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
))
);
}

Expand Down Expand Up @@ -822,10 +822,9 @@ public static BukkitData.Health from(double health, double maxHealth, double hea

@NotNull
public static BukkitData.Health adapt(@NotNull Player player) {
final double maxHealth = getMaxHealth(player);
return from(
Math.min(player.getHealth(), maxHealth),
maxHealth,
player.getHealth(),
getMaxHealth(player),
player.isHealthScaled() ? player.getHealthScale() : 0d
);
}
Expand All @@ -834,64 +833,65 @@ public static BukkitData.Health adapt(@NotNull Player player) {
public void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException {
final Player player = user.getPlayer();

// Set base max health
final AttributeInstance maxHealthAttribute = Objects.requireNonNull(
player.getAttribute(Attribute.GENERIC_MAX_HEALTH), "Max health attribute was null"
);
double currentMaxHealth = maxHealthAttribute.getBaseValue();
if (plugin.getSettings().doSynchronizeMaxHealth() && maxHealth != 0d) {
maxHealthAttribute.setBaseValue(maxHealth);
currentMaxHealth = maxHealth;
// Set max health
final AttributeInstance maxHealth = getMaxHealthAttribute(player);
try {
if (plugin.getSettings().doSynchronizeMaxHealth() && this.maxHealth != 0) {
maxHealth.setBaseValue(this.maxHealth);
}
} catch (Throwable e) {
plugin.log(Level.WARNING, String.format("Failed setting the max health of %s to %s",
player.getName(), this.maxHealth), e);
}

// Set health
final double currentHealth = player.getHealth();
if (health != currentHealth) {
final double healthToSet = currentHealth > currentMaxHealth ? currentMaxHealth : health;
try {
player.setHealth(Math.min(healthToSet, currentMaxHealth));
} catch (IllegalArgumentException e) {
plugin.log(Level.WARNING, "Failed to set player health", e);
}
try {
final double health = player.getHealth();
player.setHealth(Math.min(health, maxHealth.getBaseValue()));
} catch (Throwable e) {
plugin.log(Level.WARNING, String.format("Failed setting the health of %s to %s",
player.getName(), this.maxHealth), e);
}

// Set health scale
try {
if (healthScale != 0d) {
player.setHealthScale(healthScale);
if (this.healthScale != 0d) {
player.setHealthScaled(true);
player.setHealthScale(this.healthScale);
} else {
player.setHealthScale(maxHealth);
player.setHealthScaled(false);
player.setHealthScale(this.maxHealth);
}
player.setHealthScaled(healthScale != 0D);
} catch (IllegalArgumentException e) {
plugin.log(Level.WARNING, "Failed to set player health scale", e);
} catch (Throwable e) {
plugin.log(Level.WARNING, String.format("Failed setting the health scale of %s to %s",
player.getName(), this.healthScale), e);
}
}

/**
* 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
*/
// Returns the max health of a player, accounting for health boost potion effects
private static double getMaxHealth(@NotNull Player player) {
double maxHealth = Objects.requireNonNull(
player.getAttribute(Attribute.GENERIC_MAX_HEALTH), "Max health attribute was null"
).getBaseValue();
// Get the base value of the attribute (ignore armor, items that give health boosts, etc.)
double maxHealth = getMaxHealthAttribute(player).getBaseValue();

// If the player has additional health bonuses from synchronized potion effects,
// subtract these from this number as they are synchronized separately
// Subtract health boost potion effects from stored max health
if (player.hasPotionEffect(PotionEffectType.HEALTH_BOOST) && maxHealth > 20d) {
final PotionEffect healthBoost = Objects.requireNonNull(
player.getPotionEffect(PotionEffectType.HEALTH_BOOST), "Health boost effect was null"
);
final double boostEffect = 4 * (healthBoost.getAmplifier() + 1);
maxHealth -= boostEffect;
maxHealth -= (4 * (healthBoost.getAmplifier() + 1));
}

return maxHealth;
}

// Returns the max health attribute of a player
@NotNull
private static AttributeInstance getMaxHealthAttribute(@NotNull Player player) {
return Objects.requireNonNull(
player.getAttribute(Attribute.GENERIC_MAX_HEALTH), "Max health attribute was null"
);
}

@Override
public double getHealth() {
return health;
Expand Down

0 comments on commit 2aa33b2

Please sign in to comment.