dep-ygopro-core/scriptlib.cpp
Edoardo Lolletti 4ab85de875
Move Group's lifetime management to Lua (#166)
No longer manage a Group's lifetime explicitly in native code, but instead leverage Lua's garbage collector to handle it's lifetime.
In this change, Groups will be stored internally in the core inside a weak table, so that they will still be reference-able when needed, but if no reference were to exist in Lua code, they will be subject to garbage collection.
Whenever a Group is used by native code, its wrapped by a owned_lua template class, this is akin to std::shared_ptr--It reference counts the usages of that group internally in the core, including managing it in the Lua registry index when in use, so that it won't be collected.
With this approach, the exponential memory usage growth caused by long running procedures due to the creation hundredths of thousands of groups per iteration, is mitigated, since the temporary groups will be reaped more frequently, as opposed to the old approach of them being cleared only after the topmost Lua function had finished executing.
For now a single GC step is triggered whenever the total number of groups is more than 2048--an arbitrary value that seems to work well enough, but which can be changed/optimized after proper profiling.
A major upgrade with this approach is the removal of the concept of Groups "kept alive" and having to "explicitly delete" them to make them outlive a Lua function execution, since they will now follow Lua's variables lifetime properly.
2025-05-30 23:20:22 +02:00

79 lines
1.8 KiB
C++

/*
* Copyright (c) 2010-2015, Argon Sun (Fluorohydride)
* Copyright (c) 2018-2025, Edoardo Lolletti (edo9300) <edoardo762@gmail.com>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include "duel.h"
#include "field.h"
#include "lua_obj.h"
#include "scriptlib.h"
namespace scriptlib {
const char* get_lua_type_name(lua_State* L, int32_t index) {
switch(auto type = lua_type(L, index); type) {
case LUA_TFUNCTION:
return "Function";
case LUA_TSTRING:
return "String";
case LUA_TNUMBER:
return "Int";
case LUA_TBOOLEAN:
return "boolean";
case LUA_TTABLE:
return "table";
case LUA_TNIL:
case LUA_TNONE:
return "nil";
case LUA_TUSERDATA:
if(auto* obj = lua_get<lua_obj*>(L, index); obj != nullptr) {
switch(obj->lua_type) {
case LuaParam::CARD:
return "Card";
case LuaParam::GROUP:
return "Group";
case LuaParam::EFFECT:
return "Effect";
case LuaParam::DELETED:
return "Deleted";
default:
unreachable();
}
}
[[fallthrough]];
default:
return "unknown";
}
}
void check_action_permission(lua_State* L) {
if(lua_get<duel*>(L)->lua->no_action)
lua_error(L, "Action is not allowed here.");
}
int32_t push_return_cards(lua_State* L, int32_t/* status*/, lua_KContext ctx) {
const auto pduel = lua_get<duel*>(L);
bool cancelable = (bool)ctx;
if(pduel->game_field->return_cards.canceled) {
if(cancelable) {
lua_pushnil(L);
} else {
auto pgroup = pduel->new_group();
interpreter::pushobject(L, pgroup);
}
} else {
auto pgroup = pduel->new_group(pduel->game_field->return_cards.list);
interpreter::pushobject(L, pgroup);
}
return 1;
}
int32_t is_deleted_object(lua_State* L) {
if(auto obj = lua_touserdata(L, 1)) {
auto* ret = *static_cast<lua_obj**>(obj);
lua_pushboolean(L, ret->lua_type == LuaParam::DELETED);
} else {
lua_pushboolean(L, false);
}
return 1;
}
}