mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2024-11-24 14:16:45 +01:00
Fix infinite recursion caused by inadvertent chunk loading/unloading Fixes #5112
This commit is contained in:
parent
4cb3d3181b
commit
79ad86ff29
@ -1,3 +1,6 @@
|
|||||||
|
Version 2.2.028
|
||||||
|
Fix stack overflow during ChunkUnloadEvent
|
||||||
|
|
||||||
Version 2.2.027
|
Version 2.2.027
|
||||||
Added Tridents / Crossbows to salvage.vanilla.yml config (see notes)
|
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
|
Fixed an issue where Folia could have all of its threads lock up effectively killing the server
|
||||||
|
2
pom.xml
2
pom.xml
@ -2,7 +2,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.gmail.nossr50.mcMMO</groupId>
|
<groupId>com.gmail.nossr50.mcMMO</groupId>
|
||||||
<artifactId>mcMMO</artifactId>
|
<artifactId>mcMMO</artifactId>
|
||||||
<version>2.2.027</version>
|
<version>2.2.028-SNAPSHOT</version>
|
||||||
<name>mcMMO</name>
|
<name>mcMMO</name>
|
||||||
<url>https://github.com/mcMMO-Dev/mcMMO</url>
|
<url>https://github.com/mcMMO-Dev/mcMMO</url>
|
||||||
<scm>
|
<scm>
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
package com.gmail.nossr50.listeners;
|
package com.gmail.nossr50.listeners;
|
||||||
|
|
||||||
import com.gmail.nossr50.mcMMO;
|
import com.gmail.nossr50.mcMMO;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
import org.bukkit.entity.LivingEntity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ChunkListener implements Listener {
|
public class ChunkListener implements Listener {
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler(ignoreCancelled = true)
|
||||||
public void onChunkUnload(ChunkUnloadEvent event) {
|
public void onChunkUnload(ChunkUnloadEvent event) {
|
||||||
List<LivingEntity> matchingEntities
|
final Chunk unloadingChunk = event.getChunk();
|
||||||
= mcMMO.getTransientEntityTracker().getAllTransientEntitiesInChunk(event.getChunk());
|
Arrays.stream(unloadingChunk.getEntities())
|
||||||
for(LivingEntity livingEntity : matchingEntities) {
|
.filter(entity -> entity instanceof LivingEntity)
|
||||||
mcMMO.getTransientEntityTracker().killSummonAndCleanMobFlags(livingEntity, null, false);
|
.map(entity -> (LivingEntity) entity)
|
||||||
}
|
.forEach(livingEntity -> mcMMO.getTransientEntityTracker().removeTrackedEntity(livingEntity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import com.gmail.nossr50.skills.taming.TrackedTamingEntity;
|
|||||||
import com.gmail.nossr50.util.player.NotificationManager;
|
import com.gmail.nossr50.util.player.NotificationManager;
|
||||||
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
||||||
import com.gmail.nossr50.util.text.StringUtils;
|
import com.gmail.nossr50.util.text.StringUtils;
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.entity.LivingEntity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
@ -19,10 +18,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import static java.util.stream.Collectors.toSet;
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
|
||||||
public class TransientEntityTracker {
|
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() {
|
public TransientEntityTracker() {
|
||||||
playerSummonedEntityTracker = new ConcurrentHashMap<>();
|
this.playerSummonedEntityTracker = new ConcurrentHashMap<>();
|
||||||
|
this.entityLookupCache = ConcurrentHashMap.newKeySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initPlayer(@NotNull Player player) {
|
public void initPlayer(@NotNull Player player) {
|
||||||
@ -33,14 +35,6 @@ public class TransientEntityTracker {
|
|||||||
cleanPlayer(player, player.getUniqueId());
|
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) {
|
public int summonCountForPlayerOfType(@NotNull UUID playerUUID, @NotNull CallOfTheWildType callOfTheWildType) {
|
||||||
return getTrackedEntities(playerUUID, callOfTheWildType).size();
|
return getTrackedEntities(playerUUID, callOfTheWildType).size();
|
||||||
}
|
}
|
||||||
@ -48,6 +42,7 @@ public class TransientEntityTracker {
|
|||||||
public void addSummon(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) {
|
public void addSummon(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) {
|
||||||
playerSummonedEntityTracker.computeIfAbsent(playerUUID, __ -> ConcurrentHashMap.newKeySet())
|
playerSummonedEntityTracker.computeIfAbsent(playerUUID, __ -> ConcurrentHashMap.newKeySet())
|
||||||
.add(trackedTamingEntity);
|
.add(trackedTamingEntity);
|
||||||
|
entityLookupCache.add(trackedTamingEntity.getLivingEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void killSummonAndCleanMobFlags(@NotNull LivingEntity livingEntity, @Nullable Player player,
|
public void killSummonAndCleanMobFlags(@NotNull LivingEntity livingEntity, @Nullable Player player,
|
||||||
@ -77,9 +72,7 @@ public class TransientEntityTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTransient(@NotNull LivingEntity livingEntity) {
|
public boolean isTransient(@NotNull LivingEntity livingEntity) {
|
||||||
return playerSummonedEntityTracker.values().stream().anyMatch(
|
return entityLookupCache.contains(livingEntity);
|
||||||
trackedEntities -> trackedEntities.stream()
|
|
||||||
.anyMatch(trackedTamingEntity -> trackedTamingEntity.getLivingEntity().equals(livingEntity)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NotNull Set<TrackedTamingEntity> getTrackedEntities(@NotNull UUID playerUUID,
|
private @NotNull Set<TrackedTamingEntity> getTrackedEntities(@NotNull UUID playerUUID,
|
||||||
@ -117,7 +110,29 @@ public class TransientEntityTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeSummonFromTracker(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) {
|
public void removeSummonFromTracker(@NotNull UUID playerUUID, @NotNull TrackedTamingEntity trackedTamingEntity) {
|
||||||
playerSummonedEntityTracker.computeIfAbsent(playerUUID, __ -> ConcurrentHashMap.newKeySet())
|
if (playerSummonedEntityTracker.containsKey(playerUUID)) {
|
||||||
.remove(trackedTamingEntity);
|
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<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…
Reference in New Issue
Block a user