Entity data
Author(s): LizzieTags:
Work in progress!
This article is a work in progress! Some sections may be incomplete or need revising.
You may have read the guide on saving and storing data, which covers how to export data to a save file and have it persist between runs. This tutorial covers a similar concept that can be combined with the knowledge of saving data: creating data associated with objects.
GetData
and its drawbacks⚓︎
Isaac provides a simple way of storing data within entities: a method known as GetData. Utilizing GetData
is quite simple, as it can be referenced from any kind of Entity
. GetData
simply returns a table, for the convenience of the user, which can be used to store any amount of arbitrary data and access it later.
Although GetData
sounds convenient, it has several drawbacks which make it bad practice to depend on for modders. The docs provide some reasoning for this, citing that the data table is shared, volatile, and will require its own saving and storing regardless of it being a feature of the API, but these can seem like upsides depending on the use case. Here are a few additional reasons why GetData
is not ideal, and should most likely be avoided:
GetData
creates a small amount of overhead when called. Because it is calling the C++ side, it disrupts the flow of code. Storing the tables on the Lua side would allow for better flow and management of code. Additionally, this overhead can stack up if the method is called consistently. If you still wish to useGetData
, avoid calling it multiple times within the same function, and structure your code to be able to pass it to lower level functions instead of calling the method again.GetData
does some amount of memory allocation on the C++ side, meaning its impact on performance can range depending on the speed and state of the user's memory.
If you are unbothered by these reasons, or performance is not a concern for you, here are a few more reasons why GetData
might not be ideal:
GetData
does not account for items such as Glowing Hourglass. It may be hard to store a backup of data, as it must either be done in a sub-table or already stored locally.GetData
is erased from an entity onMC_POST_ENTITY_REMOVE
, which runs beforeMC_POST_NPC_DEATH
.
Depending on the use case, GetData
may still fit the needs of the modder, providing the convenience of being able to store smaller and simpler data that doesn't necessarily need to be scalable or need a complicated system. In cases where data needs to be scalable, or there are issues of performance, it may be worth writing a simple system to store data locally on the Lua side.
Entity data holders⚓︎
Conversely, writing a entity data holder has many upsides:
- Because the system is in the user's control, the user can control when the data is cleared.
- It can handle specific and custom behavior, such as behavior for instantiation, or whenever the data is cleared.
- Any issues or edge cases can be handled by the system through custom behavior.
While writing a entity data holder is fairly straightforward, there are still a few considerations:
- The user must find a way to store the data in relation to specific entities or objects.
GetPtrHash
is a good starting point, but has several issues: BecauseGetPtrHash
returns a hash based on a pointer, when that memory is eventually cleared, the pointer will be reused. This means that if data is not cleared ahead of time, a new entity or object may reference the data of an old object by accident. - The data must be cleared manually, otherwise it will continue to stack up and cause a memory leak. This is also important for avoiding the previous issue.
In cases where writing a data holder might not be worth the time and effort, or seems undesirable, it may be worth sticking to GetData
. GetData
provides undeniable convenience and value to simple and smaller mods, which may not need to be able to store their own data.
Writing a data holder⚓︎
To get started, simply create a new Lua file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
As it is just a holder, you may require
this Lua file so that the data is persistent across the project. Using include
would make the data local to the file you are including it in.
Clearing data in your holder⚓︎
Lastly, we will cover removing and clearing custom data. Additional callbacks should be registered to clear the information after specified events. Depending on the use case, this can range from clearing data when entities are removed directly in MC_POST_ENTITY_REMOVE
, clearing data when the entity is fully dead in MC_POST_NPC_DEATH
, or clearing entity data at the end of the room with MC_POST_NEW_ROOM
.
Below is an example of how to clear custom data on MC_POST_NEW_ROOM
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Similarly, the following code clears entity data on removal or death:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Further resources⚓︎
- IsaacSaveManager provides many utilities for creating associated data without the use of
GetData
. Data Storage is also much more involved, covering many edge cases like Glowing Hourglass backup saves and reroll dependent and independent save information.