2015-09-25 21:07:16 +08:00
|
|
|
/*
|
2024-01-03 01:21:08 +01:00
|
|
|
* Copyright (c) 2010-2015, Argon Sun (Fluorohydride)
|
2025-04-25 11:00:10 +02:00
|
|
|
* Copyright (c) 2017-2025, Edoardo Lolletti (edo9300) <edoardo762@gmail.com>
|
2015-09-25 21:07:16 +08:00
|
|
|
*
|
2024-01-03 01:21:08 +01:00
|
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
2015-09-25 21:07:16 +08:00
|
|
|
*/
|
2024-01-05 12:17:47 +01:00
|
|
|
#include <algorithm> //std::find, std::remove, std::includes, std::set_intersection
|
|
|
|
|
#include <iterator> //std::advance, std::inserter
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <tuple>
|
|
|
|
|
#include <utility> //std::move, std::swap
|
|
|
|
|
#include "bit.h"
|
2015-09-25 21:07:16 +08:00
|
|
|
#include "card.h"
|
|
|
|
|
#include "duel.h"
|
2024-01-05 12:17:47 +01:00
|
|
|
#include "field.h"
|
|
|
|
|
#include "group.h"
|
|
|
|
|
#include "scriptlib.h"
|
2015-09-25 21:07:16 +08:00
|
|
|
|
2022-05-14 19:55:59 +02:00
|
|
|
#define LUA_MODULE Group
|
2025-07-05 15:03:10 +02:00
|
|
|
#define LUA_CLASS group
|
2022-05-14 19:55:59 +02:00
|
|
|
#include "function_array_helper.h"
|
|
|
|
|
|
2021-10-13 17:29:29 +02:00
|
|
|
namespace {
|
2025-07-05 19:50:36 +02:00
|
|
|
namespace LUA_NAMESPACE {
|
2021-10-13 17:29:29 +02:00
|
|
|
|
|
|
|
|
using namespace scriptlib;
|
|
|
|
|
|
|
|
|
|
void assert_readonly_group(lua_State* L, group* pgroup) {
|
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
|
|
|
if(!pgroup->is_readonly)
|
2021-09-04 20:29:09 +02:00
|
|
|
return;
|
2022-06-23 11:47:00 +02:00
|
|
|
lua_error(L, "attempt to modify a read only group");
|
2021-08-11 14:24:03 +02:00
|
|
|
}
|
|
|
|
|
|
2023-11-21 11:56:23 +01:00
|
|
|
LUA_STATIC_FUNCTION(CreateGroup) {
|
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
|
|
|
auto pgroup = pduel->new_group();
|
2022-10-27 12:49:36 +02:00
|
|
|
lua_iterate_table_or_stack(L, 1, lua_gettop(L), [L, &container = pgroup->container] {
|
2022-10-26 18:13:11 +02:00
|
|
|
if(!lua_isnil(L, -1)) {
|
|
|
|
|
auto pcard = lua_get<card*, true>(L, -1);
|
|
|
|
|
container.insert(pcard);
|
|
|
|
|
}
|
|
|
|
|
});
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, pgroup);
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-10-19 15:07:05 +02:00
|
|
|
LUA_FUNCTION_ALIAS(FromCards);
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Clone) {
|
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
|
|
|
auto newgroup = pduel->new_group(self);
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, newgroup);
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(DeleteGroup) {
|
2015-09-25 21:07:16 +08:00
|
|
|
return 0;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(KeepAlive) {
|
2023-11-21 11:56:23 +01:00
|
|
|
interpreter::pushobject(L, self);
|
2023-02-05 11:23:31 +01:00
|
|
|
return 1;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Clear) {
|
2023-11-21 11:56:23 +01:00
|
|
|
assert_readonly_group(L, self);
|
|
|
|
|
self->is_iterator_dirty = true;
|
|
|
|
|
self->container.clear();
|
|
|
|
|
interpreter::pushobject(L, self);
|
2023-02-05 11:23:31 +01:00
|
|
|
return 1;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(AddCard) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
assert_readonly_group(L, self);
|
|
|
|
|
self->is_iterator_dirty = true;
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pcard, pgroup] = lua_get_card_or_group(L, 2); pcard)
|
2023-11-21 11:56:23 +01:00
|
|
|
self->container.insert(pcard);
|
2021-08-11 14:24:03 +02:00
|
|
|
else
|
2023-11-26 15:11:50 +01:00
|
|
|
self->container.insert(pgroup->container.begin(), pgroup->container.end());
|
2023-11-21 11:56:23 +01:00
|
|
|
interpreter::pushobject(L, self);
|
2021-08-11 14:24:03 +02:00
|
|
|
return 1;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION_ALIAS(Merge);
|
|
|
|
|
LUA_FUNCTION(RemoveCard) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
assert_readonly_group(L, self);
|
|
|
|
|
self->is_iterator_dirty = true;
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pcard, pgroup] = lua_get_card_or_group(L, 2); pcard)
|
2023-11-21 11:56:23 +01:00
|
|
|
self->container.erase(pcard);
|
2021-08-11 14:24:03 +02:00
|
|
|
else {
|
2023-11-26 15:11:50 +01:00
|
|
|
if(self == pgroup)
|
2022-08-10 16:05:20 +02:00
|
|
|
lua_error(L, "Attempting to remove a group from itself");
|
2023-11-26 15:11:50 +01:00
|
|
|
for(auto& _pcard : pgroup->container)
|
2023-11-21 11:56:23 +01:00
|
|
|
self->container.erase(_pcard);
|
2021-08-11 14:24:03 +02:00
|
|
|
}
|
2023-11-21 11:56:23 +01:00
|
|
|
interpreter::pushobject(L, self);
|
2021-08-05 16:53:23 +02:00
|
|
|
return 1;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION_ALIAS(Sub);
|
|
|
|
|
LUA_FUNCTION(GetNext) {
|
2023-11-21 11:56:23 +01:00
|
|
|
if(self->is_iterator_dirty)
|
2022-06-23 11:47:00 +02:00
|
|
|
lua_error(L, "Called Group.GetNext without first calling Group.GetFirst");
|
2023-12-02 12:04:35 +01:00
|
|
|
if(self->it == self->container.end() || (++self->it) == self->container.end())
|
2015-09-25 21:07:16 +08:00
|
|
|
lua_pushnil(L);
|
2023-12-02 12:04:35 +01:00
|
|
|
else
|
|
|
|
|
interpreter::pushobject(L, *self->it);
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetFirst) {
|
2023-11-21 11:56:23 +01:00
|
|
|
self->is_iterator_dirty = false;
|
2023-12-02 12:04:35 +01:00
|
|
|
if(self->it = self->container.begin(); self->it != self->container.end())
|
|
|
|
|
interpreter::pushobject(L, *self->it);
|
2020-07-20 19:44:59 +02:00
|
|
|
else
|
2015-09-25 21:07:16 +08:00
|
|
|
lua_pushnil(L);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(TakeatPos) {
|
2017-09-23 19:29:58 +02:00
|
|
|
check_param_count(L, 2);
|
2020-07-20 19:44:59 +02:00
|
|
|
auto pos = lua_get<size_t>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
if(pos >= self->container.size())
|
2017-09-23 19:29:58 +02:00
|
|
|
lua_pushnil(L);
|
|
|
|
|
else {
|
2023-11-21 11:56:23 +01:00
|
|
|
auto cit = self->container.begin();
|
2017-09-23 19:29:58 +02:00
|
|
|
std::advance(cit, pos);
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, *cit);
|
2017-09-23 19:29:58 +02:00
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetCount) {
|
2023-11-21 11:56:23 +01:00
|
|
|
lua_pushinteger(L, self->container.size());
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Filter) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 3);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
card_set cset(self->container);
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, 3); pexception) {
|
2017-02-08 21:16:32 +09:00
|
|
|
cset.erase(pexception);
|
2023-12-03 10:27:47 +01:00
|
|
|
} else if(pexgroup) {
|
2018-09-03 16:36:45 +08:00
|
|
|
for(auto& pcard : pexgroup->container)
|
|
|
|
|
cset.erase(pcard);
|
2017-02-08 21:16:32 +09:00
|
|
|
}
|
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
|
|
|
auto new_group = pduel->new_group();
|
2021-09-04 22:04:14 +02:00
|
|
|
uint32_t extraargs = lua_gettop(L) - 3;
|
2018-09-03 16:36:45 +08:00
|
|
|
for(auto& pcard : cset) {
|
2022-06-11 12:59:38 +02:00
|
|
|
if(pduel->lua->check_matching(pcard, findex, extraargs)) {
|
2018-09-03 16:36:45 +08:00
|
|
|
new_group->container.insert(pcard);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, new_group);
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Match) {
|
2021-08-14 23:38:25 +02:00
|
|
|
check_param_count(L, 3);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
assert_readonly_group(L, self);
|
|
|
|
|
self->is_iterator_dirty = true;
|
2021-09-04 22:04:14 +02:00
|
|
|
uint32_t extraargs = lua_gettop(L) - 3;
|
2023-11-21 11:56:23 +01:00
|
|
|
auto& cset = self->container;
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, 3); pexception) {
|
2022-05-15 16:48:51 +02:00
|
|
|
for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) {
|
|
|
|
|
auto rm = cit++;
|
|
|
|
|
auto* pcard = *rm;
|
2022-06-11 12:59:38 +02:00
|
|
|
if(pcard == pexception || !pduel->lua->check_matching(pcard, findex, extraargs))
|
2022-05-15 16:48:51 +02:00
|
|
|
cset.erase(rm);
|
|
|
|
|
}
|
|
|
|
|
} else if(pexgroup) {
|
2023-12-03 10:27:47 +01:00
|
|
|
auto should_remove = [pexbegin = pexgroup->container.cbegin(), pexend = pexgroup->container.cend()](card* pcard) mutable {
|
2022-05-15 16:48:51 +02:00
|
|
|
if(pexbegin == pexend)
|
|
|
|
|
return false;
|
|
|
|
|
if(*pexbegin == pcard) {
|
|
|
|
|
++pexbegin;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) {
|
|
|
|
|
auto rm = cit++;
|
|
|
|
|
auto* pcard = *rm;
|
2022-06-11 12:59:38 +02:00
|
|
|
if(should_remove(pcard) || !pduel->lua->check_matching(pcard, findex, extraargs))
|
2022-05-15 16:48:51 +02:00
|
|
|
cset.erase(rm);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) {
|
|
|
|
|
auto rm = cit++;
|
|
|
|
|
auto* pcard = *rm;
|
2022-06-11 12:59:38 +02:00
|
|
|
if(!pduel->lua->check_matching(pcard, findex, extraargs))
|
2022-05-15 16:48:51 +02:00
|
|
|
cset.erase(rm);
|
|
|
|
|
}
|
2021-08-14 23:38:25 +02:00
|
|
|
}
|
2023-11-21 11:56:23 +01:00
|
|
|
interpreter::pushobject(L, self);
|
2021-08-14 23:38:25 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(FilterCount) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 3);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
card_set cset(self->container);
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, 3); pexception) {
|
2022-09-29 19:46:15 +02:00
|
|
|
cset.erase(pexception);
|
2023-12-03 10:27:47 +01:00
|
|
|
} else if(pexgroup) {
|
2022-09-29 19:46:15 +02:00
|
|
|
for(auto& pcard : pexgroup->container)
|
|
|
|
|
cset.erase(pcard);
|
|
|
|
|
}
|
2021-09-04 22:04:14 +02:00
|
|
|
uint32_t extraargs = lua_gettop(L) - 3;
|
|
|
|
|
uint32_t count = 0;
|
2022-09-29 19:46:15 +02:00
|
|
|
for (auto& pcard : cset) {
|
|
|
|
|
if(pduel->lua->check_matching(pcard, findex, extraargs))
|
|
|
|
|
++count;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, count);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(FilterSelect) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_action_permission(L);
|
|
|
|
|
check_param_count(L, 6);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 3);
|
2023-11-21 11:56:23 +01:00
|
|
|
card_set cset(self->container);
|
2020-07-23 23:50:09 +02:00
|
|
|
bool cancelable = false;
|
2020-03-14 18:40:09 +01:00
|
|
|
uint8_t lastarg = 6;
|
2020-07-24 00:16:34 +02:00
|
|
|
if(lua_isboolean(L, lastarg)) {
|
2020-07-23 23:50:09 +02:00
|
|
|
cancelable = lua_get<bool, false>(L, lastarg);
|
2022-03-05 13:56:43 +01:00
|
|
|
++lastarg;
|
2020-03-14 18:40:09 +01:00
|
|
|
}
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, lastarg); pexception) {
|
2017-02-08 21:16:32 +09:00
|
|
|
cset.erase(pexception);
|
2023-12-03 10:27:47 +01:00
|
|
|
} else if(pexgroup) {
|
2018-09-03 16:36:45 +08:00
|
|
|
for(auto& pcard : pexgroup->container)
|
|
|
|
|
cset.erase(pcard);
|
2017-02-08 21:16:32 +09:00
|
|
|
}
|
2021-09-04 22:04:14 +02:00
|
|
|
auto playerid = lua_get<uint8_t>(L, 2);
|
2015-09-25 21:07:16 +08:00
|
|
|
if(playerid != 0 && playerid != 1)
|
|
|
|
|
return 0;
|
2021-09-04 22:04:14 +02:00
|
|
|
auto min = lua_get<uint16_t>(L, 4);
|
|
|
|
|
auto max = lua_get<uint16_t>(L, 5);
|
|
|
|
|
uint32_t extraargs = lua_gettop(L) - lastarg;
|
2015-09-25 21:07:16 +08:00
|
|
|
pduel->game_field->core.select_cards.clear();
|
2021-10-13 17:29:29 +02:00
|
|
|
for(auto& pcard : cset) {
|
2022-06-11 12:59:38 +02:00
|
|
|
if(pduel->lua->check_matching(pcard, findex, extraargs))
|
2018-09-03 16:36:45 +08:00
|
|
|
pduel->game_field->core.select_cards.push_back(pcard);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2024-01-08 20:46:41 +01:00
|
|
|
pduel->game_field->emplace_process<Processors::SelectCard>(playerid, cancelable, min, max);
|
2023-11-25 11:15:21 +01:00
|
|
|
return push_return_cards(L, cancelable);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Select) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_action_permission(L);
|
|
|
|
|
check_param_count(L, 5);
|
2023-11-21 11:56:23 +01:00
|
|
|
card_set cset(self->container);
|
2020-07-23 23:50:09 +02:00
|
|
|
bool cancelable = false;
|
2020-03-14 18:40:09 +01:00
|
|
|
uint8_t lastarg = 5;
|
2020-07-24 00:16:34 +02:00
|
|
|
if(lua_isboolean(L, lastarg)) {
|
2020-07-23 23:50:09 +02:00
|
|
|
cancelable = lua_get<bool, false>(L, lastarg);
|
2022-03-05 13:56:43 +01:00
|
|
|
++lastarg;
|
2020-03-14 18:40:09 +01:00
|
|
|
}
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, lastarg); pexception) {
|
2017-02-08 21:16:32 +09:00
|
|
|
cset.erase(pexception);
|
2023-12-03 10:27:47 +01:00
|
|
|
} else if(pexgroup) {
|
2018-09-03 16:36:45 +08:00
|
|
|
for(auto& pcard : pexgroup->container)
|
|
|
|
|
cset.erase(pcard);
|
2017-02-08 21:16:32 +09:00
|
|
|
}
|
2021-09-04 22:04:14 +02:00
|
|
|
auto playerid = lua_get<uint8_t>(L, 2);
|
2015-09-25 21:07:16 +08:00
|
|
|
if(playerid != 0 && playerid != 1)
|
|
|
|
|
return 0;
|
2021-09-04 22:04:14 +02:00
|
|
|
auto min = lua_get<uint16_t>(L, 3);
|
|
|
|
|
auto max = lua_get<uint16_t>(L, 4);
|
2020-07-20 19:44:59 +02:00
|
|
|
pduel->game_field->core.select_cards.assign(cset.begin(), cset.end());
|
2024-01-08 20:46:41 +01:00
|
|
|
pduel->game_field->emplace_process<Processors::SelectCard>(playerid, cancelable, min, max);
|
2023-11-25 11:15:21 +01:00
|
|
|
return push_return_cards(L, cancelable);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(SelectUnselect) {
|
2017-02-06 23:16:54 +01:00
|
|
|
check_action_permission(L);
|
|
|
|
|
check_param_count(L, 3);
|
2022-05-15 17:05:45 +02:00
|
|
|
auto pgroup2 = lua_get<group*>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
auto playerid = lua_get<uint8_t>(L, 3);
|
2017-02-06 23:16:54 +01:00
|
|
|
if(playerid != 0 && playerid != 1)
|
|
|
|
|
return 0;
|
2022-05-15 17:05:45 +02:00
|
|
|
if(pgroup2) {
|
2023-11-21 11:56:23 +01:00
|
|
|
auto first1 = self->container.begin();
|
|
|
|
|
auto last1 = self->container.end();
|
2022-05-15 16:47:13 +02:00
|
|
|
auto first2 = pgroup2->container.begin();
|
|
|
|
|
auto last2 = pgroup2->container.end();
|
|
|
|
|
while(first1 != last1 && first2 != last2) {
|
|
|
|
|
if((*first1)->cardid < (*first2)->cardid) {
|
|
|
|
|
++first1;
|
|
|
|
|
} else {
|
|
|
|
|
if(!((*first2)->cardid < (*first1)->cardid)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
++first2;
|
|
|
|
|
}
|
2017-02-06 23:16:54 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-23 23:50:09 +02:00
|
|
|
bool finishable = lua_get<bool, false>(L, 4);
|
|
|
|
|
bool cancelable = lua_get<bool, false>(L, 5);
|
2022-05-15 17:05:45 +02:00
|
|
|
uint16_t min = lua_get<uint16_t, 1>(L, 6);
|
|
|
|
|
uint16_t max = lua_get<uint16_t, 1>(L, 7);
|
2018-04-05 14:41:14 +02:00
|
|
|
if(min > max)
|
2017-02-06 23:16:54 +01:00
|
|
|
min = max;
|
2023-11-21 11:56:23 +01:00
|
|
|
pduel->game_field->core.select_cards.assign(self->container.begin(), self->container.end());
|
2022-05-15 17:05:45 +02:00
|
|
|
if(pgroup2)
|
|
|
|
|
pduel->game_field->core.unselect_cards.assign(pgroup2->container.begin(), pgroup2->container.end());
|
|
|
|
|
else
|
|
|
|
|
pduel->game_field->core.unselect_cards.clear();
|
2024-01-08 20:46:41 +01:00
|
|
|
pduel->game_field->emplace_process<Processors::SelectUnselectCard>(playerid, cancelable, min, max, finishable);
|
2023-11-25 11:15:21 +01:00
|
|
|
return yieldk({
|
2021-04-19 12:24:48 +02:00
|
|
|
if(pduel->game_field->return_cards.canceled)
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
else
|
|
|
|
|
interpreter::pushobject(L, pduel->game_field->return_cards.list[0]);
|
|
|
|
|
return 1;
|
|
|
|
|
});
|
2017-02-06 23:16:54 +01:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(RandomSelect) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 3);
|
2021-09-04 22:04:14 +02:00
|
|
|
auto playerid = lua_get<uint8_t>(L, 2);
|
|
|
|
|
size_t count = lua_get<uint32_t>(L, 3);
|
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
|
|
|
auto newgroup = pduel->new_group();
|
2023-11-21 11:56:23 +01:00
|
|
|
if(count > self->container.size())
|
|
|
|
|
count = self->container.size();
|
2019-06-23 14:15:19 +08:00
|
|
|
if(count == 0) {
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, newgroup);
|
2019-06-23 14:15:19 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2023-11-21 11:56:23 +01:00
|
|
|
if(count == self->container.size())
|
|
|
|
|
newgroup->container = self->container;
|
2019-06-23 14:15:19 +08:00
|
|
|
else {
|
2020-10-03 18:07:30 +02:00
|
|
|
while(newgroup->container.size() < count) {
|
2023-11-21 11:56:23 +01:00
|
|
|
int32_t i = pduel->get_next_integer(0, (int32_t)self->container.size() - 1);
|
|
|
|
|
auto cit = self->container.begin();
|
2020-10-03 18:07:30 +02:00
|
|
|
std::advance(cit, i);
|
2019-06-23 14:15:19 +08:00
|
|
|
newgroup->container.insert(*cit);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-16 00:57:53 +02:00
|
|
|
auto message = pduel->new_message(MSG_RANDOM_SELECTED);
|
2021-09-04 22:04:14 +02:00
|
|
|
message->write<uint8_t>(playerid);
|
|
|
|
|
message->write<uint32_t>(count);
|
2019-06-23 14:15:19 +08:00
|
|
|
for(auto& pcard : newgroup->container) {
|
2019-07-16 00:57:53 +02:00
|
|
|
message->write(pcard->get_info_location());
|
2019-06-23 14:15:19 +08:00
|
|
|
}
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, newgroup);
|
2019-06-23 14:15:19 +08:00
|
|
|
return 1;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(IsExists) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 4);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
card_set cset(self->container);
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, 4); pexception) {
|
2017-02-08 21:16:32 +09:00
|
|
|
cset.erase(pexception);
|
2023-12-03 10:27:47 +01:00
|
|
|
} else if(pexgroup) {
|
2018-09-03 16:36:45 +08:00
|
|
|
for(auto& pcard : pexgroup->container)
|
|
|
|
|
cset.erase(pcard);
|
2017-02-08 21:16:32 +09:00
|
|
|
}
|
2021-09-04 22:04:14 +02:00
|
|
|
auto count = lua_get<uint16_t>(L, 3);
|
|
|
|
|
uint32_t extraargs = lua_gettop(L) - 4;
|
|
|
|
|
uint32_t fcount = 0;
|
2021-10-13 17:29:29 +02:00
|
|
|
for(auto& pcard : cset) {
|
2022-06-11 12:59:38 +02:00
|
|
|
if(pduel->lua->check_matching(pcard, findex, extraargs)) {
|
2022-03-05 13:56:43 +01:00
|
|
|
++fcount;
|
2020-07-20 19:44:59 +02:00
|
|
|
if(fcount >= count)
|
2015-09-25 21:07:16 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-20 19:44:59 +02:00
|
|
|
lua_pushboolean(L, fcount >= count);
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(CheckWithSumEqual) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 5);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
auto acc = lua_get<uint32_t>(L, 3);
|
|
|
|
|
auto min = lua_get<int32_t>(L, 4);
|
|
|
|
|
auto max = lua_get<int32_t>(L, 5);
|
2016-03-18 00:48:52 +09:00
|
|
|
if(min < 0)
|
|
|
|
|
min = 0;
|
2015-09-25 21:07:16 +08:00
|
|
|
if(max < min)
|
|
|
|
|
max = min;
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 5;
|
2021-09-04 20:46:43 +02:00
|
|
|
card_vector cv(pduel->game_field->core.must_select_cards);
|
2023-02-07 16:43:27 +01:00
|
|
|
int32_t mcount = static_cast<int32_t>(cv.size());
|
2021-12-04 15:51:42 +01:00
|
|
|
const auto beginit = pduel->game_field->core.must_select_cards.begin();
|
|
|
|
|
const auto endit = pduel->game_field->core.must_select_cards.end();
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container) {
|
2021-12-04 15:51:42 +01:00
|
|
|
if(std::find(beginit, endit, pcard) == endit)
|
2018-09-03 16:36:45 +08:00
|
|
|
cv.push_back(pcard);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2016-03-18 00:48:52 +09:00
|
|
|
pduel->game_field->core.must_select_cards.clear();
|
2025-04-25 11:00:10 +02:00
|
|
|
for(auto& pcard : cv) {
|
|
|
|
|
if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) {
|
|
|
|
|
lua_error(L, "Group contains a card for which the value function returned 0.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t should_continue = TRUE;
|
2019-12-10 19:04:45 +01:00
|
|
|
lua_pushboolean(L, field::check_with_sum_limit_m(cv, acc, 0, min, max, mcount, &should_continue));
|
|
|
|
|
lua_pushboolean(L, should_continue);
|
|
|
|
|
return 2;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(SelectWithSumEqual) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_action_permission(L);
|
|
|
|
|
check_param_count(L, 6);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 3);
|
2021-09-04 22:04:14 +02:00
|
|
|
auto playerid = lua_get<uint8_t>(L, 2);
|
2015-09-25 21:07:16 +08:00
|
|
|
if(playerid != 0 && playerid != 1)
|
|
|
|
|
return 0;
|
2021-09-04 22:04:14 +02:00
|
|
|
auto acc = lua_get<uint32_t>(L, 4);
|
|
|
|
|
auto min = lua_get<int32_t>(L, 5);
|
|
|
|
|
auto max = lua_get<int32_t>(L, 6);
|
2016-03-18 00:48:52 +09:00
|
|
|
if(min < 0)
|
|
|
|
|
min = 0;
|
2015-09-25 21:07:16 +08:00
|
|
|
if(max < min)
|
|
|
|
|
max = min;
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 6;
|
2023-11-21 11:56:23 +01:00
|
|
|
pduel->game_field->core.select_cards.assign(self->container.begin(), self->container.end());
|
2018-09-03 16:36:45 +08:00
|
|
|
for(auto& pcard : pduel->game_field->core.must_select_cards) {
|
|
|
|
|
auto it = std::remove(pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end(), pcard);
|
2015-12-19 09:56:22 +09:00
|
|
|
pduel->game_field->core.select_cards.erase(it, pduel->game_field->core.select_cards.end());
|
|
|
|
|
}
|
2021-09-04 20:46:43 +02:00
|
|
|
card_vector cv(pduel->game_field->core.must_select_cards);
|
2023-02-07 16:43:27 +01:00
|
|
|
int32_t mcount = static_cast<int32_t>(cv.size());
|
2015-12-19 09:56:22 +09:00
|
|
|
cv.insert(cv.end(), pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end());
|
2025-04-25 11:00:10 +02:00
|
|
|
for(auto& pcard : cv) {
|
|
|
|
|
if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) {
|
|
|
|
|
lua_error(L, "Group contains a card for which the value function returned 0.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-10 19:04:45 +01:00
|
|
|
if(!field::check_with_sum_limit_m(cv, acc, 0, min, max, mcount, nullptr)) {
|
2016-03-18 00:48:52 +09:00
|
|
|
pduel->game_field->core.must_select_cards.clear();
|
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
|
|
|
auto empty_group = pduel->new_group();
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, empty_group);
|
2016-12-06 19:07:43 +08:00
|
|
|
return 1;
|
2016-03-18 00:48:52 +09:00
|
|
|
}
|
2024-01-08 20:46:41 +01:00
|
|
|
pduel->game_field->emplace_process<Processors::SelectSum>(playerid, acc, min, max);
|
2023-11-25 11:15:21 +01:00
|
|
|
return yieldk({
|
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
|
|
|
auto pgroup = pduel->new_group(pduel->game_field->return_cards.list);
|
2019-06-23 14:15:19 +08:00
|
|
|
pduel->game_field->core.must_select_cards.clear();
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, pgroup);
|
2019-06-23 14:15:19 +08:00
|
|
|
return 1;
|
2021-12-04 15:51:42 +01:00
|
|
|
});
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(CheckWithSumGreater) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 3);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
auto acc = lua_get<uint32_t>(L, 3);
|
|
|
|
|
int32_t extraargs = lua_gettop(L) - 3;
|
2021-09-04 20:46:43 +02:00
|
|
|
card_vector cv(pduel->game_field->core.must_select_cards);
|
2023-02-07 16:43:27 +01:00
|
|
|
int32_t mcount = static_cast<int32_t>(cv.size());
|
2021-12-04 15:51:42 +01:00
|
|
|
const auto beginit = pduel->game_field->core.must_select_cards.begin();
|
|
|
|
|
const auto endit = pduel->game_field->core.must_select_cards.end();
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container) {
|
2021-12-04 15:51:42 +01:00
|
|
|
if(std::find(beginit, endit, pcard) == endit)
|
2018-09-03 16:36:45 +08:00
|
|
|
cv.push_back(pcard);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2016-03-18 00:48:52 +09:00
|
|
|
pduel->game_field->core.must_select_cards.clear();
|
2025-04-25 11:00:10 +02:00
|
|
|
for(auto& pcard : cv) {
|
|
|
|
|
if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) {
|
|
|
|
|
lua_error(L, "Group contains a card for which the value function returned 0.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t should_continue = TRUE;
|
2019-12-10 19:04:45 +01:00
|
|
|
lua_pushboolean(L, field::check_with_sum_greater_limit_m(cv, acc, 0, 0xffff, mcount, &should_continue));
|
|
|
|
|
lua_pushboolean(L, should_continue);
|
|
|
|
|
return 2;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(SelectWithSumGreater) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_action_permission(L);
|
|
|
|
|
check_param_count(L, 4);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 3);
|
2021-09-04 22:04:14 +02:00
|
|
|
auto playerid = lua_get<uint8_t>(L, 2);
|
2015-09-25 21:07:16 +08:00
|
|
|
if(playerid != 0 && playerid != 1)
|
|
|
|
|
return 0;
|
2021-09-04 22:04:14 +02:00
|
|
|
auto acc = lua_get<uint32_t>(L, 4);
|
|
|
|
|
int32_t extraargs = lua_gettop(L) - 4;
|
2023-11-21 11:56:23 +01:00
|
|
|
pduel->game_field->core.select_cards.assign(self->container.begin(), self->container.end());
|
2018-09-03 16:36:45 +08:00
|
|
|
for(auto& pcard : pduel->game_field->core.must_select_cards) {
|
|
|
|
|
auto it = std::remove(pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end(), pcard);
|
2016-03-18 00:48:52 +09:00
|
|
|
pduel->game_field->core.select_cards.erase(it, pduel->game_field->core.select_cards.end());
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2021-09-04 20:46:43 +02:00
|
|
|
card_vector cv(pduel->game_field->core.must_select_cards);
|
2023-02-07 16:43:27 +01:00
|
|
|
int32_t mcount = static_cast<int32_t>(cv.size());
|
2016-03-18 00:48:52 +09:00
|
|
|
cv.insert(cv.end(), pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end());
|
2025-04-25 11:00:10 +02:00
|
|
|
for(auto& pcard : cv) {
|
|
|
|
|
if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) {
|
|
|
|
|
lua_error(L, "Group contains a card for which the value function returned 0.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-10 19:04:45 +01:00
|
|
|
if(!field::check_with_sum_greater_limit_m(cv, acc, 0, 0xffff, mcount, nullptr)) {
|
2016-03-18 00:48:52 +09:00
|
|
|
pduel->game_field->core.must_select_cards.clear();
|
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
|
|
|
auto empty_group = pduel->new_group();
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, empty_group);
|
2016-12-06 19:07:43 +08:00
|
|
|
return 1;
|
2016-03-18 00:48:52 +09:00
|
|
|
}
|
2024-01-08 20:46:41 +01:00
|
|
|
pduel->game_field->emplace_process<Processors::SelectSum>(playerid, acc, 0, 0);
|
2023-11-25 11:15:21 +01:00
|
|
|
return yieldk({
|
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
|
|
|
auto pgroup = pduel->new_group(pduel->game_field->return_cards.list);
|
2019-06-23 14:15:19 +08:00
|
|
|
pduel->game_field->core.must_select_cards.clear();
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, pgroup);
|
2019-06-23 14:15:19 +08:00
|
|
|
return 1;
|
2022-05-14 19:55:59 +02:00
|
|
|
});
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetMinGroup) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
if(self->container.size() == 0)
|
2015-09-25 21:07:16 +08:00
|
|
|
return 0;
|
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
|
|
|
auto newgroup = pduel->new_group();
|
2022-08-27 16:59:47 +02:00
|
|
|
int64_t min, op;
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
2023-11-21 11:56:23 +01:00
|
|
|
auto cit = self->container.begin();
|
2022-06-11 12:59:38 +02:00
|
|
|
min = pduel->lua->get_operation_value(*cit, findex, extraargs);
|
2015-09-25 21:07:16 +08:00
|
|
|
newgroup->container.insert(*cit);
|
|
|
|
|
++cit;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(; cit != self->container.end(); ++cit) {
|
2022-06-11 12:59:38 +02:00
|
|
|
op = pduel->lua->get_operation_value(*cit, findex, extraargs);
|
2015-09-25 21:07:16 +08:00
|
|
|
if(op == min)
|
|
|
|
|
newgroup->container.insert(*cit);
|
|
|
|
|
else if(op < min) {
|
|
|
|
|
newgroup->container.clear();
|
|
|
|
|
newgroup->container.insert(*cit);
|
|
|
|
|
min = op;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, newgroup);
|
2015-09-25 21:07:16 +08:00
|
|
|
lua_pushinteger(L, min);
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetMaxGroup) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
if(self->container.size() == 0)
|
2015-09-25 21:07:16 +08:00
|
|
|
return 0;
|
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
|
|
|
auto newgroup = pduel->new_group();
|
2022-08-27 16:59:47 +02:00
|
|
|
int64_t max, op;
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
2023-11-21 11:56:23 +01:00
|
|
|
auto cit = self->container.begin();
|
2022-06-11 12:59:38 +02:00
|
|
|
max = pduel->lua->get_operation_value(*cit, findex, extraargs);
|
2015-09-25 21:07:16 +08:00
|
|
|
newgroup->container.insert(*cit);
|
|
|
|
|
++cit;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(; cit != self->container.end(); ++cit) {
|
2022-06-11 12:59:38 +02:00
|
|
|
op = pduel->lua->get_operation_value(*cit, findex, extraargs);
|
2015-09-25 21:07:16 +08:00
|
|
|
if(op == max)
|
|
|
|
|
newgroup->container.insert(*cit);
|
|
|
|
|
else if(op > max) {
|
|
|
|
|
newgroup->container.clear();
|
|
|
|
|
newgroup->container.insert(*cit);
|
|
|
|
|
max = op;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, newgroup);
|
2015-09-25 21:07:16 +08:00
|
|
|
lua_pushinteger(L, max);
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetSum) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
2022-08-27 16:59:47 +02:00
|
|
|
int64_t sum = 0;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container) {
|
2022-06-11 12:59:38 +02:00
|
|
|
sum += pduel->lua->get_operation_value(pcard, findex, extraargs);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, sum);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-15 16:08:07 +02:00
|
|
|
LUA_FUNCTION(GetBitwiseAnd) {
|
|
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2022-05-15 16:08:07 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
|
|
|
|
uint64_t total = 0;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container) {
|
2022-06-11 12:59:38 +02:00
|
|
|
total &= static_cast<uint64_t>(pduel->lua->get_operation_value(pcard, findex, extraargs));
|
2022-05-15 16:08:07 +02:00
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, total);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
LUA_FUNCTION(GetBitwiseOr) {
|
|
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2022-05-15 16:08:07 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
|
|
|
|
uint64_t total = 0;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container) {
|
2022-06-11 12:59:38 +02:00
|
|
|
total |= static_cast<uint64_t>(pduel->lua->get_operation_value(pcard, findex, extraargs));
|
2022-05-15 16:08:07 +02:00
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, total);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetClass) {
|
2020-02-12 19:41:43 +01:00
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
2022-08-27 16:59:47 +02:00
|
|
|
std::set<int64_t> er;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container) {
|
2022-06-11 12:59:38 +02:00
|
|
|
er.insert(pduel->lua->get_operation_value(pcard, findex, extraargs));
|
2020-02-12 19:41:43 +01:00
|
|
|
}
|
2024-04-15 22:39:25 +02:00
|
|
|
lua_createtable(L, static_cast<int>(er.size()), 0);
|
2020-02-12 19:41:43 +01:00
|
|
|
int i = 1;
|
|
|
|
|
for(auto& val : er) {
|
2020-07-23 23:50:09 +02:00
|
|
|
lua_pushinteger(L, i++);
|
2020-02-12 19:41:43 +01:00
|
|
|
lua_pushinteger(L, val);
|
|
|
|
|
lua_settable(L, -3);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetClassCount) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
2022-08-27 16:59:47 +02:00
|
|
|
std::set<int64_t> er;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container) {
|
2022-06-11 12:59:38 +02:00
|
|
|
er.insert(pduel->lua->get_operation_value(pcard, findex, extraargs));
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, er.size());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Remove) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 3);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
assert_readonly_group(L, self);
|
|
|
|
|
self->is_iterator_dirty = true;
|
2021-09-04 22:04:14 +02:00
|
|
|
uint32_t extraargs = lua_gettop(L) - 3;
|
2023-12-03 10:26:44 +01:00
|
|
|
auto& cset = self->container;
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, 3); pexception) {
|
2023-12-03 10:26:44 +01:00
|
|
|
for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) {
|
|
|
|
|
auto rm = cit++;
|
|
|
|
|
auto* pcard = *rm;
|
|
|
|
|
if(pcard != pexception && pduel->lua->check_matching(pcard, findex, extraargs))
|
|
|
|
|
cset.erase(rm);
|
|
|
|
|
}
|
|
|
|
|
} else if(pexgroup) {
|
|
|
|
|
auto should_keep = [pexbegin = pexgroup->container.cbegin(), pexend = pexgroup->container.cend()](card* pcard) mutable {
|
|
|
|
|
if(pexbegin == pexend)
|
|
|
|
|
return false;
|
|
|
|
|
if(*pexbegin == pcard) {
|
|
|
|
|
++pexbegin;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) {
|
|
|
|
|
auto rm = cit++;
|
|
|
|
|
auto* pcard = *rm;
|
|
|
|
|
if(!should_keep(pcard) && pduel->lua->check_matching(pcard, findex, extraargs))
|
|
|
|
|
cset.erase(rm);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) {
|
|
|
|
|
auto rm = cit++;
|
|
|
|
|
auto* pcard = *rm;
|
|
|
|
|
if(pduel->lua->check_matching(pcard, findex, extraargs))
|
|
|
|
|
cset.erase(rm);
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
|
|
|
|
}
|
2023-11-21 11:56:23 +01:00
|
|
|
interpreter::pushobject(L, self);
|
2021-08-10 16:05:36 +02:00
|
|
|
return 1;
|
2015-09-25 21:07:16 +08:00
|
|
|
}
|
2023-12-03 11:19:22 +01:00
|
|
|
std::tuple<group*, group*, card*> get_binary_op_group_card_parameters(lua_State* L) {
|
2020-07-21 15:03:03 +02:00
|
|
|
auto obj1 = lua_get<lua_obj*>(L, 1);
|
2020-07-31 22:55:46 +02:00
|
|
|
auto obj2 = lua_get<lua_obj*>(L, 2);
|
2023-12-03 11:19:22 +01:00
|
|
|
if(!obj1 || !obj2)
|
2022-06-23 11:47:00 +02:00
|
|
|
lua_error(L, "At least 1 parameter should be \"Group\".");
|
2023-12-08 19:53:35 +01:00
|
|
|
if(obj1->lua_type != LuaParam::GROUP)
|
2023-12-03 11:19:22 +01:00
|
|
|
std::swap(obj1, obj2);
|
2023-12-08 19:53:35 +01:00
|
|
|
if(obj1->lua_type != LuaParam::GROUP)
|
2023-12-03 11:19:22 +01:00
|
|
|
lua_error(L, "At least 1 parameter should be \"Group\".");
|
|
|
|
|
|
|
|
|
|
switch(obj2->lua_type) {
|
2023-12-08 19:53:35 +01:00
|
|
|
case LuaParam::GROUP:
|
2023-12-03 11:19:22 +01:00
|
|
|
return { static_cast<group*>(obj1), static_cast<group*>(obj2), nullptr};
|
2023-12-08 19:53:35 +01:00
|
|
|
case LuaParam::CARD:
|
2023-12-03 11:19:22 +01:00
|
|
|
return { static_cast<group*>(obj1), nullptr, static_cast<card*>(obj2) };
|
|
|
|
|
default:
|
2022-06-23 11:47:00 +02:00
|
|
|
lua_error(L, "A parameter isn't \"Group\" nor \"Card\".");
|
2020-07-21 15:03:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-11-21 11:56:23 +01:00
|
|
|
LUA_STATIC_FUNCTION(__band) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 2);
|
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
|
|
|
auto [pgroup1, pgroup2, pcard] = get_binary_op_group_card_parameters(L);
|
2021-09-04 20:46:43 +02:00
|
|
|
card_set cset;
|
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
|
|
|
if(pcard) {
|
2020-10-30 17:05:28 +01:00
|
|
|
if(pgroup1->has_card(pcard)) {
|
2020-02-12 00:21:50 +01:00
|
|
|
cset.insert(pcard);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2020-10-31 02:06:44 +01:00
|
|
|
std::set_intersection(pgroup1->container.cbegin(), pgroup1->container.cend(), pgroup2->container.cbegin(), pgroup2->container.cend(),
|
|
|
|
|
std::inserter(cset, cset.begin()), card_sort());
|
2020-02-12 00:21:50 +01:00
|
|
|
}
|
2021-09-04 20:46:43 +02:00
|
|
|
interpreter::pushobject(L, pduel->new_group(std::move(cset)));
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2023-11-21 11:56:23 +01:00
|
|
|
LUA_STATIC_FUNCTION(__add) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 2);
|
2023-12-03 11:19:22 +01:00
|
|
|
auto [pgroup1, pgroup2, pcard] = get_binary_op_group_card_parameters(L);
|
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
|
|
|
auto newgroup = pduel->new_group(pgroup1);
|
2020-02-12 00:21:50 +01:00
|
|
|
if(pcard) {
|
|
|
|
|
newgroup->container.insert(pcard);
|
|
|
|
|
} else {
|
|
|
|
|
newgroup->container.insert(pgroup2->container.begin(), pgroup2->container.end());
|
|
|
|
|
}
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, newgroup);
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(__sub) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 2);
|
2023-12-03 11:34:38 +01:00
|
|
|
auto [pcard, pgroup] = lua_get_card_or_group(L, 2);
|
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
|
|
|
auto newgroup = pduel->new_group(self);
|
2023-11-26 15:11:50 +01:00
|
|
|
if(pgroup) {
|
|
|
|
|
for(auto& _pcard : pgroup->container)
|
2020-09-16 17:12:50 +02:00
|
|
|
newgroup->container.erase(_pcard);
|
2021-08-11 14:24:03 +02:00
|
|
|
} else
|
2020-02-12 00:21:50 +01:00
|
|
|
newgroup->container.erase(pcard);
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, newgroup);
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(__len) {
|
2023-11-21 11:56:23 +01:00
|
|
|
lua_pushinteger(L, self->container.size());
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(__eq) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 2);
|
2020-07-21 01:21:25 +02:00
|
|
|
auto sgroup = lua_get<group*, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
lua_pushboolean(L, self->container.size() == sgroup->container.size());
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Equal) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2020-07-21 01:21:25 +02:00
|
|
|
auto sgroup = lua_get<group*, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
lua_pushboolean(L, self->container == sgroup->container);
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(__lt) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 2);
|
2020-07-21 01:21:25 +02:00
|
|
|
auto sgroup = lua_get<group*, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
lua_pushboolean(L, self->container.size() < sgroup->container.size());
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(__le) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 2);
|
2020-07-21 01:21:25 +02:00
|
|
|
auto sgroup = lua_get<group*, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
lua_pushboolean(L, self->container.size() <= sgroup->container.size());
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
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
|
|
|
LUA_FUNCTION(__gc) {
|
|
|
|
|
pduel->delete_group(self);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(IsContains) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2020-07-21 01:21:25 +02:00
|
|
|
auto pcard = lua_get<card*, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
lua_pushboolean(L, self->has_card(pcard));
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(SearchCard) {
|
2015-09-25 21:07:16 +08:00
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
uint32_t extraargs = lua_gettop(L) - 2;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container)
|
2022-06-11 12:59:38 +02:00
|
|
|
if(pduel->lua->check_matching(pcard, findex, extraargs)) {
|
2020-07-20 22:05:04 +02:00
|
|
|
interpreter::pushobject(L, pcard);
|
2015-09-25 21:07:16 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Split) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 3);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2023-11-21 11:56:23 +01:00
|
|
|
card_set cset(self->container);
|
2021-09-04 20:46:43 +02:00
|
|
|
card_set notmatching;
|
2023-12-03 11:34:38 +01:00
|
|
|
if(auto [pexception, pexgroup] = lua_get_card_or_group<true>(L, 3); pexception) {
|
2020-02-12 00:21:50 +01:00
|
|
|
cset.erase(pexception);
|
|
|
|
|
notmatching.insert(pexception);
|
2023-12-03 10:27:47 +01:00
|
|
|
} else if(pexgroup) {
|
2020-02-12 00:21:50 +01:00
|
|
|
for(auto& pcard : pexgroup->container) {
|
|
|
|
|
cset.erase(pcard);
|
|
|
|
|
notmatching.insert(pcard);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-04 22:04:14 +02:00
|
|
|
uint32_t extraargs = lua_gettop(L) - 3;
|
2022-10-25 15:40:48 +02:00
|
|
|
for(auto it = cset.begin(); it != cset.end();) {
|
|
|
|
|
auto pcard = *it;
|
2022-06-11 12:59:38 +02:00
|
|
|
if(pduel->lua->check_matching(pcard, findex, extraargs)) {
|
2022-10-25 15:40:48 +02:00
|
|
|
++it;
|
2020-02-12 00:21:50 +01:00
|
|
|
} else {
|
|
|
|
|
notmatching.insert(pcard);
|
2022-10-25 15:40:48 +02:00
|
|
|
it = cset.erase(it);
|
2020-02-12 00:21:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-10-25 15:40:48 +02:00
|
|
|
interpreter::pushobject(L, pduel->new_group(std::move(cset)));
|
2021-09-04 20:46:43 +02:00
|
|
|
interpreter::pushobject(L, pduel->new_group(std::move(notmatching)));
|
2020-02-12 00:21:50 +01:00
|
|
|
return 2;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(Includes) {
|
2020-02-12 00:21:50 +01:00
|
|
|
check_param_count(L, 2);
|
2020-07-21 01:21:25 +02:00
|
|
|
auto pgroup2 = lua_get<group*, true>(L, 2);
|
2020-10-31 01:58:04 +01:00
|
|
|
int res = TRUE;
|
2023-11-21 11:56:23 +01:00
|
|
|
if(self->container.size() < pgroup2->container.size())
|
2020-10-31 01:58:04 +01:00
|
|
|
res = FALSE;
|
2021-10-13 17:29:29 +02:00
|
|
|
else if(!pgroup2->container.empty())
|
2023-11-21 11:56:23 +01:00
|
|
|
res = std::includes(self->container.cbegin(), self->container.cend(), pgroup2->container.cbegin(), pgroup2->container.cend(), card_sort());
|
2020-10-31 01:58:04 +01:00
|
|
|
lua_pushboolean(L, res);
|
2020-02-12 00:21:50 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION(GetBinClassCount) {
|
2018-04-27 21:17:45 +08:00
|
|
|
check_param_count(L, 2);
|
2022-06-11 12:59:38 +02:00
|
|
|
const auto findex = lua_get<function, true>(L, 2);
|
2021-09-04 22:04:14 +02:00
|
|
|
int32_t extraargs = lua_gettop(L) - 2;
|
2022-08-27 16:49:51 +02:00
|
|
|
uint64_t er = 0;
|
2023-11-21 11:56:23 +01:00
|
|
|
for(auto& pcard : self->container)
|
2022-08-27 16:49:51 +02:00
|
|
|
er |= static_cast<uint64_t>(pduel->lua->get_operation_value(pcard, findex, extraargs));
|
2023-04-23 21:34:36 +02:00
|
|
|
lua_pushinteger(L, bit::popcnt(er));
|
2018-04-27 21:17:45 +08:00
|
|
|
return 1;
|
|
|
|
|
}
|
2022-05-14 19:55:59 +02:00
|
|
|
LUA_FUNCTION_EXISTING(GetLuaRef, get_lua_ref<group>);
|
|
|
|
|
LUA_FUNCTION_EXISTING(FromLuaRef, from_lua_ref<group>);
|
|
|
|
|
LUA_FUNCTION_EXISTING(IsDeleted, is_deleted_object);
|
2021-10-13 17:29:29 +02:00
|
|
|
}
|
2025-07-05 19:50:36 +02:00
|
|
|
}
|
2021-10-13 17:29:29 +02:00
|
|
|
|
|
|
|
|
void scriptlib::push_group_lib(lua_State* L) {
|
2022-05-14 19:55:59 +02:00
|
|
|
static constexpr auto grouplib = GET_LUA_FUNCTIONS_ARRAY();
|
2023-11-26 14:47:09 +01:00
|
|
|
static_assert(grouplib.back().name == nullptr);
|
2023-02-07 16:43:27 +01:00
|
|
|
lua_createtable(L, 0, static_cast<int>(grouplib.size() - 1));
|
2024-04-13 10:34:09 +02:00
|
|
|
ensure_luaL_stack(luaL_setfuncs, L, grouplib.data(), 0);
|
2021-10-13 17:29:29 +02:00
|
|
|
lua_pushstring(L, "__index");
|
|
|
|
|
lua_pushvalue(L, -2);
|
|
|
|
|
lua_rawset(L, -3);
|
|
|
|
|
lua_setglobal(L, "Group");
|
2023-02-07 16:43:27 +01:00
|
|
|
}
|