From e1b740880be0cdcb7459eeff4790a38911fc8804 Mon Sep 17 00:00:00 2001 From: Aryan_Gupta Date: Wed, 14 Oct 2026 20:55:18 +0530 Subject: [PATCH 1/2] feat: add configurable stat synchronization --- .../fr/korastrip/paperplugin/SharedStats.java | 14 ++- .../paperplugin/SynchronizationConfig.java | 77 ++++++++++++++++ .../commands/group/GroupCommand.java | 2 + .../commands/group/sub/Disable.java | 45 +++++++++ .../commands/group/sub/Enable.java | 47 ++++++++++ .../paperplugin/commands/group/sub/Help.java | 4 +- .../paperplugin/commands/group/sub/Start.java | 6 +- .../paperplugin/listener/DamageListener.java | 2 +- .../listener/FoodSynchronizationListener.java | 65 +++++++++++++ .../listener/PlayerRespawnListener.java | 6 +- .../PotionSynchronizationListener.java | 91 +++++++++++++++++++ .../listener/RegenerationListener.java | 2 + src/main/resources/configuration.yml | 4 + 13 files changed, 357 insertions(+), 8 deletions(-) create mode 100644 src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java create mode 100644 src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java create mode 100644 src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java create mode 100644 src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java create mode 100644 src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java create mode 100644 src/main/resources/configuration.yml diff --git a/src/main/java/fr/korastrip/paperplugin/SharedStats.java b/src/main/java/fr/korastrip/paperplugin/SharedStats.java index f1c6bd5..e2faad6 100644 --- a/src/main/java/fr/korastrip/paperplugin/SharedStats.java +++ b/src/main/java/fr/korastrip/paperplugin/SharedStats.java @@ -13,21 +13,31 @@ public final class SharedStats extends JavaPlugin implements Listener { private final SharedGroup group = new SharedGroup(); GroupCommand groupCommand = new GroupCommand(this); + private SynchronizationConfig synchronizationConfig; public SharedGroup getGroup() { return group; } + public SynchronizationConfig getSynchronizationConfig() { + return synchronizationConfig; + } + + + @Override public void onEnable() { - + synchronizationConfig = new SynchronizationConfig(this); this.getServer().getPluginManager().registerEvents(this, this); this.getServer().getPluginManager().registerEvents(new DamageListener(this), this); this.getServer().getPluginManager().registerEvents(new PlayerQuitListener(this), this); this.getServer().getPluginManager().registerEvents(new PlayerDeathListener(this), this); this.getServer().getPluginManager().registerEvents(new RegenerationListener(this), this); this.getServer().getPluginManager().registerEvents(new PlayerRespawnListener(this), this); - + this.getServer().getPluginManager() + .registerEvents(new FoodSynchronizationListener(this), this); + this.getServer().getPluginManager() + .registerEvents(new PotionSynchronizationListener(this), this); this.getLifecycleManager().registerEventHandler( LifecycleEvents.COMMANDS, event -> event.registrar().register(groupCommand.create()) diff --git a/src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java b/src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java new file mode 100644 index 0000000..0f98c05 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java @@ -0,0 +1,77 @@ +package fr.korastrip.paperplugin; + +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; + +public class SynchronizationConfig { + + private final SharedStats plugin; + private final File file; + private YamlConfiguration config; + + public SynchronizationConfig(SharedStats plugin) { + this.plugin = plugin; + this.file = new File(plugin.getDataFolder(), "configuration.yml"); + + if (!file.exists()) { + plugin.saveResource("configuration.yml", false); + } + + reload(); + } + + public void reload() { + config = YamlConfiguration.loadConfiguration(file); + + config.addDefault("synchronization.health", true); + config.addDefault("synchronization.food", true); + config.addDefault("synchronization.potions", true); + config.options().copyDefaults(true); + + try { + config.save(file); + } catch (IOException exception) { + plugin.getLogger().severe( + "Failed to save default configuration: " + exception.getMessage() + ); + } + } + + public boolean isEnabled(String stat) { + String key = getKey(stat); + return config.getBoolean(key); + } + + public boolean setEnabled(String stat, boolean enabled) { + String key = getKey(stat); + boolean previousValue = config.getBoolean(key); + + config.set(key, enabled); + + try { + config.save(file); + return true; + } catch (IOException exception) { + config.set(key, previousValue); + plugin.getLogger().severe( + "Failed to save configuration.yml: " + exception.getMessage() + ); + return false; + } + } + + private String getKey(String stat) { + String normalizedStat = stat.toLowerCase(Locale.ROOT); + + if (!normalizedStat.equals("health") + && !normalizedStat.equals("food") + && !normalizedStat.equals("potions")) { + throw new IllegalArgumentException("Unknown synchronization stat: " + stat); + } + + return "synchronization." + normalizedStat; + } +} diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java index 4eec59f..ec3081d 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java @@ -25,6 +25,8 @@ public LiteralCommandNode create() { .then(new Remove(plugin).build()) .then(new Remove(plugin).build()) .then(new Players(plugin).build()) + .then(new Enable(plugin).build()) + .then(new Disable(plugin).build()) .build(); } diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java new file mode 100644 index 0000000..f3149d1 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java @@ -0,0 +1,45 @@ + +package fr.korastrip.paperplugin.commands.group.sub; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; + +public class Disable extends GroupSubCommand { + + public Disable(SharedStats plugin) { + super(plugin, "disable", "Disable synchronization for a stat", "disable "); + } + + @Override + public LiteralArgumentBuilder build() { + return Commands.literal("disable") + .requires(source -> source.getSender().isOp()) + .then(Commands.literal("health") + .executes(context -> disable("health", context.getSource()))) + .then(Commands.literal("food") + .executes(context -> disable("food", context.getSource()))) + .then(Commands.literal("potions") + .executes(context -> disable("potions", context.getSource()))); + } + + private int disable(String stat, CommandSourceStack source) { + var config = plugin.getSynchronizationConfig(); + + if (!config.isEnabled(stat)) { + source.getSender().sendRichMessage("" + stat + " synchronization is already disabled."); + return Command.SINGLE_SUCCESS; + } + + if (!config.setEnabled(stat, false)) { + source.getSender().sendRichMessage("Could not save the configuration."); + return 0; + } + + source.getSender().sendRichMessage("Disabled " + stat + " synchronization."); + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java new file mode 100644 index 0000000..11ec7d8 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java @@ -0,0 +1,47 @@ + +package fr.korastrip.paperplugin.commands.group.sub; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; + +import java.util.Locale; +import java.util.Set; + +public class Enable extends GroupSubCommand { + public Enable(SharedStats plugin) { + super(plugin, "enable", "Enable synchronization for a stat", "enable "); + } + + @Override + public LiteralArgumentBuilder build() { + return Commands.literal("enable") + .requires(source -> source.getSender().isOp()) + .then(Commands.literal("health") + .executes(context -> enable("health", context.getSource()))) + .then(Commands.literal("food") + .executes(context -> enable("food", context.getSource()))) + .then(Commands.literal("potions") + .executes(context -> enable("potions", context.getSource()))); + } + + private int enable(String stat, CommandSourceStack source) { + var config = plugin.getSynchronizationConfig(); + + if (config.isEnabled(stat)) { + source.getSender().sendRichMessage("" + stat + " synchronization is already enabled."); + return Command.SINGLE_SUCCESS; + } + + if (!config.setEnabled(stat, true)) { + source.getSender().sendRichMessage("Could not save the configuration."); + return 0; + } + + source.getSender().sendRichMessage("Enabled " + stat + " synchronization."); + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java index 36a95d2..2e96031 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java @@ -39,7 +39,9 @@ public LiteralArgumentBuilder build() { new Start(plugin), new Stop(plugin), new Players(plugin), - new Version(plugin) + new Version(plugin), + new Enable(plugin), + new Disable(plugin) ); commands.forEach(command -> sender.sendRichMessage(": ", Placeholder.unparsed("description", command.getDescription()), Placeholder.unparsed("usage", command.getUsage()))); diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java index 1d68891..d58b4b9 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java @@ -53,8 +53,10 @@ public LiteralArgumentBuilder build() { return Command.SINGLE_SUCCESS; } - group.setHealth(20.0); - group.syncHealth(); + if (plugin.getSynchronizationConfig().isEnabled("health")) { + group.setHealth(20.0); + group.syncHealth(); + } players.forEach(p -> { diff --git a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java index f40f1e1..50fb84a 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java @@ -27,7 +27,7 @@ public void onDamageReceive(EntityDamageEvent event) { if (event.getEntity() instanceof Player player) { SharedGroup group = this.plugin.getGroup(); - + if (!plugin.getSynchronizationConfig().isEnabled("health")) return; if (!group.getIsPlaying()) return; if (!group.getPlayersId().contains(player.getUniqueId())) return; diff --git a/src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java b/src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java new file mode 100644 index 0000000..28693ca --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java @@ -0,0 +1,65 @@ +package fr.korastrip.paperplugin.listener; + +import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.model.SharedGroup; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.FoodLevelChangeEvent; + +public class FoodSynchronizationListener implements Listener { + private final SharedStats plugin; + private boolean synchronizing = false; + + public FoodSynchronizationListener(SharedStats plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onFoodLevelChange(FoodLevelChangeEvent event) { + if (synchronizing || event.isCancelled()) { + return; + } + + if (!(event.getEntity() instanceof Player source)) { + return; + } + + if (!plugin.getSynchronizationConfig().isEnabled("food")) { + return; + } + + SharedGroup group = plugin.getGroup(); + + if (!group.getIsPlaying() + || !group.getPlayersId().contains(source.getUniqueId())) { + return; + } + + // Run after Minecraft applies the food change. + plugin.getServer().getScheduler().runTask(plugin, () -> { + if (!group.getIsPlaying() + || !group.getPlayersId().contains(source.getUniqueId()) + || !plugin.getSynchronizationConfig().isEnabled("food")) { + return; + } + + int foodLevel = source.getFoodLevel(); + float saturation = source.getSaturation(); + + synchronizing = true; + try { + for (Player target : group.getOnlinePlayers()) { + if (target.equals(source)) { + continue; + } + + target.setFoodLevel(foodLevel); + target.setSaturation(Math.min(saturation, foodLevel)); + } + } finally { + synchronizing = false; + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java b/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java index f508857..1595c64 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java @@ -28,8 +28,10 @@ public void onPlayerRespawn(PlayerRespawnEvent event) { plugin, () -> { if (group.getOnlinePlayers().size() == group.getRespawnedPlayers().size()) { - group.setHealth(20); - group.syncHealth(); + if (plugin.getSynchronizationConfig().isEnabled("health")) { + group.setHealth(20); + group.syncHealth(); + } group.clearRespawnedPlayers(); group.isGroupDying = false; } diff --git a/src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java b/src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java new file mode 100644 index 0000000..539cdbd --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java @@ -0,0 +1,91 @@ + +package fr.korastrip.paperplugin.listener; + +import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.model.SharedGroup; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPotionEffectEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class PotionSynchronizationListener implements Listener { + private final SharedStats plugin; + private final Set synchronizing = new HashSet<>(); + + public PotionSynchronizationListener(SharedStats plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPotionEffectChange(EntityPotionEffectEvent event) { + if (event.isCancelled() + || !(event.getEntity() instanceof Player source) + || synchronizing.contains(source.getUniqueId()) + || !plugin.getSynchronizationConfig().isEnabled("potions")) { + return; + } + + SharedGroup group = plugin.getGroup(); + + if (!group.getIsPlaying() + || !group.getPlayersId().contains(source.getUniqueId())) { + return; + } + + UUID sourceId = source.getUniqueId(); + + plugin.getServer().getScheduler().runTask(plugin, () -> { + if (!group.getIsPlaying() + || !group.getPlayersId().contains(sourceId) + || !plugin.getSynchronizationConfig().isEnabled("potions")) { + return; + } + + Set sourceTypes = new HashSet<>(); + for (PotionEffect effect : source.getActivePotionEffects()) { + sourceTypes.add(effect.getType()); + } + + for (Player target : group.getOnlinePlayers()) { + if (target.equals(source)) { + continue; + } + + UUID targetId = target.getUniqueId(); + synchronizing.add(targetId); + try { + // Remove effects that the source no longer has. + for (PotionEffect existing : target.getActivePotionEffects()) { + if (!sourceTypes.contains(existing.getType())) { + target.removePotionEffect(existing.getType()); + } + } + + // Add missing effects or update effects whose strength differs. + for (PotionEffect effect : source.getActivePotionEffects()) { + PotionEffect existing = + target.getPotionEffect(effect.getType()); + + boolean needsUpdate = existing == null + || existing.getAmplifier() != effect.getAmplifier() + || existing.isAmbient() != effect.isAmbient() + || existing.hasParticles() != effect.hasParticles() + || existing.hasIcon() != effect.hasIcon(); + + if (needsUpdate) { + target.addPotionEffect(effect, true); + } + } + } finally { + synchronizing.remove(targetId); + } + } + }); + } +} diff --git a/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java b/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java index 28b44c5..add443d 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java @@ -19,6 +19,8 @@ public RegenerationListener(SharedStats plugin) { public void onRegeneration(EntityRegainHealthEvent event) { SharedGroup group = this.plugin.getGroup(); + if (!plugin.getSynchronizationConfig().isEnabled("health")) return; + if (event.getEntity() instanceof Player player) { if(group.getPlayersId().contains(player.getUniqueId()) && group.getIsPlaying()) { diff --git a/src/main/resources/configuration.yml b/src/main/resources/configuration.yml new file mode 100644 index 0000000..1a243ce --- /dev/null +++ b/src/main/resources/configuration.yml @@ -0,0 +1,4 @@ +synchronization: + health: true + food: true + potions: true \ No newline at end of file From 6a4c6b153592fa7539541f2131311aa77c43e78b Mon Sep 17 00:00:00 2001 From: Aryan_Gupta Date: Wed, 14 Oct 2026 21:52:32 +0530 Subject: [PATCH 2/2] refactor: remove out-of-scope stat synchronization --- .../fr/korastrip/paperplugin/SharedStats.java | 4 - .../paperplugin/commands/group/sub/Start.java | 6 +- .../listener/FoodSynchronizationListener.java | 65 ------------- .../PotionSynchronizationListener.java | 91 ------------------- 4 files changed, 2 insertions(+), 164 deletions(-) delete mode 100644 src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java delete mode 100644 src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java diff --git a/src/main/java/fr/korastrip/paperplugin/SharedStats.java b/src/main/java/fr/korastrip/paperplugin/SharedStats.java index e2faad6..e4d7b6f 100644 --- a/src/main/java/fr/korastrip/paperplugin/SharedStats.java +++ b/src/main/java/fr/korastrip/paperplugin/SharedStats.java @@ -34,10 +34,6 @@ public void onEnable() { this.getServer().getPluginManager().registerEvents(new PlayerDeathListener(this), this); this.getServer().getPluginManager().registerEvents(new RegenerationListener(this), this); this.getServer().getPluginManager().registerEvents(new PlayerRespawnListener(this), this); - this.getServer().getPluginManager() - .registerEvents(new FoodSynchronizationListener(this), this); - this.getServer().getPluginManager() - .registerEvents(new PotionSynchronizationListener(this), this); this.getLifecycleManager().registerEventHandler( LifecycleEvents.COMMANDS, event -> event.registrar().register(groupCommand.create()) diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java index d58b4b9..1d68891 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java @@ -53,10 +53,8 @@ public LiteralArgumentBuilder build() { return Command.SINGLE_SUCCESS; } - if (plugin.getSynchronizationConfig().isEnabled("health")) { - group.setHealth(20.0); - group.syncHealth(); - } + group.setHealth(20.0); + group.syncHealth(); players.forEach(p -> { diff --git a/src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java b/src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java deleted file mode 100644 index 28693ca..0000000 --- a/src/main/java/fr/korastrip/paperplugin/listener/FoodSynchronizationListener.java +++ /dev/null @@ -1,65 +0,0 @@ -package fr.korastrip.paperplugin.listener; - -import fr.korastrip.paperplugin.SharedStats; -import fr.korastrip.paperplugin.model.SharedGroup; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.FoodLevelChangeEvent; - -public class FoodSynchronizationListener implements Listener { - private final SharedStats plugin; - private boolean synchronizing = false; - - public FoodSynchronizationListener(SharedStats plugin) { - this.plugin = plugin; - } - - @EventHandler - public void onFoodLevelChange(FoodLevelChangeEvent event) { - if (synchronizing || event.isCancelled()) { - return; - } - - if (!(event.getEntity() instanceof Player source)) { - return; - } - - if (!plugin.getSynchronizationConfig().isEnabled("food")) { - return; - } - - SharedGroup group = plugin.getGroup(); - - if (!group.getIsPlaying() - || !group.getPlayersId().contains(source.getUniqueId())) { - return; - } - - // Run after Minecraft applies the food change. - plugin.getServer().getScheduler().runTask(plugin, () -> { - if (!group.getIsPlaying() - || !group.getPlayersId().contains(source.getUniqueId()) - || !plugin.getSynchronizationConfig().isEnabled("food")) { - return; - } - - int foodLevel = source.getFoodLevel(); - float saturation = source.getSaturation(); - - synchronizing = true; - try { - for (Player target : group.getOnlinePlayers()) { - if (target.equals(source)) { - continue; - } - - target.setFoodLevel(foodLevel); - target.setSaturation(Math.min(saturation, foodLevel)); - } - } finally { - synchronizing = false; - } - }); - } -} \ No newline at end of file diff --git a/src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java b/src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java deleted file mode 100644 index 539cdbd..0000000 --- a/src/main/java/fr/korastrip/paperplugin/listener/PotionSynchronizationListener.java +++ /dev/null @@ -1,91 +0,0 @@ - -package fr.korastrip.paperplugin.listener; - -import fr.korastrip.paperplugin.SharedStats; -import fr.korastrip.paperplugin.model.SharedGroup; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityPotionEffectEvent; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -public class PotionSynchronizationListener implements Listener { - private final SharedStats plugin; - private final Set synchronizing = new HashSet<>(); - - public PotionSynchronizationListener(SharedStats plugin) { - this.plugin = plugin; - } - - @EventHandler - public void onPotionEffectChange(EntityPotionEffectEvent event) { - if (event.isCancelled() - || !(event.getEntity() instanceof Player source) - || synchronizing.contains(source.getUniqueId()) - || !plugin.getSynchronizationConfig().isEnabled("potions")) { - return; - } - - SharedGroup group = plugin.getGroup(); - - if (!group.getIsPlaying() - || !group.getPlayersId().contains(source.getUniqueId())) { - return; - } - - UUID sourceId = source.getUniqueId(); - - plugin.getServer().getScheduler().runTask(plugin, () -> { - if (!group.getIsPlaying() - || !group.getPlayersId().contains(sourceId) - || !plugin.getSynchronizationConfig().isEnabled("potions")) { - return; - } - - Set sourceTypes = new HashSet<>(); - for (PotionEffect effect : source.getActivePotionEffects()) { - sourceTypes.add(effect.getType()); - } - - for (Player target : group.getOnlinePlayers()) { - if (target.equals(source)) { - continue; - } - - UUID targetId = target.getUniqueId(); - synchronizing.add(targetId); - try { - // Remove effects that the source no longer has. - for (PotionEffect existing : target.getActivePotionEffects()) { - if (!sourceTypes.contains(existing.getType())) { - target.removePotionEffect(existing.getType()); - } - } - - // Add missing effects or update effects whose strength differs. - for (PotionEffect effect : source.getActivePotionEffects()) { - PotionEffect existing = - target.getPotionEffect(effect.getType()); - - boolean needsUpdate = existing == null - || existing.getAmplifier() != effect.getAmplifier() - || existing.isAmbient() != effect.isAmbient() - || existing.hasParticles() != effect.hasParticles() - || existing.hasIcon() != effect.hasIcon(); - - if (needsUpdate) { - target.addPotionEffect(effect, true); - } - } - } finally { - synchronizing.remove(targetId); - } - } - }); - } -}