Skip to content

Fix pet entities disappearing by adding setRemoveWhenFarAway(false) and chunk-load persistence#299

Open
Copilot wants to merge 5 commits intomainfrom
copilot/fix-pet-iron-golems-disappearing
Open

Fix pet entities disappearing by adding setRemoveWhenFarAway(false) and chunk-load persistence#299
Copilot wants to merge 5 commits intomainfrom
copilot/fix-pet-iron-golems-disappearing

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 8, 2026

Pet entities (notably iron golems) could despawn because the plugin only called setPersistent(true) at tame time but never set setRemoveWhenFarAway(false) on the Mob, and had no mechanism to re-apply persistence flags after chunk load or server restart.

Changes

  • Pet.ensurePersistence() — new method that applies both setPersistent(true) and setRemoveWhenFarAway(false) (via Mob cast, the correct Spigot API type), with null-safety for unloaded entities
  • PetListRepository.addNewPet() — delegates to ensurePersistence() at tame time to avoid duplicating persistence logic
  • PetListRepository UUID index — added a HashMap<UUID, Pet> index for O(1) pet lookups by entity UUID, replacing the previous O(petLists × petsPerList) linear scan. Maintained via addNewPet(), removePet(), addExistingPet(), and clearAll().
  • PetListRepository.removePet() — guards against null owner PetList and only removes from the UUID index after confirmed successful removal, keeping the index consistent
  • StorageService.applyAIStateToPets() — call ensurePersistence() alongside applyAIState() on the post-load scheduled tick
  • StorageService.loadPets() — uses new addExistingPet() and clearAll() methods to properly maintain the UUID index during load
  • ChunkLoadHandler (new listener) — re-applies persistence on ChunkLoadEvent for any pet entity in the loaded chunk, using the O(1) UUID index
  • PetList.removePet() — restore setRemoveWhenFarAway(true) (via Mob cast) when releasing a pet so freed entities resume normal despawn behavior
// Pet.java
public void ensurePersistence() {
    Entity entity = server.getEntity(uniqueID);
    if (entity == null) return;
    entity.setPersistent(true);
    if (entity instanceof Mob) {
        ((Mob) entity).setRemoveWhenFarAway(false);
    }
}

Unit tests added for ensurePersistence() covering Mob, non-Mob entity, null entity, and null server provider cases.

Original prompt

This section details on the original issue you should resolve

<issue_title>Fix pet iron golems disappearing.</issue_title>
<issue_description>Reported by Lucky on Kingdom: Dark Ages.

This needs to be verified.</issue_description>

Comments on the Issue (you are @copilot in this section)

@dmccoystephenson > Hey there, > > I wanted to look more into this, I was just wondering if there was another other information on this. > > Like were the iron golem disappearing when they used /restart, used the plugman plugin, started/stopped the server? > > Did they mean disappear as in how an iron golem cannot be found when you teleport far away and must use the gather command in the WP plugin? > > Thanks, Gabriel

Hey! Sorry for the late reply. Honestly I don’t remember, but it’s my assumption that it is the latter.</comment_new>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits March 8, 2026 09:29
- Add Pet.ensurePersistence() to re-apply setPersistent(true) and setRemoveWhenFarAway(false)
- Add setRemoveWhenFarAway(false) when taming pets in PetListRepository.addNewPet()
- Re-apply persistence flags after server load in StorageService.applyAIStateToPets()
- Add ChunkLoadHandler to re-apply persistence when pet entities' chunks load
- Restore setRemoveWhenFarAway(true) when releasing pets in PetList.removePet()
- Add unit tests for ensurePersistence()

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…uplicating logic

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix pet iron golems disappearing issue Fix pet entities disappearing by adding setRemoveWhenFarAway(false) and chunk-load persistence Mar 8, 2026
@dmccoystephenson dmccoystephenson requested a review from Copilot March 8, 2026 09:33
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to prevent tamed pet entities (e.g., iron golems) from despawning by consistently applying persistence-related flags at tame time and re-applying them after entity reload scenarios (startup + chunk loads).

Changes:

  • Adds Pet.ensurePersistence() to apply setPersistent(true) and (for LivingEntity) setRemoveWhenFarAway(false) with null-safety.
  • Re-applies persistence after storage load (StorageService.applyAIStateToPets) and on chunk load via a new ChunkLoadHandler listener.
  • Restores normal despawn behavior when releasing a pet by setting setRemoveWhenFarAway(true) (for LivingEntity) in PetList.removePet(), and adds unit tests for ensurePersistence().

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/test/java/dansplugins/wildpets/pet/PetTest.java Adds unit tests covering ensurePersistence() behavior across entity types and null cases.
src/main/java/dansplugins/wildpets/storage/StorageService.java Ensures persistence flags are applied on the delayed post-load tick alongside AI state.
src/main/java/dansplugins/wildpets/pet/list/PetListRepository.java Applies setRemoveWhenFarAway(false) at tame time for LivingEntity pets.
src/main/java/dansplugins/wildpets/pet/list/PetList.java Resets setRemoveWhenFarAway(true) when removing/freeing a pet (for LivingEntity).
src/main/java/dansplugins/wildpets/pet/Pet.java Introduces ensurePersistence() with null-safe entity retrieval and flag application.
src/main/java/dansplugins/wildpets/listeners/ChunkLoadHandler.java New listener that re-applies persistence for pet entities present in loaded chunks.
src/main/java/dansplugins/wildpets/WildPets.java Registers the new ChunkLoadHandler listener.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +24 to +29
public void handle(ChunkLoadEvent event) {
for (Entity entity : event.getChunk().getEntities()) {
Pet pet = petListRepository.getPet(entity);
if (pet != null) {
pet.ensurePersistence();
}
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ChunkLoadEvent can fire very frequently, and this handler does an O(entities_in_chunk × total_pets) scan because petListRepository.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 in PetListRepository (updated on add/remove/load) so the lookup here is O(1), or otherwise avoid nested linear scans in this event handler.

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +129
* Ensures that the pet entity is marked as persistent and will not be removed when far away.
* This prevents the entity from despawning due to distance from players or chunk unloading.
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc says this prevents despawning due to "chunk unloading", but chunk unloading typically unloads entities rather than despawning them; the despawn issue here is primarily distance-based removal and/or loss of flags after reload. Rewording this comment to match the actual behavior (persistence + no-remove-when-far-away, and reapplying after chunk load) would avoid misleading future readers.

Suggested change
* Ensures that the pet entity is marked as persistent and will not be removed when far away.
* This prevents the entity from despawning due to distance from players or chunk unloading.
* 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.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +44
entity.setPersistent(true);
if (entity instanceof LivingEntity) {
((LivingEntity) entity).setRemoveWhenFarAway(false);
}
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Persistence logic is now duplicated between Pet.ensurePersistence() and this tame-time code path (setPersistent(true) + setRemoveWhenFarAway(false)). To reduce the chance of these paths drifting over time, consider reusing Pet.ensurePersistence() here (or extracting a shared helper) so persistence behavior is defined in one place.

Suggested change
entity.setPersistent(true);
if (entity instanceof LivingEntity) {
((LivingEntity) entity).setRemoveWhenFarAway(false);
}
newPet.ensurePersistence();

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot address comments

…doc, deduplicate persistence logic

- Add HashMap<UUID, Pet> index to PetListRepository for O(1) lookups in ChunkLoadHandler
- Fix ensurePersistence() Javadoc to accurately describe distance-based despawn prevention
- Replace inline setPersistent/setRemoveWhenFarAway in addNewPet() with ensurePersistence()
- Add addExistingPet() and clearAll() methods to properly maintain the index

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 8, 2026

@copilot address comments

Addressed all three review comments in e8e5eb2:

  1. Performance (ChunkLoadHandler): Added a HashMap<UUID, Pet> index to PetListRepository so getPet(Entity) is now O(1) instead of O(petLists × petsPerList). The index is maintained via addNewPet(), removePet(), addExistingPet(), and clearAll().

  2. Javadoc (Pet.ensurePersistence): Reworded to accurately describe distance-based despawn prevention rather than chunk unloading.

  3. Duplication (PetListRepository.addNewPet): Replaced inline setPersistent/setRemoveWhenFarAway with newPet.ensurePersistence().

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +148 to +149
if (entity instanceof LivingEntity) {
((LivingEntity) entity).setRemoveWhenFarAway(false);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setRemoveWhenFarAway(...) is not a method on LivingEntity in the Spigot API (it’s on Mob). As written, this will not compile against spigot-api 1.17.1-R0.1-SNAPSHOT. Change the check/cast to entity instanceof Mob and call ((Mob) entity).setRemoveWhenFarAway(false) (and drop the LivingEntity import if no longer needed).

Suggested change
if (entity instanceof LivingEntity) {
((LivingEntity) entity).setRemoveWhenFarAway(false);
if (entity instanceof Mob) {
((Mob) entity).setRemoveWhenFarAway(false);

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +68
if (entity instanceof LivingEntity) {
((LivingEntity) entity).setRemoveWhenFarAway(true);
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in Pet.ensurePersistence(): setRemoveWhenFarAway(...) is not on LivingEntity in Spigot 1.17.1. Use entity instanceof Mob and call ((Mob) entity).setRemoveWhenFarAway(true) so this compiles and only applies to entities that support the flag.

Copilot uses AI. Check for mistakes.
Comment on lines 53 to 54
petsByEntityUUID.remove(petToRemove.getUniqueID());
return getPetList(petToRemove.getOwnerUUID()).removePet(petToRemove);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removePet() removes the entry from petsByEntityUUID before confirming the pet was actually removed from the owning PetList. If PetList.removePet(...) returns false (or the owner list is missing), the UUID index becomes inconsistent with petLists. Consider only removing from the map after a successful removal (and guarding against a null PetList).

Suggested change
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;

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot address comments

@dmccoystephenson dmccoystephenson marked this pull request as ready for review March 22, 2026 17:01
…gainst null PetList

- Pet.java: cast to Mob instead of LivingEntity for setRemoveWhenFarAway()
- PetList.java: same LivingEntity→Mob fix
- PetListRepository.removePet(): only remove from UUID index after confirmed
  removal from PetList, guard against null owner PetList
- Update test comments to reference Mob instead of LivingEntity

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Dans-Plugins/Wild-Pets/sessions/8576f88b-e3bb-4567-902b-11a5f37ec487
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 22, 2026

@copilot address comments

Addressed all three review comments in 59bfb05:

  1. LivingEntityMob: Changed instanceof LivingEntity / (LivingEntity) casts to instanceof Mob / (Mob) in both Pet.ensurePersistence() and PetList.removePet(), since setRemoveWhenFarAway() is defined on Mob in the Spigot API.

  2. PetListRepository.removePet() consistency: Now guards against a null owner PetList and only removes from the UUID index after a successful removal from the PetList.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • hub.spigotmc.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.13/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.13/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.13 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.13/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Wild-Pets/Wild-Pets org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.13/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.13/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.13 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.13/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Wild-Pets/Wild-Pets org.codehaus.plexus.classworlds.launcher.Launcher test -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.13/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.13/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.13 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.13/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Wild-Pets/Wild-Pets org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix pet iron golems disappearing.

3 participants