Skip to content

Commit

Permalink
Merge branch 'master' into update
Browse files Browse the repository at this point in the history
  • Loading branch information
SrBedrock committed Nov 18, 2024
2 parents 815b13b + a93f08a commit 993f547
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 117 deletions.
5 changes: 5 additions & 0 deletions Changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Version 2.2.028
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)
Fixed an issue where Folia could have all of its threads lock up effectively killing the server
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.gmail.nossr50.mcMMO</groupId>
<artifactId>mcMMO</artifactId>
<version>2.2.027</version>
<version>2.2.028-SNAPSHOT</version>
<name>mcMMO</name>
<url>https://github.com/mcMMO-Dev/mcMMO</url>
<scm>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.gmail.nossr50.events.skills.fishing;

import com.gmail.nossr50.datatypes.player.McMMOPlayer;
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,
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();
}
}
13 changes: 7 additions & 6 deletions src/main/java/com/gmail/nossr50/listeners/ChunkListener.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
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.List;
import java.util.Arrays;

public class ChunkListener implements Listener {

@EventHandler(ignoreCancelled = true)
public void onChunkUnload(ChunkUnloadEvent event) {
List<LivingEntity> 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(LivingEntity.class::isInstance)
.map(LivingEntity.class::cast)
.forEach(livingEntity -> mcMMO.getTransientEntityTracker().removeTrackedEntity(livingEntity));
}
}
47 changes: 36 additions & 11 deletions src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,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;
Expand All @@ -55,9 +55,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) {
Expand Down Expand Up @@ -272,12 +280,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;

Expand All @@ -287,11 +291,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));
Expand All @@ -318,8 +334,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);
Expand Down Expand Up @@ -555,7 +571,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);
}
}
Expand Down Expand Up @@ -715,4 +732,12 @@ private List<Enchantment> getPossibleEnchantments(ItemStack treasureDrop) {
private int getVanillaXpMultiplier() {
return getVanillaXPBoostModifier();
}

public int getMasterAnglerMinWaitLowerBound() {
return masterAnglerMinWaitLowerBound;
}

public int getMasterAnglerMaxWaitLowerBound() {
return masterAnglerMaxWaitLowerBound;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,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) {
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/gmail/nossr50/util/ItemUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

/**
Expand All @@ -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());
}

/**
Expand Down Expand Up @@ -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()) {
Expand Down
55 changes: 37 additions & 18 deletions src/main/java/com/gmail/nossr50/util/TransientEntityTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,10 +18,13 @@
import static java.util.stream.Collectors.toSet;

public class TransientEntityTracker {
private final @NotNull Map<UUID, Set<TrackedTamingEntity>> playerSummonedEntityTracker;
final @NotNull Map<UUID, Set<TrackedTamingEntity>> playerSummonedEntityTracker;
// used for fast lookups during chunk unload events
final @NotNull Set<LivingEntity> entityLookupCache;

public TransientEntityTracker() {
playerSummonedEntityTracker = new ConcurrentHashMap<>();
this.playerSummonedEntityTracker = new ConcurrentHashMap<>();
this.entityLookupCache = ConcurrentHashMap.newKeySet();
}

public void initPlayer(@NotNull Player player) {
Expand All @@ -33,21 +35,17 @@ public void cleanupPlayer(@NotNull Player player) {
cleanPlayer(player, player.getUniqueId());
}

public @NotNull List<LivingEntity> 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();
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) {
playerSummonedEntityTracker.computeIfAbsent(playerUUID, __ -> ConcurrentHashMap.newKeySet())
.add(trackedTamingEntity);
entityLookupCache.add(trackedTamingEntity.getLivingEntity());
}

public void killSummonAndCleanMobFlags(@NotNull LivingEntity livingEntity, @Nullable Player player,
Expand Down Expand Up @@ -77,9 +75,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<TrackedTamingEntity> getTrackedEntities(@NotNull UUID playerUUID,
Expand Down Expand Up @@ -117,7 +113,30 @@ 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);
entityLookupCache.remove(trackedTamingEntity.getLivingEntity());

if (playerSummonedEntityTracker.containsKey(playerUUID)) {
playerSummonedEntityTracker.get(playerUUID).remove(trackedTamingEntity);
}
}

public void removeTrackedEntity(@NotNull LivingEntity livingEntity) {
// Fail fast if the entity isn't being tracked
if (!entityLookupCache.contains(livingEntity)) {
return;
}

final List<TrackedTamingEntity> 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);
}

}
Loading

0 comments on commit 993f547

Please sign in to comment.