diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java index 6609c2f7..ea5e26b1 100644 --- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java +++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java @@ -14,6 +14,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryClickEvent; @@ -121,6 +122,13 @@ public class BukkitEventListener extends EventListener implements Listener { } } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) { + if (event.getEntity() instanceof Player player) { + event.setCancelled(cancelPlayerEvent(BukkitPlayer.adapt(player))); + } + } + @EventHandler(ignoreCancelled = true) public void onPlayerDeath(PlayerDeathEvent event) { if (cancelPlayerEvent(BukkitPlayer.adapt(event.getEntity()))) { diff --git a/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java b/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java index 05582fdf..d835d07f 100644 --- a/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java +++ b/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java @@ -97,7 +97,7 @@ public class BukkitPlayer extends OnlineUser { final double currentHealth = player.getHealth(); if (statusData.health != currentHealth) { final double healthToSet = currentHealth > currentMaxHealth ? currentMaxHealth : statusData.health; - if (healthToSet <= 0) { + if (healthToSet < 1) { Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> player.setHealth(healthToSet)); } else { player.setHealth(healthToSet); @@ -572,6 +572,11 @@ public class BukkitPlayer extends OnlineUser { }); } + @Override + public boolean isDead() { + return player.getHealth() < 1; + } + @Override public void sendActionBar(@NotNull MineDown mineDown) { player.spigot().sendMessage(ChatMessageType.ACTION_BAR, mineDown.replace().toComponent()); diff --git a/common/src/main/java/net/william278/husksync/api/BaseHuskSyncAPI.java b/common/src/main/java/net/william278/husksync/api/BaseHuskSyncAPI.java index 3827e8c6..73b878b2 100644 --- a/common/src/main/java/net/william278/husksync/api/BaseHuskSyncAPI.java +++ b/common/src/main/java/net/william278/husksync/api/BaseHuskSyncAPI.java @@ -72,7 +72,7 @@ public abstract class BaseHuskSyncAPI { public final CompletableFuture> getUserData(@NotNull User user) { return CompletableFuture.supplyAsync(() -> { if (user instanceof OnlineUser) { - return ((OnlineUser) user).getUserData(plugin.getLoggingAdapter()).join(); + return ((OnlineUser) user).getUserData(plugin.getLoggingAdapter(), plugin.getSettings()).join(); } else { return plugin.getDatabase().getCurrentUserData(user).join().map(UserDataSnapshot::userData); } @@ -103,8 +103,9 @@ public abstract class BaseHuskSyncAPI { * @since 2.0 */ public final CompletableFuture saveUserData(@NotNull OnlineUser user) { - return CompletableFuture.runAsync(() -> user.getUserData(plugin.getLoggingAdapter()).thenAccept(optionalUserData -> optionalUserData.ifPresent( - userData -> plugin.getDatabase().setUserData(user, userData, DataSaveCause.API).join()))); + return CompletableFuture.runAsync(() -> user.getUserData(plugin.getLoggingAdapter(), plugin.getSettings()) + .thenAccept(optionalUserData -> optionalUserData.ifPresent( + userData -> plugin.getDatabase().setUserData(user, userData, DataSaveCause.API).join()))); } /** diff --git a/common/src/main/java/net/william278/husksync/config/Settings.java b/common/src/main/java/net/william278/husksync/config/Settings.java index 5182630c..cf171028 100644 --- a/common/src/main/java/net/william278/husksync/config/Settings.java +++ b/common/src/main/java/net/william278/husksync/config/Settings.java @@ -144,6 +144,7 @@ public class Settings { SYNCHRONIZATION_SAVE_ON_WORLD_SAVE("synchronization.save_on_world_save", OptionType.BOOLEAN, true), SYNCHRONIZATION_COMPRESS_DATA("synchronization.compress_data", OptionType.BOOLEAN, true), SYNCHRONIZATION_NETWORK_LATENCY_MILLISECONDS("synchronization.network_latency_milliseconds", OptionType.INTEGER, 500), + SYNCHRONIZATION_SAVE_DEAD_PLAYER_INVENTORIES("synchronization.save_dead_player_inventories", OptionType.BOOLEAN, true), SYNCHRONIZATION_SYNC_INVENTORIES("synchronization.features.inventories", OptionType.BOOLEAN, true), SYNCHRONIZATION_SYNC_ENDER_CHESTS("synchronization.features.ender_chests", OptionType.BOOLEAN, true), SYNCHRONIZATION_SYNC_HEALTH("synchronization.features.health", OptionType.BOOLEAN, true), diff --git a/common/src/main/java/net/william278/husksync/listener/EventListener.java b/common/src/main/java/net/william278/husksync/listener/EventListener.java index 4370ed65..b2300a60 100644 --- a/common/src/main/java/net/william278/husksync/listener/EventListener.java +++ b/common/src/main/java/net/william278/husksync/listener/EventListener.java @@ -152,9 +152,10 @@ public abstract class EventListener { // Handle asynchronous disconnection lockedPlayers.add(user.uuid); CompletableFuture.runAsync(() -> plugin.getRedisManager().setUserServerSwitch(user) - .thenRun(() -> user.getUserData(plugin.getLoggingAdapter()).thenAccept(optionalUserData -> - optionalUserData.ifPresent(userData -> plugin.getRedisManager().setUserData(user, userData) - .thenRun(() -> plugin.getDatabase().setUserData(user, userData, DataSaveCause.DISCONNECT))))) + .thenRun(() -> user.getUserData(plugin.getLoggingAdapter(), plugin.getSettings()).thenAccept( + optionalUserData -> optionalUserData.ifPresent(userData -> plugin.getRedisManager() + .setUserData(user, userData).thenRun(() -> plugin.getDatabase() + .setUserData(user, userData, DataSaveCause.DISCONNECT))))) .thenRun(() -> lockedPlayers.remove(user.uuid)).exceptionally(throwable -> { plugin.getLoggingAdapter().log(Level.SEVERE, "An exception occurred handling a player disconnection"); @@ -172,7 +173,7 @@ public abstract class EventListener { if (disabling || !plugin.getSettings().getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SAVE_ON_WORLD_SAVE)) { return; } - usersInWorld.forEach(user -> user.getUserData(plugin.getLoggingAdapter()).join().ifPresent( + usersInWorld.forEach(user -> user.getUserData(plugin.getLoggingAdapter(), plugin.getSettings()).join().ifPresent( userData -> plugin.getDatabase().setUserData(user, userData, DataSaveCause.WORLD_SAVE).join())); } @@ -217,7 +218,7 @@ public abstract class EventListener { disabling = true; plugin.getOnlineUsers().stream().filter(user -> !lockedPlayers.contains(user.uuid)).forEach( - user -> user.getUserData(plugin.getLoggingAdapter()).join().ifPresent( + user -> user.getUserData(plugin.getLoggingAdapter(), plugin.getSettings()).join().ifPresent( userData -> plugin.getDatabase().setUserData(user, userData, DataSaveCause.SERVER_SHUTDOWN).join())); plugin.getDatabase().close(); diff --git a/common/src/main/java/net/william278/husksync/player/OnlineUser.java b/common/src/main/java/net/william278/husksync/player/OnlineUser.java index f285d5e7..d3e08903 100644 --- a/common/src/main/java/net/william278/husksync/player/OnlineUser.java +++ b/common/src/main/java/net/william278/husksync/player/OnlineUser.java @@ -260,16 +260,28 @@ public abstract class OnlineUser extends User { */ public abstract void showMenu(@NotNull ItemEditorMenu menu); + /** + * Returns true if the player is dead + * + * @return true if the player is dead + */ + public abstract boolean isDead(); + /** * Get the player's current {@link UserData} in an {@link Optional} - *

+ *

+ * If the {@code SYNCHRONIZATION_SAVE_DEAD_PLAYER_INVENTORIES} ConfigOption has been set, + * the user's inventory will only be returned if they are alive + *

* If the user data could not be returned due to an exception, the optional will return empty * * @param logger The logger to use for handling exceptions * @return the player's current {@link UserData} in an optional; empty if an exception occurs */ - public final CompletableFuture> getUserData(@NotNull Logger logger) { - return CompletableFuture.supplyAsync(() -> Optional.of(new UserData(getStatus().join(), getInventory().join(), + public final CompletableFuture> getUserData(@NotNull Logger logger, @NotNull Settings settings) { + return CompletableFuture.supplyAsync(() -> Optional.of(new UserData(getStatus().join(), + (settings.getBooleanValue(Settings.ConfigOption.SYNCHRONIZATION_SAVE_DEAD_PLAYER_INVENTORIES) + ? getInventory().join() : (isDead() ? new ItemData("") : getInventory().join())), getEnderChest().join(), getPotionEffects().join(), getAdvancements().join(), getStatistics().join(), getLocation().join(), getPersistentDataContainer().join(), getMinecraftVersion().toString()))) diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml index 542f4fde..5d8ac465 100644 --- a/common/src/main/resources/config.yml +++ b/common/src/main/resources/config.yml @@ -38,6 +38,7 @@ synchronization: max_user_data_snapshots: 5 save_on_world_save: true compress_data: true + save_dead_player_inventories: true network_latency_milliseconds: 500 features: inventories: true