From cceeb82fefdd2a0d0198f8a474ead97b83a14989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Wojtas?= <80779749+CitralFlo@users.noreply.github.com> Date: Mon, 2 Oct 2023 11:42:04 +0200 Subject: [PATCH] GH-116 Add Effects applied during Combat (#116) * Add inCombatEffects based on custom Events * Improve give back of potion effects. * Fix merge conflicts and add okaeri bukkit serdes. * Simplification of loops, new methods and optimization * Change HashMap based on Player to UUID for better security * Fix issues * Update src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/event/FightDeathEvent.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/event/FightTagEvent.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/event/FightDeathEvent.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/FightManager.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/FightManager.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Update src/main/java/com/eternalcode/combat/fight/effect/EffectService.java Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> * Codestyle fixes * Codestyle fixes * Remove unnesesary FightDeathEvent.java, refactor class names to match project style, fixes in codestyle, move to UUID event pass down instead of bukkit player. * Add brackets * Follow Rollczi's review * Follow DMK's review. * simple rename --------- Co-authored-by: Jakubk15 <77227023+Jakubk15@users.noreply.github.com> Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com> --- build.gradle.kts | 1 + .../com/eternalcode/combat/CombatPlugin.java | 19 ++- .../combat/config/ConfigService.java | 3 +- .../config/implementation/PluginConfig.java | 9 +- .../eternalcode/combat/event/EventCaller.java | 20 +++ .../combat/fight/FightManager.java | 17 ++- .../fight/effect/FightEffectController.java | 116 ++++++++++++++++++ .../fight/effect/FightEffectService.java | 75 +++++++++++ .../fight/effect/FightEffectSettings.java | 26 ++++ .../combat/fight/event/FightTagEvent.java | 29 +++++ .../combat/fight/event/FightUntagEvent.java | 29 +++++ 11 files changed, 333 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/eternalcode/combat/event/EventCaller.java create mode 100644 src/main/java/com/eternalcode/combat/fight/effect/FightEffectController.java create mode 100644 src/main/java/com/eternalcode/combat/fight/effect/FightEffectService.java create mode 100644 src/main/java/com/eternalcode/combat/fight/effect/FightEffectSettings.java create mode 100644 src/main/java/com/eternalcode/combat/fight/event/FightTagEvent.java create mode 100644 src/main/java/com/eternalcode/combat/fight/event/FightUntagEvent.java diff --git a/build.gradle.kts b/build.gradle.kts index ea155ab8..166255aa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { // Okaeri configs implementation("eu.okaeri:okaeri-configs-yaml-bukkit:5.0.0-beta.5") implementation("eu.okaeri:okaeri-configs-serdes-commons:5.0.0-beta.5") + implementation("eu.okaeri:okaeri-configs-serdes-bukkit:5.0.0-beta.5") // Panda utilities implementation("org.panda-lang:panda-utilities:0.5.2-alpha") diff --git a/src/main/java/com/eternalcode/combat/CombatPlugin.java b/src/main/java/com/eternalcode/combat/CombatPlugin.java index 9cfb06c0..e4f5ad45 100644 --- a/src/main/java/com/eternalcode/combat/CombatPlugin.java +++ b/src/main/java/com/eternalcode/combat/CombatPlugin.java @@ -11,6 +11,8 @@ import com.eternalcode.combat.drop.DropManager; import com.eternalcode.combat.drop.impl.PercentDropModifier; import com.eternalcode.combat.drop.impl.PlayersHealthDropModifier; +import com.eternalcode.combat.fight.effect.FightEffectController; +import com.eternalcode.combat.event.EventCaller; import com.eternalcode.combat.fight.FightManager; import com.eternalcode.combat.fight.FightTask; import com.eternalcode.combat.fight.bossbar.FightBossBarService; @@ -19,6 +21,7 @@ import com.eternalcode.combat.fight.controller.FightEscapeController; import com.eternalcode.combat.fight.controller.FightTagController; import com.eternalcode.combat.fight.controller.FightUnTagController; +import com.eternalcode.combat.fight.effect.FightEffectService; import com.eternalcode.combat.fight.pearl.FightPearlController; import com.eternalcode.combat.fight.pearl.FightPearlManager; import com.eternalcode.combat.notification.NotificationAnnouncer; @@ -47,7 +50,6 @@ public final class CombatPlugin extends JavaPlugin { private FightManager fightManager; - private FightPearlManager fightPearlManager; private FightBossBarService fightBossBarService; @@ -64,10 +66,12 @@ public void onEnable() { ConfigBackupService backupService = new ConfigBackupService(dataFolder); ConfigService configService = new ConfigService(backupService); + EventCaller eventCaller = new EventCaller(server); + PluginConfig pluginConfig = configService.create(PluginConfig.class, new File(dataFolder, "config.yml")); - this.fightManager = new FightManager(); - this.fightPearlManager = new FightPearlManager(pluginConfig.pearl); + this.fightManager = new FightManager(eventCaller); + FightPearlManager fightPearlManager = new FightPearlManager(pluginConfig.pearl); UpdaterService updaterService = new UpdaterService(this.getDescription()); @@ -95,13 +99,15 @@ public void onEnable() { .register(); FightTask fightTask = new FightTask(server, pluginConfig, this.fightManager, this.fightBossBarService, notificationAnnouncer); - this.getServer().getScheduler().runTaskTimerAsynchronously(this, fightTask, 20L, 20L); + this.getServer().getScheduler().runTaskTimer(this, fightTask, 20L, 20L); new Metrics(this, 17803); DropManager dropManager = new DropManager(); DropKeepInventoryManager keepInventoryManager = new DropKeepInventoryManager(); + FightEffectService effectService = new FightEffectService(); + Stream.of( new PercentDropModifier(pluginConfig.dropSettings), new PlayersHealthDropModifier(pluginConfig.dropSettings) @@ -114,9 +120,10 @@ public void onEnable() { new FightUnTagController(this.fightManager, pluginConfig, notificationAnnouncer), new FightEscapeController(this.fightManager, pluginConfig, notificationAnnouncer), new FightActionBlockerController(this.fightManager, notificationAnnouncer, pluginConfig), - new FightPearlController(pluginConfig.pearl, notificationAnnouncer, this.fightManager, this.fightPearlManager), + new FightPearlController(pluginConfig.pearl, notificationAnnouncer, this.fightManager, fightPearlManager), new UpdaterNotificationController(updaterService, pluginConfig, this.audienceProvider, miniMessage), - new RegionController(notificationAnnouncer, bridgeService.getRegionProvider(), this.fightManager, pluginConfig) + new RegionController(notificationAnnouncer, bridgeService.getRegionProvider(), this.fightManager, pluginConfig), + new FightEffectController(pluginConfig.effect, effectService, this.fightManager, this.getServer()) ).forEach(listener -> this.getServer().getPluginManager().registerEvents(listener, this)); long millis = started.elapsed(TimeUnit.MILLISECONDS); diff --git a/src/main/java/com/eternalcode/combat/config/ConfigService.java b/src/main/java/com/eternalcode/combat/config/ConfigService.java index 58814c34..89b20fff 100644 --- a/src/main/java/com/eternalcode/combat/config/ConfigService.java +++ b/src/main/java/com/eternalcode/combat/config/ConfigService.java @@ -5,6 +5,7 @@ import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.serdes.commons.SerdesCommons; import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer; +import eu.okaeri.configs.yaml.bukkit.serdes.SerdesBukkit; import java.io.File; import java.util.HashSet; @@ -25,7 +26,7 @@ public T create(Class config, File file) { T configFile = ConfigManager.create(config); - configFile.withConfigurer(new YamlBukkitConfigurer(), new SerdesCommons()); + configFile.withConfigurer(new YamlBukkitConfigurer(), new SerdesCommons(), new SerdesBukkit()); configFile.withSerdesPack(registry -> { registry.register(new NotificationSerializer()); }); diff --git a/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java b/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java index effcdc39..5e07bac3 100644 --- a/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java +++ b/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java @@ -2,6 +2,7 @@ import com.eternalcode.combat.WhitelistBlacklistMode; import com.eternalcode.combat.drop.DropSettings; +import com.eternalcode.combat.fight.effect.FightEffectSettings; import com.eternalcode.combat.fight.pearl.FightPearlSettings; import com.eternalcode.combat.notification.Notification; import com.eternalcode.combat.notification.implementation.ActionBarNotification; @@ -23,7 +24,10 @@ public class PluginConfig extends OkaeriConfig { @Comment({" ", "# Ender pearl settings"}) public FightPearlSettings pearl = new FightPearlSettings(); - @Comment({ " ", "# Set a custom way for a player's items to drop on death (if in combat)" }) + @Comment({" ", "# Custom effects settings"}) + public FightEffectSettings effect = new FightEffectSettings(); + + @Comment({" ", "# Set a custom way for a player's items to drop on death (if in combat)"}) public DropSettings dropSettings = new DropSettings(); public static class Settings extends OkaeriConfig { @@ -114,9 +118,10 @@ public static class Settings extends OkaeriConfig { public List disabledProjectileEntities = List.of( EntityType.ENDER_PEARL ); + } - @Comment({ " ", "# Do you want to change the plugin messages?" }) + @Comment({" ", "# Do you want to change the plugin messages?"}) public Messages messages = new Messages(); public static class Messages extends OkaeriConfig { diff --git a/src/main/java/com/eternalcode/combat/event/EventCaller.java b/src/main/java/com/eternalcode/combat/event/EventCaller.java new file mode 100644 index 00000000..3c2ca8f8 --- /dev/null +++ b/src/main/java/com/eternalcode/combat/event/EventCaller.java @@ -0,0 +1,20 @@ +package com.eternalcode.combat.event; + +import org.bukkit.Server; +import org.bukkit.event.Event; + +public class EventCaller { + + private final Server server; + + public EventCaller(Server server) { + this.server = server; + } + + public T callEvent(T event) { + this.server.getPluginManager().callEvent(event); + + return event; + } + +} diff --git a/src/main/java/com/eternalcode/combat/fight/FightManager.java b/src/main/java/com/eternalcode/combat/fight/FightManager.java index b2a1b8e1..e43d3ae1 100644 --- a/src/main/java/com/eternalcode/combat/fight/FightManager.java +++ b/src/main/java/com/eternalcode/combat/fight/FightManager.java @@ -1,16 +1,25 @@ package com.eternalcode.combat.fight; +import com.eternalcode.combat.event.EventCaller; +import com.eternalcode.combat.fight.event.FightTagEvent; +import com.eternalcode.combat.fight.event.FightUntagEvent; + import java.time.Duration; import java.time.Instant; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; +import java.util.HashMap; public class FightManager { - private final Map fights = new ConcurrentHashMap<>(); + private final Map fights = new HashMap<>(); + private final EventCaller eventCaller; + + public FightManager(EventCaller eventCaller) { + this.eventCaller = eventCaller; + } public boolean isInCombat(UUID player) { if (!this.fights.containsKey(player)) { @@ -23,10 +32,14 @@ public boolean isInCombat(UUID player) { } public void untag(UUID player) { + this.eventCaller.callEvent(new FightUntagEvent(player)); + this.fights.remove(player); } public void tag(UUID target, Duration delay) { + this.eventCaller.callEvent(new FightTagEvent(target)); + Instant now = Instant.now(); Instant endOfCombatLog = now.plus(delay); diff --git a/src/main/java/com/eternalcode/combat/fight/effect/FightEffectController.java b/src/main/java/com/eternalcode/combat/fight/effect/FightEffectController.java new file mode 100644 index 00000000..7447b471 --- /dev/null +++ b/src/main/java/com/eternalcode/combat/fight/effect/FightEffectController.java @@ -0,0 +1,116 @@ +package com.eternalcode.combat.fight.effect; + +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.fight.event.FightTagEvent; +import com.eternalcode.combat.fight.event.FightUntagEvent; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPotionEffectEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; + +public class FightEffectController implements Listener { + + private final FightEffectService effectService; + private final FightEffectSettings effectSettings; + private final FightManager fightManager; + private final Server server; + + public FightEffectController(FightEffectSettings settings, FightEffectService effectService, FightManager fightManager, Server server) { + this.effectSettings = settings; + this.effectService = effectService; + this.fightManager = fightManager; + this.server = server; + } + + @EventHandler + public void onTag(FightTagEvent event) { + if (!this.effectSettings.customEffectsEnabled) { + return; + } + + Player player = this.server.getPlayer(event.getPlayer()); + + if (player == null) { + return; + } + + this.effectSettings.customEffects.forEach((key, value) -> + this.effectService.applyCustomEffect(player, key, value)); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + if (!this.effectSettings.customEffectsEnabled) { + return; + } + + Player player = event.getPlayer(); + this.effectService.restoreActiveEffects(player); + } + + @EventHandler + public void onUntag(FightUntagEvent event) { + if (!this.effectSettings.customEffectsEnabled) { + return; + } + + Player player = this.server.getPlayer(event.getPlayer()); + + if (player == null) { + return; + } + + this.effectSettings.customEffects.forEach((key, value) -> this.effectService.removeCustomEffect(player, key, value)); + + this.effectService.restoreActiveEffects(player); + } + + @EventHandler + public void onDeath(PlayerDeathEvent event) { + if (!this.effectSettings.customEffectsEnabled) { + return; + } + + Player player = event.getEntity(); + this.effectService.clearStoredEffects(player); + } + + @EventHandler + public void onEffectChange(EntityPotionEffectEvent event) { + if (!this.effectSettings.customEffectsEnabled) { + return; + } + + if (!(event.getEntity() instanceof Player player)) { + return; + } + + if (!this.fightManager.isInCombat(player.getUniqueId())) { + return; + } + + PotionEffect newEffect = event.getNewEffect(); + PotionEffect oldEffect = event.getOldEffect(); + + if (!this.isRemovedEffect(newEffect, oldEffect)) { + return; + } + + Integer customAmplifier = this.effectSettings.customEffects.get(oldEffect.getType()); + + if (customAmplifier == null) { + return; + } + + player.addPotionEffect(new PotionEffect(oldEffect.getType(), -1, customAmplifier)); + } + + private boolean isRemovedEffect(PotionEffect newEffect, PotionEffect oldEffect) { + return newEffect == null && oldEffect != null; + } + +} diff --git a/src/main/java/com/eternalcode/combat/fight/effect/FightEffectService.java b/src/main/java/com/eternalcode/combat/fight/effect/FightEffectService.java new file mode 100644 index 00000000..13910444 --- /dev/null +++ b/src/main/java/com/eternalcode/combat/fight/effect/FightEffectService.java @@ -0,0 +1,75 @@ +package com.eternalcode.combat.fight.effect; + +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.List; +import java.util.ArrayList; + +public class FightEffectService { + + private final Map> activeEffects = new HashMap<>(); + private static final int INFINITE_DURATION = -1; + + public void storeActiveEffect(Player player, PotionEffect effect) { + List effects = this.activeEffects.computeIfAbsent(player.getUniqueId(), k -> new ArrayList<>()); + + effects.add(effect); + } + + public void restoreActiveEffects(Player player) { + List currentEffects = this.getCurrentEffects(player); + + for (PotionEffect effect : currentEffects) { + player.addPotionEffect(effect); + } + + this.clearStoredEffects(player); + } + + public void clearStoredEffects(Player player) { + this.activeEffects.remove(player.getUniqueId()); + } + + public List getCurrentEffects(Player player) { + return this.activeEffects.getOrDefault(player.getUniqueId(), new ArrayList<>()); + } + + public void applyCustomEffect(Player player, PotionEffectType type, Integer amplifier) { + PotionEffect activeEffect = player.getPotionEffect(type); + + if (activeEffect == null) { + player.addPotionEffect(new PotionEffect(type, INFINITE_DURATION, amplifier)); + return; + } + + if (activeEffect.getAmplifier() > amplifier) { + return; + } + + if (activeEffect.getDuration() == -1) { + return; + } + + this.storeActiveEffect(player, activeEffect); + player.addPotionEffect(new PotionEffect(type, INFINITE_DURATION, amplifier)); + } + + public void removeCustomEffect(Player player, PotionEffectType type, Integer amplifier) { + PotionEffect activeEffect = player.getPotionEffect(type); + + if (activeEffect == null) { + return; + } + + if (activeEffect.getAmplifier() != amplifier) { + return; + } + + player.removePotionEffect(type); + } +} diff --git a/src/main/java/com/eternalcode/combat/fight/effect/FightEffectSettings.java b/src/main/java/com/eternalcode/combat/fight/effect/FightEffectSettings.java new file mode 100644 index 00000000..bd3f2cb1 --- /dev/null +++ b/src/main/java/com/eternalcode/combat/fight/effect/FightEffectSettings.java @@ -0,0 +1,26 @@ +package com.eternalcode.combat.fight.effect; + +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Comment; + +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; + +public class FightEffectSettings extends OkaeriConfig { + + @Comment({"# Do you want to add effects to players in combat?"}) + public boolean customEffectsEnabled = true; + + @Comment({ + "# If the option above is set to true, you can add effects to players in combat below", + "# You can find a list of all potion effects here: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/potion/PotionEffectType.html", + "# Correct format: 'EFFECT_TYPE:AMPLIFIER' Amplifier strength starts from 0, so level 1 gives effect strength 2", + "# Example: SPEED:1, DAMAGE_RESISTANCE:0", + }) + public Map customEffects = Map.of( + PotionEffectType.SPEED, 1, + PotionEffectType.DAMAGE_RESISTANCE, 0 + ); + +} diff --git a/src/main/java/com/eternalcode/combat/fight/event/FightTagEvent.java b/src/main/java/com/eternalcode/combat/fight/event/FightTagEvent.java new file mode 100644 index 00000000..8f360625 --- /dev/null +++ b/src/main/java/com/eternalcode/combat/fight/event/FightTagEvent.java @@ -0,0 +1,29 @@ +package com.eternalcode.combat.fight.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.UUID; + +public class FightTagEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + private final UUID player; + + public FightTagEvent(UUID player) { + this.player = player; + } + + public UUID getPlayer() { + return this.player; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/com/eternalcode/combat/fight/event/FightUntagEvent.java b/src/main/java/com/eternalcode/combat/fight/event/FightUntagEvent.java new file mode 100644 index 00000000..b5d4361a --- /dev/null +++ b/src/main/java/com/eternalcode/combat/fight/event/FightUntagEvent.java @@ -0,0 +1,29 @@ +package com.eternalcode.combat.fight.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.UUID; + +public class FightUntagEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + private final UUID player; + + public FightUntagEvent(UUID player) { + this.player = player; + } + + public UUID getPlayer() { + return this.player; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +}