From 79ad86ff29b005594e1be161954dcde1c0afbbfa Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 16 Nov 2024 14:33:51 -0800 Subject: [PATCH 1/6] Fix infinite recursion caused by inadvertent chunk loading/unloading Fixes #5112 --- Changelog.txt | 3 ++ pom.xml | 2 +- .../nossr50/listeners/ChunkListener.java | 12 +++-- .../nossr50/util/TransientEntityTracker.java | 47 ++++++++++++------- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index c7c898bafd..ece84ec195 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,6 @@ +Version 2.2.028 + Fix stack overflow during ChunkUnloadEvent + Version 2.2.027 Added Tridents / Crossbows to salvage.vanilla.yml config (see notes) Fixed an issue where Folia could have all of its threads lock up effectively killing the server diff --git a/pom.xml b/pom.xml index b06f0ce9bd..284d337fa2 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.gmail.nossr50.mcMMO mcMMO - 2.2.027 + 2.2.028-SNAPSHOT mcMMO https://github.com/mcMMO-Dev/mcMMO diff --git a/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java b/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java index f972af3c01..1a6a146d7c 100644 --- a/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java @@ -1,21 +1,23 @@ package com.gmail.nossr50.listeners; import com.gmail.nossr50.mcMMO; +import org.bukkit.Chunk; import org.bukkit.entity.LivingEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkUnloadEvent; +import java.util.Arrays; import java.util.List; public class ChunkListener implements Listener { @EventHandler(ignoreCancelled = true) public void onChunkUnload(ChunkUnloadEvent event) { - List matchingEntities - = mcMMO.getTransientEntityTracker().getAllTransientEntitiesInChunk(event.getChunk()); - for(LivingEntity livingEntity : matchingEntities) { - mcMMO.getTransientEntityTracker().killSummonAndCleanMobFlags(livingEntity, null, false); - } + final Chunk unloadingChunk = event.getChunk(); + Arrays.stream(unloadingChunk.getEntities()) + .filter(entity -> entity instanceof LivingEntity) + .map(entity -> (LivingEntity) entity) + .forEach(livingEntity -> mcMMO.getTransientEntityTracker().removeTrackedEntity(livingEntity)); } } diff --git a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java index f45ca61401..ed3b1c16bd 100644 --- a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java +++ b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java @@ -5,7 +5,6 @@ import com.gmail.nossr50.util.player.NotificationManager; import com.gmail.nossr50.util.skills.ParticleEffectUtils; import com.gmail.nossr50.util.text.StringUtils; -import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Sound; import org.bukkit.entity.LivingEntity; @@ -19,10 +18,13 @@ import static java.util.stream.Collectors.toSet; public class TransientEntityTracker { - private final @NotNull Map> playerSummonedEntityTracker; + final @NotNull Map> playerSummonedEntityTracker; + // used for fast lookups during chunk unload events + final @NotNull Set entityLookupCache; public TransientEntityTracker() { - playerSummonedEntityTracker = new ConcurrentHashMap<>(); + this.playerSummonedEntityTracker = new ConcurrentHashMap<>(); + this.entityLookupCache = ConcurrentHashMap.newKeySet(); } public void initPlayer(@NotNull Player player) { @@ -33,14 +35,6 @@ public void cleanupPlayer(@NotNull Player player) { cleanPlayer(player, player.getUniqueId()); } - public @NotNull List getAllTransientEntitiesInChunk(@NotNull Chunk chunk) { - return playerSummonedEntityTracker.values().stream() - .flatMap(Collection::stream) - .map(TrackedTamingEntity::getLivingEntity) - .filter(livingEntity -> livingEntity.getLocation().getChunk() == chunk) - .toList(); - } - public int summonCountForPlayerOfType(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { return getTrackedEntities(playerUUID, callOfTheWildType).size(); } @@ -48,6 +42,7 @@ public int summonCountForPlayerOfType(@NotNull UUID playerUUID, @NotNull CallOfT public void addSummon(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) { playerSummonedEntityTracker.computeIfAbsent(playerUUID, __ -> ConcurrentHashMap.newKeySet()) .add(trackedTamingEntity); + entityLookupCache.add(trackedTamingEntity.getLivingEntity()); } public void killSummonAndCleanMobFlags(@NotNull LivingEntity livingEntity, @Nullable Player player, @@ -77,9 +72,7 @@ public void killSummonAndCleanMobFlags(@NotNull LivingEntity livingEntity, @Null } public boolean isTransient(@NotNull LivingEntity livingEntity) { - return playerSummonedEntityTracker.values().stream().anyMatch( - trackedEntities -> trackedEntities.stream() - .anyMatch(trackedTamingEntity -> trackedTamingEntity.getLivingEntity().equals(livingEntity))); + return entityLookupCache.contains(livingEntity); } private @NotNull Set getTrackedEntities(@NotNull UUID playerUUID, @@ -117,7 +110,29 @@ public void killAndCleanSummon(@NotNull UUID playerUUID, @Nullable Player player } public void removeSummonFromTracker(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) { - playerSummonedEntityTracker.computeIfAbsent(playerUUID, __ -> ConcurrentHashMap.newKeySet()) - .remove(trackedTamingEntity); + if (playerSummonedEntityTracker.containsKey(playerUUID)) { + playerSummonedEntityTracker.get(playerUUID).remove(trackedTamingEntity); + entityLookupCache.remove(trackedTamingEntity.getLivingEntity()); + } + } + + public void removeTrackedEntity(@NotNull LivingEntity livingEntity) { + // Fail fast if the entity isn't being tracked + if (!entityLookupCache.contains(livingEntity)) { + return; + } + + final List matchingEntities = new ArrayList<>(); + + // Collect matching entities without copying each set + playerSummonedEntityTracker.values().forEach(trackedEntitiesPerPlayer -> + trackedEntitiesPerPlayer.stream() + .filter(trackedTamingEntity -> trackedTamingEntity.getLivingEntity() == livingEntity) + .forEach(matchingEntities::add) + ); + + // Iterate over the collected list to handle removal and cleanup + matchingEntities.forEach(TrackedTamingEntity::run); } + } From 570cadb2765a4dc095c941b908b9e9d3a422dc0e Mon Sep 17 00:00:00 2001 From: Jacob Date: Sat, 16 Nov 2024 18:24:05 -0500 Subject: [PATCH 2/6] add McMMOPlayerMasterAnglerEvent and ability to cancel it if desired (#5111) --- .../fishing/McMMOPlayerMasterAnglerEvent.java | 14 ++++++++++++++ .../nossr50/skills/fishing/FishingManager.java | 8 ++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java diff --git a/src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java b/src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java new file mode 100644 index 0000000000..c7b77baa61 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java @@ -0,0 +1,14 @@ +package com.gmail.nossr50.events.skills.fishing; + +import com.gmail.nossr50.datatypes.player.McMMOPlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class McMMOPlayerMasterAnglerEvent extends McMMOPlayerFishingEvent { + + public McMMOPlayerMasterAnglerEvent(@NotNull McMMOPlayer mcMMOPlayer) { + super(mcMMOPlayer); + } + +} \ No newline at end of file diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index 3ca634ec60..96cfccaf37 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java @@ -10,6 +10,7 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.treasure.*; import com.gmail.nossr50.events.skills.fishing.McMMOPlayerFishingTreasureEvent; +import com.gmail.nossr50.events.skills.fishing.McMMOPlayerMasterAnglerEvent; import com.gmail.nossr50.events.skills.fishing.McMMOPlayerShakeEvent; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.mcMMO; @@ -253,6 +254,13 @@ public void masterAngler(@NotNull FishHook hook, int lureLevel) { * @param fishHook target fish hook */ public void processMasterAngler(@NotNull FishHook fishHook, int lureLevel) { + McMMOPlayerMasterAnglerEvent event = new McMMOPlayerMasterAnglerEvent(mmoPlayer); + mcMMO.p.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + MasterAnglerCompatibilityLayer masterAnglerCompatibilityLayer = (MasterAnglerCompatibilityLayer) mcMMO.getCompatibilityManager().getMasterAnglerCompatibilityLayer(); if (masterAnglerCompatibilityLayer != null) { From 69e90a89f7319e3b0302159b2c70b7abb1ef82be Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 16 Nov 2024 15:24:48 -0800 Subject: [PATCH 3/6] minor refactoring --- .../skills/fishing/FishingManager.java | 3 +- .../nossr50/util/skills/CombatUtils.java | 61 ++++++------------- 2 files changed, 21 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index 3ca634ec60..12c193cef3 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java @@ -555,7 +555,8 @@ public void shakeCheck(@NotNull LivingEntity target) { } ItemUtils.spawnItem(getPlayer(), target.getLocation(), drop, ItemSpawnReason.FISHING_SHAKE_TREASURE); - CombatUtils.dealDamage(target, Math.min(Math.max(target.getMaxHealth() / 4, 1), 10), EntityDamageEvent.DamageCause.CUSTOM, getPlayer()); // Make it so you can shake a mob no more than 4 times. + // Make it so you can shake a mob no more than 4 times. + CombatUtils.dealDamage(target, Math.min(Math.max(target.getMaxHealth() / 4, 1), 10), getPlayer()); applyXpGain(ExperienceConfig.getInstance().getFishingShakeXP(), XPGainReason.PVE); } } diff --git a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java index b48fcf5ec6..4b4e8f9962 100644 --- a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java +++ b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java @@ -509,7 +509,6 @@ public static void processCombatAttack(@NotNull EntityDamageByEntityEvent event, if (mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.SWORDS)) { processSwordCombat(target, player, event); - } } else if (ItemUtils.isAxe(heldItem)) { if (!mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.AXES, target)) { @@ -698,46 +697,17 @@ public static boolean canUseLimitBreak(@NotNull Player player, LivingEntity targ * @param damage Amount of damage to attempt to do */ public static void dealDamage(@NotNull LivingEntity target, double damage) { - dealDamage(target, damage, DamageCause.CUSTOM, null); + dealDamage(target, damage, null); } /** * Attempt to damage target for value dmg with reason ENTITY_ATTACK with damager attacker * - * @param target LivingEntity which to attempt to damage + * @param target the entity to attempt to damage * @param damage Amount of damage to attempt to do - * @param attacker Player to pass to event as damager + * @param attacker the responsible entity (nullable) */ - @Deprecated - public static void dealDamage(@NotNull LivingEntity target, double damage, @NotNull LivingEntity attacker) { - dealDamage(target, damage, DamageCause.CUSTOM, attacker); - } - -// /** -// * Attempt to damage target for value dmg with reason ENTITY_ATTACK with damager attacker -// * -// * @param target LivingEntity which to attempt to damage -// * @param damage Amount of damage to attempt to do -// * @param attacker Player to pass to event as damager -// */ -// public static void dealDamage(LivingEntity target, double damage, Map modifiers, LivingEntity attacker) { -// if (target.isDead()) { -// return; -// } -// -// // Aren't we applying the damage twice???? -// target.damage(getFakeDamageFinalResult(attacker, target, damage, modifiers)); -// } - - /** - * Attempt to damage target for value dmg with reason ENTITY_ATTACK with damager attacker - * - * @param target LivingEntity which to attempt to damage - * @param damage Amount of damage to attempt to do - * @param attacker Player to pass to event as damager - */ - @Deprecated - public static void dealDamage(@NotNull LivingEntity target, double damage, @NotNull DamageCause cause, @Nullable Entity attacker) { + public static void dealDamage(@NotNull LivingEntity target, double damage, @Nullable Entity attacker) { if (target.isDead()) { return; } @@ -807,12 +777,13 @@ public static void dealNoInvulnerabilityTickDamageRupture(@NotNull LivingEntity /** * Apply Area-of-Effect ability actions. - * @param attacker The attacking player + * @param attacker The attacking player * @param target The defending entity * @param damage The initial damage amount * @param type The type of skill being used */ - public static void applyAbilityAoE(@NotNull Player attacker, @NotNull LivingEntity target, double damage, @NotNull PrimarySkillType type) { + public static void applyAbilityAoE(@NotNull Player attacker, @NotNull LivingEntity target, + double damage, @NotNull PrimarySkillType type) { int numberOfTargets = getTier(attacker.getInventory().getItemInMainHand()); // The higher the weapon tier, the more targets you hit double damageAmount = Math.max(damage, 1); @@ -821,7 +792,8 @@ public static void applyAbilityAoE(@NotNull Player attacker, @NotNull LivingEnti break; } - if ((ExperienceConfig.getInstance().isNPCInteractionPrevented() && Misc.isNPCEntityExcludingVillagers(entity)) + if ((ExperienceConfig.getInstance().isNPCInteractionPrevented() + && Misc.isNPCEntityExcludingVillagers(entity)) || !(entity instanceof LivingEntity livingEntity) || !shouldBeAffected(attacker, entity)) { continue; } @@ -831,7 +803,8 @@ public static void applyAbilityAoE(@NotNull Player attacker, @NotNull LivingEnti switch (type) { case SWORDS: if (entity instanceof Player) { - NotificationManager.sendPlayerInformation((Player)entity, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.SS.Struck"); + NotificationManager.sendPlayerInformation((Player)entity, NotificationType.SUBSKILL_MESSAGE, + "Swords.Combat.SS.Struck"); } final McMMOPlayer mmoAttacker = UserManager.getPlayer(attacker); @@ -844,9 +817,9 @@ public static void applyAbilityAoE(@NotNull Player attacker, @NotNull LivingEnti case AXES: if (entity instanceof Player) { - NotificationManager.sendPlayerInformation((Player)entity, NotificationType.SUBSKILL_MESSAGE, "Axes.Combat.SS.Struck"); + NotificationManager.sendPlayerInformation((Player) entity, NotificationType.SUBSKILL_MESSAGE, + "Axes.Combat.SS.Struck"); } - break; default: @@ -963,7 +936,9 @@ private static boolean shouldBeAffected(@NotNull Player player, @NotNull Entity } if (mcMMO.p.getPartyConfig().isPartyEnabled()) { - if ((mcMMO.p.getPartyManager().inSameParty(player, defender) || mcMMO.p.getPartyManager().areAllies(player, defender)) && !(Permissions.friendlyFire(player) && Permissions.friendlyFire(defender))) { + if ((mcMMO.p.getPartyManager().inSameParty(player, defender) + || mcMMO.p.getPartyManager().areAllies(player, defender)) + && !(Permissions.friendlyFire(player) && Permissions.friendlyFire(defender))) { return false; } } @@ -1015,7 +990,9 @@ public static boolean isFriendlyPet(@NotNull Player attacker, @NotNull Tameable if (tamer instanceof Player owner) { - return (owner == attacker || (mcMMO.p.getPartyConfig().isPartyEnabled() && (mcMMO.p.getPartyManager().inSameParty(attacker, owner) || mcMMO.p.getPartyManager().areAllies(attacker, owner)))); + return (owner == attacker || (mcMMO.p.getPartyConfig().isPartyEnabled() + && (mcMMO.p.getPartyManager().inSameParty(attacker, owner) + || mcMMO.p.getPartyManager().areAllies(attacker, owner)))); } } From 19bf96ab33708758c75f8ffb3f87f80677576415 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 16 Nov 2024 15:48:41 -0800 Subject: [PATCH 4/6] Expand API for McMMOPlayerMasterAnglerEvent --- .../fishing/McMMOPlayerMasterAnglerEvent.java | 45 +++++++++++++++-- .../skills/fishing/FishingManager.java | 49 +++++++++++++------ 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java b/src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java index c7b77baa61..a3532fa232 100644 --- a/src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java +++ b/src/main/java/com/gmail/nossr50/events/skills/fishing/McMMOPlayerMasterAnglerEvent.java @@ -1,14 +1,53 @@ package com.gmail.nossr50.events.skills.fishing; import com.gmail.nossr50.datatypes.player.McMMOPlayer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; +import com.gmail.nossr50.skills.fishing.FishingManager; import org.jetbrains.annotations.NotNull; public class McMMOPlayerMasterAnglerEvent extends McMMOPlayerFishingEvent { + private int reducedMinWaitTime; + private int reducedMaxWaitTime; + private final FishingManager fishingManager; - public McMMOPlayerMasterAnglerEvent(@NotNull McMMOPlayer mcMMOPlayer) { + public McMMOPlayerMasterAnglerEvent(@NotNull McMMOPlayer mcMMOPlayer, + int reducedMinWaitTime, + int reducedMaxWaitTime, + FishingManager fishingManager) { super(mcMMOPlayer); + this.fishingManager = fishingManager; + this.reducedMinWaitTime = Math.max(reducedMinWaitTime, getReducedMinWaitTimeLowerBound()); + this.reducedMaxWaitTime = Math.max(reducedMaxWaitTime, getReducedMaxWaitTimeLowerBound()); } + public int getReducedMinWaitTime() { + return reducedMinWaitTime; + } + + public void setReducedMinWaitTime(int reducedMinWaitTime) { + if (reducedMinWaitTime < 0 || reducedMinWaitTime > reducedMaxWaitTime) { + throw new IllegalArgumentException("Reduced min wait time must be greater than or equal to 0" + + " and less than reduced max wait time."); + } + this.reducedMinWaitTime = Math.max(reducedMinWaitTime, getReducedMinWaitTimeLowerBound()); + } + + public int getReducedMaxWaitTime() { + return reducedMaxWaitTime; + } + + public void setReducedMaxWaitTime(int reducedMaxWaitTime) { + if (reducedMaxWaitTime < 0 || reducedMaxWaitTime < reducedMinWaitTime) { + throw new IllegalArgumentException("Reduced max wait time must be greater than or equal to 0" + + " and greater than reduced min wait time."); + } + this.reducedMaxWaitTime = Math.max(reducedMaxWaitTime, getReducedMaxWaitTimeLowerBound()); + } + + public int getReducedMinWaitTimeLowerBound() { + return fishingManager.getMasterAnglerMinWaitLowerBound(); + } + + public int getReducedMaxWaitTimeLowerBound() { + return fishingManager.getMasterAnglerMaxWaitLowerBound(); + } } \ No newline at end of file diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index 2466988c7e..ed0094a56c 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java @@ -56,9 +56,17 @@ public class FishingManager extends SkillManager { private Item fishingCatch; private Location hookLocation; private int fishCaughtCounter = 1; + private final int masterAnglerMinWaitLowerBound; + private final int masterAnglerMaxWaitLowerBound; public FishingManager(McMMOPlayer mcMMOPlayer) { super(mcMMOPlayer, PrimarySkillType.FISHING); + //Ticks for minWait and maxWait never go below this value + int bonusCapMin = mcMMO.p.getAdvancedConfig().getFishingReductionMinWaitCap(); + int bonusCapMax = mcMMO.p.getAdvancedConfig().getFishingReductionMaxWaitCap(); + + this.masterAnglerMinWaitLowerBound = Math.max(bonusCapMin, 0); + this.masterAnglerMaxWaitLowerBound = Math.max(bonusCapMax, masterAnglerMinWaitLowerBound + 40); } public boolean canShake(Entity target) { @@ -254,13 +262,6 @@ public void masterAngler(@NotNull FishHook hook, int lureLevel) { * @param fishHook target fish hook */ public void processMasterAngler(@NotNull FishHook fishHook, int lureLevel) { - McMMOPlayerMasterAnglerEvent event = new McMMOPlayerMasterAnglerEvent(mmoPlayer); - mcMMO.p.getServer().getPluginManager().callEvent(event); - - if (event.isCancelled()) { - return; - } - MasterAnglerCompatibilityLayer masterAnglerCompatibilityLayer = (MasterAnglerCompatibilityLayer) mcMMO.getCompatibilityManager().getMasterAnglerCompatibilityLayer(); if (masterAnglerCompatibilityLayer != null) { @@ -280,12 +281,8 @@ public void processMasterAngler(@NotNull FishHook fishHook, int lureLevel) { int minWaitReduction = getMasterAnglerTickMinWaitReduction(masterAnglerRank, boatBonus); int maxWaitReduction = getMasterAnglerTickMaxWaitReduction(masterAnglerRank, boatBonus, convertedLureBonus); - //Ticks for minWait and maxWait never go below this value - int bonusCapMin = mcMMO.p.getAdvancedConfig().getFishingReductionMinWaitCap(); - int bonusCapMax = mcMMO.p.getAdvancedConfig().getFishingReductionMaxWaitCap(); - - int reducedMinWaitTime = getReducedTicks(minWaitTicks, minWaitReduction, bonusCapMin); - int reducedMaxWaitTime = getReducedTicks(maxWaitTicks, maxWaitReduction, bonusCapMax); + int reducedMinWaitTime = getReducedTicks(minWaitTicks, minWaitReduction, masterAnglerMinWaitLowerBound); + int reducedMaxWaitTime = getReducedTicks(maxWaitTicks, maxWaitReduction, masterAnglerMaxWaitLowerBound); boolean badValuesFix = false; @@ -295,11 +292,23 @@ public void processMasterAngler(@NotNull FishHook fishHook, int lureLevel) { badValuesFix = true; } + final McMMOPlayerMasterAnglerEvent event = + new McMMOPlayerMasterAnglerEvent(mmoPlayer, reducedMinWaitTime, reducedMaxWaitTime, this); + mcMMO.p.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + reducedMaxWaitTime = event.getReducedMaxWaitTime(); + reducedMinWaitTime = event.getReducedMinWaitTime(); + if (mmoPlayer.isDebugMode()) { mmoPlayer.getPlayer().sendMessage(ChatColor.GOLD + "Master Angler Debug"); if (badValuesFix) { - mmoPlayer.getPlayer().sendMessage(ChatColor.RED + "Bad values were applied and corrected, check your configs, max wait should never be lower than min wait."); + mmoPlayer.getPlayer().sendMessage(ChatColor.RED + "Bad values were applied and corrected," + + " check your configs, minWaitLowerBound wait should never be lower than min wait."); } mmoPlayer.getPlayer().sendMessage("ALLOW STACK WITH LURE: " + masterAnglerCompatibilityLayer.getApplyLure(fishHook)); @@ -326,8 +335,8 @@ public void processMasterAngler(@NotNull FishHook fishHook, int lureLevel) { mmoPlayer.getPlayer().sendMessage(""); mmoPlayer.getPlayer().sendMessage(ChatColor.DARK_AQUA + "Caps / Limits (edit in advanced.yml)"); - mmoPlayer.getPlayer().sendMessage("Lowest possible max wait ticks " + bonusCapMax); - mmoPlayer.getPlayer().sendMessage("Lowest possible min wait ticks " + bonusCapMin); + mmoPlayer.getPlayer().sendMessage("Lowest possible minWaitLowerBound wait ticks " + masterAnglerMinWaitLowerBound); + mmoPlayer.getPlayer().sendMessage("Lowest possible min wait ticks " + masterAnglerMaxWaitLowerBound); } masterAnglerCompatibilityLayer.setMaxWaitTime(fishHook, reducedMaxWaitTime); @@ -724,4 +733,12 @@ private List getPossibleEnchantments(ItemStack treasureDrop) { private int getVanillaXpMultiplier() { return getVanillaXPBoostModifier(); } + + public int getMasterAnglerMinWaitLowerBound() { + return masterAnglerMinWaitLowerBound; + } + + public int getMasterAnglerMaxWaitLowerBound() { + return masterAnglerMaxWaitLowerBound; + } } \ No newline at end of file From 91ab569b81190f1996a0f13a6d7351bb93ed6ef5 Mon Sep 17 00:00:00 2001 From: nossr50 Date: Sat, 16 Nov 2024 16:10:05 -0800 Subject: [PATCH 5/6] Fixed COTW summon bug --- Changelog.txt | 4 +++- .../com/gmail/nossr50/listeners/ChunkListener.java | 1 - .../gmail/nossr50/skills/fishing/FishingManager.java | 1 - .../com/gmail/nossr50/skills/taming/TamingManager.java | 2 +- .../com/gmail/nossr50/util/TransientEntityTracker.java | 10 +++++++--- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index ece84ec195..2e0faee347 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,5 +1,7 @@ Version 2.2.028 - Fix stack overflow during ChunkUnloadEvent + Fixed a bug where you had to wait to summon another COTW summon if one or more of them had died or otherwise expired before their time limit + Fixed stack overflow during ChunkUnloadEvent + (API) Added McMMOPlayerMasterAnglerEvent (thanks bobcat4848) Version 2.2.027 Added Tridents / Crossbows to salvage.vanilla.yml config (see notes) diff --git a/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java b/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java index 1a6a146d7c..000d3ca8c9 100644 --- a/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/ChunkListener.java @@ -8,7 +8,6 @@ import org.bukkit.event.world.ChunkUnloadEvent; import java.util.Arrays; -import java.util.List; public class ChunkListener implements Listener { diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index ed0094a56c..f93b1d61dc 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java @@ -31,7 +31,6 @@ import org.bukkit.block.BlockFace; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.*; -import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.SkullMeta; diff --git a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java index e802e6bd20..104231f69e 100644 --- a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java @@ -475,7 +475,7 @@ public boolean isCOTWItem(@NotNull ItemStack itemStack) { } private int getAmountCurrentlySummoned(@NotNull CallOfTheWildType callOfTheWildType) { - return mcMMO.getTransientEntityTracker().summonCountForPlayerOfType(getPlayer().getUniqueId(), callOfTheWildType); + return mcMMO.getTransientEntityTracker().getActiveSummonsForPlayerOfType(getPlayer().getUniqueId(), callOfTheWildType); } private void addToTracker(@NotNull LivingEntity livingEntity, @NotNull CallOfTheWildType callOfTheWildType) { diff --git a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java index ed3b1c16bd..fccff2664b 100644 --- a/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java +++ b/src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java @@ -35,8 +35,11 @@ public void cleanupPlayer(@NotNull Player player) { cleanPlayer(player, player.getUniqueId()); } - public int summonCountForPlayerOfType(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { - return getTrackedEntities(playerUUID, callOfTheWildType).size(); + public int getActiveSummonsForPlayerOfType(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) { + return getTrackedEntities(playerUUID, callOfTheWildType).stream() + .filter(tte -> tte.getLivingEntity().isValid()) + .mapToInt(tte -> 1) + .sum(); } public void addSummon(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) { @@ -110,9 +113,10 @@ public void killAndCleanSummon(@NotNull UUID playerUUID, @Nullable Player player } public void removeSummonFromTracker(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) { + entityLookupCache.remove(trackedTamingEntity.getLivingEntity()); + if (playerSummonedEntityTracker.containsKey(playerUUID)) { playerSummonedEntityTracker.get(playerUUID).remove(trackedTamingEntity); - entityLookupCache.remove(trackedTamingEntity.getLivingEntity()); } } From a93f08a97b61f65b2665b92550c2c9fd66abe386 Mon Sep 17 00:00:00 2001 From: galacticwarrior9 <9055352+galacticwarrior9@users.noreply.github.com> Date: Sun, 17 Nov 2024 00:11:14 +0000 Subject: [PATCH 6/6] Fix McMMOItemSpawnEvent#setItemStack being ignored in ItemUtils (#5102) --- src/main/java/com/gmail/nossr50/util/ItemUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/ItemUtils.java b/src/main/java/com/gmail/nossr50/util/ItemUtils.java index d3aee583c2..433651cb28 100644 --- a/src/main/java/com/gmail/nossr50/util/ItemUtils.java +++ b/src/main/java/com/gmail/nossr50/util/ItemUtils.java @@ -764,7 +764,7 @@ public static void spawnItems(@Nullable Player player, return null; } - return location.getWorld().dropItem(location, itemStack); + return location.getWorld().dropItem(location, event.getItemStack()); } /** @@ -791,7 +791,7 @@ public static void spawnItems(@Nullable Player player, return null; } - return location.getWorld().dropItemNaturally(location, itemStack); + return location.getWorld().dropItemNaturally(location, event.getItemStack()); } /** @@ -845,6 +845,7 @@ public static void spawnItemsTowardsLocation(@Nullable Player player, // We can't get the item until we spawn it and we want to make it cancellable, so we have a custom event. McMMOItemSpawnEvent event = new McMMOItemSpawnEvent(spawnLocation, clonedItem, itemSpawnReason, player); mcMMO.p.getServer().getPluginManager().callEvent(event); + clonedItem = event.getItemStack(); //Something cancelled the event so back out if (event.isCancelled()) {