Skip to content

Commit 6ec7da3

Browse files
committed
LootList userdata now properly owns the contents of the deque, + garbage collection
1 parent 7242aa5 commit 6ec7da3

File tree

2 files changed

+22
-22
lines changed

2 files changed

+22
-22
lines changed

changelog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ Fixes:
191191
- Fixed MC_GET_STATUS_EFFECT_TARGET not running for the "shrink" status effect.
192192
* EntityPlayer:
193193
- Fixed SetPoopSpell & RemovePoopSpell not working correctly and messing up the poop mana value.
194+
* EntityPickup
195+
- Fix some crashes related to GetLootList
194196
* CharacterMenu:
195197
- Fixed GetCharacterMenuIDFromPlayerType not returning the correct value for Tainted Isaac
196198
* Fixed modded characters' locked/door sprites rendering on the "continue" widget if they are locked behind an achievement.

repentogon/LuaInterfaces/LuaLootList.cpp

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
#include "HookSystem.h"
44

55
LUA_FUNCTION(Lua_LootListConstructor) {
6-
LootList* loot = nullptr;
7-
loot = lua::place<LootList>(L, lua::metatables::LootListMT);
8-
6+
// Initializes a new LootList (std::deque) under lua ownership and pushes a reference to the stack.
7+
lua::place<LootList>(L, lua::metatables::LootListMT);
98
return 1;
109
}
1110

@@ -30,46 +29,46 @@ LUA_FUNCTION(Lua_PickupGetLootList) {
3029
Entity_Pickup* pickup = lua::GetLuabridgeUserdata<Entity_Pickup*>(L, 1, lua::Metatables::ENTITY_PICKUP, "EntityPickup");
3130
bool shouldAdvance = lua::luaL_optboolean(L, 2, false);
3231

33-
LootList loot = pickup->GetLootList(shouldAdvance, nullptr);
3432
LootList* toLua = (LootList*)lua_newuserdata(L, sizeof(LootList));
33+
luaL_setmetatable(L, lua::metatables::LootListMT);
34+
// Initialize a new LootList (std::deque) in the memory allocated for the userdata, using "placement new".
35+
// Using the move constructor, we can "steal" the contents of the deque returned by the function without having to copy everything.
36+
// If we just copied the raw bytes of the deque, the allocated contents would still be freed alongside the original deque.
37+
// This allows lua to properly take ownership of the deque. Destroying the deque later is handled by `Lua_LootList__gc`.
38+
new (toLua) LootList(std::move(pickup->GetLootList(shouldAdvance, nullptr)));
3539

36-
luaL_setmetatable(L, lua::metatables::LootListMT);
37-
memcpy(toLua, &loot, sizeof(LootList));
3840
return 1;
3941
}
4042

4143
LUA_FUNCTION(Lua_NPCGetFireplaceLoot) {
4244
auto* npc = lua::GetLuabridgeUserdata<Entity_NPC*>(L, 1, lua::Metatables::ENTITY_NPC, "EntityNPC");
4345
bool shouldAdvance = lua::luaL_optboolean(L, 2, false);
4446

45-
LootList loot = npc->fireplace_get_loot(shouldAdvance);
46-
LootList* toLua = (LootList*)lua_newuserdata(L, sizeof(LootList));
47+
LootList* toLua = (LootList*)lua_newuserdata(L, sizeof(LootList));
48+
luaL_setmetatable(L, lua::metatables::LootListMT);
49+
new (toLua) LootList(std::move(npc->fireplace_get_loot(shouldAdvance)));
4750

48-
luaL_setmetatable(L, lua::metatables::LootListMT);
49-
memcpy(toLua, &loot, sizeof(LootList));
5051
return 1;
5152
}
5253

5354
LUA_FUNCTION(Lua_NPCGetShopkeeperLoot) {
5455
auto* npc = lua::GetLuabridgeUserdata<Entity_NPC*>(L, 1, lua::Metatables::ENTITY_NPC, "EntityNPC");
5556
bool shouldAdvance = lua::luaL_optboolean(L, 2, false);
5657

57-
LootList loot = npc->shopkeeper_get_loot(shouldAdvance);
58-
LootList* toLua = (LootList*)lua_newuserdata(L, sizeof(LootList));
58+
LootList* toLua = (LootList*)lua_newuserdata(L, sizeof(LootList));
59+
luaL_setmetatable(L, lua::metatables::LootListMT);
60+
new (toLua) LootList(std::move(npc->shopkeeper_get_loot(shouldAdvance)));
5961

60-
luaL_setmetatable(L, lua::metatables::LootListMT);
61-
memcpy(toLua, &loot, sizeof(LootList));
6262
return 1;
6363
}
6464

6565
LUA_FUNCTION(Lua_LootListGetEntries)
6666
{
6767
LootList* lootList = lua::GetRawUserdata<LootList*>(L, 1, lua::metatables::LootListMT);
68-
auto& entries = *lootList; // Assuming lootList is a std::deque<LootListEntry>
6968

7069
lua_newtable(L);
7170
int idx = 1;
72-
for (LootListEntry& item : entries) {
71+
for (LootListEntry& item : *lootList) {
7372
LootListEntry** ud = (LootListEntry**)lua_newuserdata(L, sizeof(LootListEntry*));
7473
*ud = &item;
7574
luaL_setmetatable(L, lua::metatables::LootListEntryMT);
@@ -80,13 +79,12 @@ LUA_FUNCTION(Lua_LootListGetEntries)
8079
return 1;
8180
}
8281

83-
/*LUA_FUNCTION(Lua_LootList__gc) {
84-
LootList* lootlist = lua::GetRawUserdata<LootList*>(L, 1, lua::metatables::LootListMT);
85-
lootlist->~LootList();
86-
82+
LUA_FUNCTION(Lua_LootList__gc) {
83+
if (LootList* lootlist = lua::GetRawUserdata<LootList*>(L, 1, lua::metatables::LootListMT)) {
84+
lootlist->~LootList();
85+
}
8786
return 0;
8887
}
89-
*/
9088

9189
static void RegisterLootList(lua_State* L) {
9290

@@ -100,7 +98,7 @@ static void RegisterLootList(lua_State* L) {
10098
{ "PushEntry", Lua_LootListPushEntry },
10199
{ NULL, NULL }
102100
};
103-
lua::RegisterNewClass(L, lua::metatables::LootListMT, lua::metatables::LootListMT, functions);
101+
lua::RegisterNewClass(L, lua::metatables::LootListMT, lua::metatables::LootListMT, functions, Lua_LootList__gc);
104102
lua_register(L, lua::metatables::LootListMT, Lua_LootListConstructor);
105103
}
106104

0 commit comments

Comments
 (0)