mojira.dev
MC-306995

Warden AI writing to empty memory spaces/null pointers.

(This bug has likely been present since 1.19, but I lack the time to test every version for effect)
It is a bit of a weird one to explain, so I hope this write-up can convey the information. If anything is lacking, I am more than happy to elaborate at request.


Concern:
Based upon the player-facing representation of entity data (NBT text returned from /data), it appears that under specific conditions, Wardens will begin to write redundant data erratically in memory. I lack the means to test exactly how it is failing internally, but it is clear that broken behavior is arising. Furthermore, it appears that this issue may have the potential to cause a non-collectable memory leak.


Context (Useful for understanding why it’s an issue and how to replicate):
Vibrations propagate aggression towards the entity that caused them, if the vibration reaches an active Warden. I began designing a new redstone system that took advantage of this remote aggression to track an entity’s position remotely on ANY axis of movement. There is no way at present, that I am aware of, to do this. Such a system has a large impact on player tracking in survival-mini games. It additionally has a very beneficial use-case for delivering synchronized events using multiple wardens that are angered at one “driving” entity (effectually an @event subscriber).


Replication:
The core of this issue has to do with loading states of the entity, and based on my knowledge of the engine, specifically the Entity ID used to point to the active memory object of a mob. By “trapping” a vibration sourced from a targetable entity in a loop, one can perpetuate a source of anger for the Warden. This “pointer” of a vibration can be kept in the same chunk as a contained warden, while the initiating mob can be moved arbitrarily. While both mobs remain loaded, the warden will continue to pathfind and retain anger as expected, allowing for the uses mentioned in #Context.

However, if the initiating mob is unloaded, the warden will retain its aggression towards it, as the vibration providing the anger still remains loaded. This is expected and good behavior.

If the warden is then unloaded, while the initiating mob remains unloaded, the aggression is retained.

Now is where the issue(s) arise. Upon reloading the warden without reloading the initiating entity (such as approaching the warden from a direction away from the source mob, which can be quite far away given the vibration storage), the vibration is properly loaded, and retains a pointer to the initiating entity. This is good.

BUT, the initiating entity is not loaded, so the game now proceeds to create duplicate entries within the warden’s anger data. This is where my ability to assess the exact memory state and behavior of the game is lacking detail.
I will provide what I can see from in-game as plain text, with my conjecture on behavior following in [].

Observing the warden’s anger data using the /data command returns an array of the anger values, as well as the targeted mob’s UUID. In the condition this issue arises, there will be many entries that continue to appear with a 0s value, all pointing at the same UUID. There will also be the initial value from when the warden was first unloaded. Lastly, once the initiating mob is also loaded, the anger will stop adding duplicate entries. HOWEVER, the warden will almost always NOT become angry at the initiating mob, instead remaining in a broken state wherein it is angry at a mob it cannot find information about, thus becoming stationary. This state persists until all anger data from the warden is cleared, by one of a few means.


Conjecture based on behavior:
[It appears that, due to the discrepancy between how the reference to the initiating mob is stored in a Vibration and in the Warden, the data is remaining valid, presumably pointing to the mob UUID initially. Once this is mapped to an Entity ID (or the proper term for the in-memory address for a loaded mob), it appears to generate a new entry by some arbitrary process. These arbitrary entries seem to exist as some level of ghost data, remaining attached to the warden, but not being back-linked to the initiating mob’s UUID when it loads. Thus, the initiating mob appears at an alternate space in memory, but the multiple present entries for the “same” mob cause the Warden to fetch null data for pathfinding]

[These arbitrary data points seem to slowly add more permanently-used memory to the game, so long as the Warden remains loaded and initiating mob remains unloaded. Theoretically, this would eventually result in an out-of-memory state for the game, and lead to a crash by resource deprivation]



Over the next few days, I have time available, so if there is a preferred method by which I could send a proper copy of a replication setup for testing (structure file, world file, etc), I would be more than happy to oblige. At time of submission, images of the setup thorough enough to allow for replication were not able to be processed by the Mojira system, so they have been omitted.


Edit (3:40AM EST, Mar 23rd):

I realized I forgot to include a shortlist format of the issue’s fundamental steps.

Setup:
Have an Entity which can be targeted (henceforth called ‘Source')
Have a Warden
Have a relay of Sculk Sensors & Amethyst that is large enough to loop a vibration. Ideally, this system is able to be re-loaded without back-feeding that could cause the vibration to be lost if unloaded. This is typically just a right-angle path of sculk sensors and amethyst blocks at max range, such that each relay can only ever send to the one node down-stream.

1) Load the relay with a vibration owned by the Source.

2) Expose the Warden to the vibration relay. It will now become angry at the Source, as expected, and will be able to track its position via pathfinding to least distant position.

3) Unload the Source mob, while retaining the loading of the Warden and Relay. At this point, anger will still be managed as expected. [Presumably because the Entity ID is still in use, and thus not free’d]

4) Unload the Warden and relay

5) Reload the Warden and relay. If the issue occurs successfully, the ‘anger’ tag reported by ‘/data' will show multiple entires targeting the same UUID, with most being at a value of 0s (but never clearing).

6) Reload the Source entity. At this point, new duplicate entries should stop, and one valid entry should be made. However, the Warden may instead refer to one of the 'bad’ entries when trying to find pathfinding data. [Presumably, this is tied to the sort order of the IDs, whatever that may be]

Attachments

Comments 2

Thank you for helping us improve Minecraft! We saved your files:

[media]

Please attach a world that has a setup ready to go to reproduce this issue

Ampere O'Tea

(Unassigned)

Unconfirmed

(Unassigned)

1.21.11

Retrieved