-
Notifications
You must be signed in to change notification settings - Fork 2
Fix pet entities disappearing by adding setRemoveWhenFarAway(false) and chunk-load persistence #299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
2800c0c
d8a2978
b07db3e
e8e5eb2
59bfb05
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package dansplugins.wildpets.listeners; | ||
|
|
||
| import dansplugins.wildpets.pet.Pet; | ||
| import dansplugins.wildpets.pet.list.PetListRepository; | ||
| import org.bukkit.entity.Entity; | ||
| import org.bukkit.event.EventHandler; | ||
| import org.bukkit.event.Listener; | ||
| import org.bukkit.event.world.ChunkLoadEvent; | ||
|
|
||
| /** | ||
| * Handles chunk load events to ensure pet entities retain their | ||
| * persistence flags, preventing them from despawning. | ||
| * | ||
| * @author Daniel McCoy Stephenson | ||
| */ | ||
| public class ChunkLoadHandler implements Listener { | ||
| private final PetListRepository petListRepository; | ||
|
|
||
| public ChunkLoadHandler(PetListRepository petListRepository) { | ||
| this.petListRepository = petListRepository; | ||
| } | ||
|
|
||
| @EventHandler() | ||
| public void handle(ChunkLoadEvent event) { | ||
| for (Entity entity : event.getChunk().getEntities()) { | ||
| Pet pet = petListRepository.getPet(entity); | ||
| if (pet != null) { | ||
| pet.ensurePersistence(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |||||||||
| import dansplugins.wildpets.location.WpLocation; | ||||||||||
| import org.bukkit.Server; | ||||||||||
| import org.bukkit.entity.Entity; | ||||||||||
| import org.bukkit.entity.LivingEntity; | ||||||||||
| import org.bukkit.entity.Mob; | ||||||||||
| import dansplugins.wildpets.data.Lockable; | ||||||||||
| import dansplugins.wildpets.data.Savable; | ||||||||||
|
|
@@ -123,6 +124,32 @@ public void applyAIState() { | |||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Ensures that, for a currently loaded pet entity, persistence and "do not remove when far away" | ||||||||||
| * flags are applied. | ||||||||||
| * This helps prevent the entity from being removed by distance-based despawn mechanics while it | ||||||||||
| * is loaded. It does not prevent chunk unloading itself; these flags may need to be re-applied | ||||||||||
| * after the entity is reloaded. | ||||||||||
| * Returns silently if the entity is not currently loaded. | ||||||||||
| */ | ||||||||||
| public void ensurePersistence() { | ||||||||||
| if (serverProvider == null) { | ||||||||||
| return; | ||||||||||
| } | ||||||||||
| Server server = serverProvider.get(); | ||||||||||
| if (server == null) { | ||||||||||
| return; | ||||||||||
| } | ||||||||||
| Entity entity = server.getEntity(uniqueID); | ||||||||||
| if (entity == null) { | ||||||||||
| return; | ||||||||||
| } | ||||||||||
| entity.setPersistent(true); | ||||||||||
| if (entity instanceof LivingEntity) { | ||||||||||
| ((LivingEntity) entity).setRemoveWhenFarAway(false); | ||||||||||
|
||||||||||
| if (entity instanceof LivingEntity) { | |
| ((LivingEntity) entity).setRemoveWhenFarAway(false); | |
| if (entity instanceof Mob) { | |
| ((Mob) entity).setRemoveWhenFarAway(false); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| import org.bukkit.Bukkit; | ||
| import org.bukkit.ChatColor; | ||
| import org.bukkit.entity.Entity; | ||
| import org.bukkit.entity.LivingEntity; | ||
| import org.bukkit.entity.Player; | ||
|
|
||
| import dansplugins.wildpets.config.ConfigService; | ||
|
|
@@ -62,6 +63,9 @@ public boolean removePet(Pet petToRemove) { | |
| if (entity != null) { | ||
| entity.setCustomName(""); | ||
| entity.setPersistent(false); | ||
| if (entity instanceof LivingEntity) { | ||
| ((LivingEntity) entity).setRemoveWhenFarAway(true); | ||
| } | ||
|
||
| entity.setInvulnerable(false); | ||
| } | ||
| return getPets().remove(petToRemove); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |||||||||||||||||||||||
| import org.bukkit.entity.Player; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||||
| import java.util.HashMap; | ||||||||||||||||||||||||
| import java.util.UUID; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
|
|
@@ -20,6 +21,7 @@ public class PetListRepository { | |||||||||||||||||||||||
| private final ConfigService configService; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private final ArrayList<PetList> petLists = new ArrayList<>(); | ||||||||||||||||||||||||
| private final HashMap<UUID, Pet> petsByEntityUUID = new HashMap<>(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public PetListRepository(ConfigService configService) { | ||||||||||||||||||||||||
| this.configService = configService; | ||||||||||||||||||||||||
|
|
@@ -37,27 +39,23 @@ public boolean addNewPet(Player player, Entity entity) { | |||||||||||||||||||||||
| WpLocation wpLocation = new WpLocation(bukkitLocation.getX(), bukkitLocation.getY(), bukkitLocation.getZ()); | ||||||||||||||||||||||||
| newPet.setLastKnownLocation(wpLocation); | ||||||||||||||||||||||||
| entity.setCustomName(ChatColor.GREEN + newPet.getName()); | ||||||||||||||||||||||||
| entity.setPersistent(true); | ||||||||||||||||||||||||
| newPet.ensurePersistence(); | ||||||||||||||||||||||||
| entity.playEffect(EntityEffect.LOVE_HEARTS); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // add pet to pet list | ||||||||||||||||||||||||
| PetList petList = getPetList(player.getUniqueId()); | ||||||||||||||||||||||||
| petList.addPet(newPet); | ||||||||||||||||||||||||
| petsByEntityUUID.put(newPet.getUniqueID(), newPet); | ||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public boolean removePet(Pet petToRemove) { | ||||||||||||||||||||||||
| petsByEntityUUID.remove(petToRemove.getUniqueID()); | ||||||||||||||||||||||||
| return getPetList(petToRemove.getOwnerUUID()).removePet(petToRemove); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| petsByEntityUUID.remove(petToRemove.getUniqueID()); | |
| return getPetList(petToRemove.getOwnerUUID()).removePet(petToRemove); | |
| PetList ownerPetList = getPetList(petToRemove.getOwnerUUID()); | |
| if (ownerPetList == null) { | |
| return false; | |
| } | |
| boolean removed = ownerPetList.removePet(petToRemove); | |
| if (removed) { | |
| petsByEntityUUID.remove(petToRemove.getUniqueID()); | |
| } | |
| return removed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ChunkLoadEventcan fire very frequently, and this handler does an O(entities_in_chunk × total_pets) scan becausepetListRepository.getPet(entity)iterates all pet lists and each list iterates its pets. On servers with many pets/players this can create noticeable lag spikes during chunk loads. Consider maintaining a UUID→Pet index inPetListRepository(updated on add/remove/load) so the lookup here is O(1), or otherwise avoid nested linear scans in this event handler.