Lua API: Plugins can now execute gamestate code once they are in the game context.
This commit is contained in:
parent
cc1069734c
commit
3387eb2c26
8 changed files with 282 additions and 6 deletions
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
if wesnoth.kernel_type() == "Application Lua Kernel" then
|
if wesnoth.kernel_type() == "Application Lua Kernel" then
|
||||||
print("Loading plugin module...")
|
print("Loading plugin module...")
|
||||||
wesnoth.plugin = {}
|
|
||||||
|
|
||||||
---Yields control back to the game until the next slice.
|
---Yields control back to the game until the next slice.
|
||||||
---@return WMLTable
|
---@return WMLTable
|
||||||
|
|
|
@ -280,6 +280,7 @@ void play_controller::init(const config& level)
|
||||||
plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
|
plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
|
||||||
plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
|
plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
|
||||||
plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
|
plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
|
||||||
|
plugins_context_->set_callback_execute(*resources::lua_kernel);
|
||||||
plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
|
plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "scripting/lua_preferences.hpp"
|
#include "scripting/lua_preferences.hpp"
|
||||||
#include "scripting/plugins/context.hpp"
|
#include "scripting/plugins/context.hpp"
|
||||||
#include "scripting/plugins/manager.hpp"
|
#include "scripting/plugins/manager.hpp"
|
||||||
|
#include "scripting/push_check.hpp"
|
||||||
|
|
||||||
#ifdef DEBUG_LUA
|
#ifdef DEBUG_LUA
|
||||||
#include "scripting/debug_lua.hpp"
|
#include "scripting/debug_lua.hpp"
|
||||||
|
@ -52,7 +53,6 @@
|
||||||
|
|
||||||
#include "lua/wrapper_lauxlib.h"
|
#include "lua/wrapper_lauxlib.h"
|
||||||
|
|
||||||
|
|
||||||
static lg::log_domain log_scripting_lua("scripting/lua");
|
static lg::log_domain log_scripting_lua("scripting/lua");
|
||||||
#define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
|
#define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
|
||||||
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
|
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
|
||||||
|
@ -93,6 +93,8 @@ static int intf_delay(lua_State* L)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int intf_execute(lua_State* L);
|
||||||
|
|
||||||
application_lua_kernel::application_lua_kernel()
|
application_lua_kernel::application_lua_kernel()
|
||||||
: lua_kernel_base()
|
: lua_kernel_base()
|
||||||
{
|
{
|
||||||
|
@ -108,6 +110,14 @@ application_lua_kernel::application_lua_kernel()
|
||||||
|
|
||||||
// Create the preferences table.
|
// Create the preferences table.
|
||||||
cmd_log_ << lua_preferences::register_table(mState);
|
cmd_log_ << lua_preferences::register_table(mState);
|
||||||
|
|
||||||
|
// Create the wesnoth.plugin table
|
||||||
|
luaW_getglobal(mState, "wesnoth");
|
||||||
|
lua_newtable(mState);
|
||||||
|
lua_pushcfunction(mState, intf_execute);
|
||||||
|
lua_setfield(mState, -2, "execute");
|
||||||
|
lua_setfield(mState, -2, "plugin");
|
||||||
|
lua_pop(mState, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
application_lua_kernel::thread::thread(application_lua_kernel& owner, lua_State * T) : owner_(owner), T_(T), started_(false) {}
|
application_lua_kernel::thread::thread(application_lua_kernel& owner, lua_State * T) : owner_(owner), T_(T), started_(false) {}
|
||||||
|
@ -216,6 +226,7 @@ application_lua_kernel::thread * application_lua_kernel::load_script_from_file(c
|
||||||
|
|
||||||
struct lua_context_backend {
|
struct lua_context_backend {
|
||||||
std::vector<plugins_manager::event> requests;
|
std::vector<plugins_manager::event> requests;
|
||||||
|
lua_kernel_base* execute;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
lua_context_backend()
|
lua_context_backend()
|
||||||
|
@ -257,6 +268,47 @@ static int impl_context_accessor(lua_State * L, std::shared_ptr<lua_context_back
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int intf_execute(lua_State* L)
|
||||||
|
{
|
||||||
|
static const int CTX = 1, FUNC = 2, EVT = 3, EXEC = 4;
|
||||||
|
if(lua_gettop(L) == 2) lua_pushnil(L);
|
||||||
|
if(!luaW_table_get_def(L, CTX, "valid", false)) {
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
lua_pushstring(L, "context not valid");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if(!luaW_tableget(L, CTX, "execute")) {
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
lua_pushstring(L, "context cannot execute");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if(!lua_islightuserdata(L, EXEC)) {
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
lua_pushstring(L, "execute is not a thread");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
config data = luaW_serialize_function(L, FUNC);
|
||||||
|
if(data["params"] != 0) {
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
lua_pushstring(L, "cannot execute function with parameters");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if(!lua_isnil(L, EVT)) data["name"] = luaL_checkstring(L, EVT);
|
||||||
|
lua_pushvalue(L, FUNC);
|
||||||
|
data["ref"] = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||||
|
std::shared_ptr<lua_context_backend>* context = static_cast<std::shared_ptr<lua_context_backend>*>(lua_touserdata(L, EXEC));
|
||||||
|
luaW_pushconfig(L, data);
|
||||||
|
impl_context_backend(L, *context, "execute");
|
||||||
|
} catch(luafunc_serialize_error& e) {
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
lua_pushstring(L, e.what());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushboolean(L, true);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bool luaW_copy_upvalues(lua_State* L, const config& cfg);
|
||||||
application_lua_kernel::request_list application_lua_kernel::thread::run_script(const plugins_context & ctxt, const std::vector<plugins_manager::event> & queue)
|
application_lua_kernel::request_list application_lua_kernel::thread::run_script(const plugins_context & ctxt, const std::vector<plugins_manager::event> & queue)
|
||||||
{
|
{
|
||||||
// There are two possibilities: (1) this is the first execution, and the C function is the only thing on the stack
|
// There are two possibilities: (1) this is the first execution, and the C function is the only thing on the stack
|
||||||
|
@ -281,6 +333,11 @@ application_lua_kernel::request_list application_lua_kernel::thread::run_script(
|
||||||
lua_cpp::push_function(T_, std::bind(&impl_context_backend, std::placeholders::_1, this_context_backend, key));
|
lua_cpp::push_function(T_, std::bind(&impl_context_backend, std::placeholders::_1, this_context_backend, key));
|
||||||
lua_settable(T_, -3);
|
lua_settable(T_, -3);
|
||||||
}
|
}
|
||||||
|
if(ctxt.execute_kernel_) {
|
||||||
|
lua_pushstring(T_, "execute");
|
||||||
|
lua_pushlightuserdata(T_, &this_context_backend);
|
||||||
|
lua_settable(T_, -3);
|
||||||
|
}
|
||||||
|
|
||||||
// Now we have to create the info object (context accessors). It is arranged as a table of boost functions.
|
// Now we have to create the info object (context accessors). It is arranged as a table of boost functions.
|
||||||
lua_newtable(T_); // this will be the info table
|
lua_newtable(T_); // this will be the info table
|
||||||
|
@ -351,8 +408,59 @@ application_lua_kernel::request_list application_lua_kernel::thread::run_script(
|
||||||
application_lua_kernel::request_list results;
|
application_lua_kernel::request_list results;
|
||||||
|
|
||||||
for (const plugins_manager::event & req : this_context_backend->requests) {
|
for (const plugins_manager::event & req : this_context_backend->requests) {
|
||||||
|
if(ctxt.execute_kernel_ && req.name == "execute") {
|
||||||
|
results.push_back([this, lk = ctxt.execute_kernel_, data = req.data]() {
|
||||||
|
auto result = lk->run_binary_lua_tag(data);
|
||||||
|
int ref = result["ref"];
|
||||||
|
auto func = result.mandatory_child("executed");
|
||||||
|
result.remove_children("executed");
|
||||||
|
result.remove_attribute("ref");
|
||||||
|
plugins_manager::get()->notify_event(result["name"], result);
|
||||||
|
lua_rawgeti(T_, LUA_REGISTRYINDEX, ref);
|
||||||
|
luaW_copy_upvalues(T_, func);
|
||||||
|
luaL_unref(T_, LUA_REGISTRYINDEX, ref);
|
||||||
|
lua_pop(T_, 1);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
results.push_back(std::bind(ctxt.callbacks_.find(req.name)->second, req.data));
|
results.push_back(std::bind(ctxt.callbacks_.find(req.name)->second, req.data));
|
||||||
//results.emplace_back(ctxt.callbacks_.find(req.name)->second, req.data);
|
//results.emplace_back(ctxt.callbacks_.find(req.name)->second, req.data);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool luaW_copy_upvalues(lua_State* L, const config& cfg)
|
||||||
|
{
|
||||||
|
if(auto upvalues = cfg.optional_child("upvalues")) {
|
||||||
|
lua_pushvalue(L, -1); // duplicate function because lua_getinfo will pop it
|
||||||
|
lua_Debug info;
|
||||||
|
lua_getinfo(L, ">u", &info);
|
||||||
|
int funcindex = lua_absindex(L, -1);
|
||||||
|
for(int i = 1; i <= info.nups; i++, lua_pop(L, 1)) {
|
||||||
|
std::string_view name = lua_getupvalue(L, funcindex, i);
|
||||||
|
if(name == "_ENV") {
|
||||||
|
lua_pushglobaltable(L);
|
||||||
|
} else if(upvalues->has_attribute(name)) {
|
||||||
|
luaW_pushscalar(L, (*upvalues)[name]);
|
||||||
|
} else if(upvalues->has_child(name)) {
|
||||||
|
const auto& child = upvalues->mandatory_child(name);
|
||||||
|
if(child["upvalue_type"] == "array") {
|
||||||
|
auto children = upvalues->child_range(name);
|
||||||
|
lua_createtable(L, children.size(), 0);
|
||||||
|
for(const auto& cfg : children) {
|
||||||
|
luaW_pushscalar(L, cfg["value"]);
|
||||||
|
lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
|
||||||
|
}
|
||||||
|
} else if(child["upvalue_type"] == "config") {
|
||||||
|
luaW_pushconfig(L, child);
|
||||||
|
} else if(child["upvalue_type"] == "function") {
|
||||||
|
luaW_copy_upvalues(L, child);
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
}
|
||||||
|
} else continue;
|
||||||
|
lua_setupvalue(L, funcindex, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -1065,10 +1065,10 @@ bool lua_kernel_base::protected_call(lua_State * L, int nArgs, int nRets, error_
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lua_kernel_base::load_string(char const * prog, const std::string& name, error_handler e_h)
|
bool lua_kernel_base::load_string(const std::string& prog, const std::string& name, error_handler e_h, bool allow_unsafe)
|
||||||
{
|
{
|
||||||
// pass 't' to prevent loading bytecode which is unsafe and can be used to escape the sandbox.
|
// pass 't' to prevent loading bytecode which is unsafe and can be used to escape the sandbox.
|
||||||
int errcode = luaL_loadbufferx(mState, prog, strlen(prog), name.empty() ? prog : name.c_str(), "t");
|
int errcode = luaL_loadbufferx(mState, prog.c_str(), prog.size(), name.empty() ? prog.c_str() : name.c_str(), allow_unsafe ? "tb" : "t");
|
||||||
if (errcode != LUA_OK) {
|
if (errcode != LUA_OK) {
|
||||||
char const * msg = lua_tostring(mState, -1);
|
char const * msg = lua_tostring(mState, -1);
|
||||||
std::string message = msg ? msg : "null string";
|
std::string message = msg ? msg : "null string";
|
||||||
|
@ -1101,6 +1101,147 @@ void lua_kernel_base::run_lua_tag(const config& cfg)
|
||||||
}
|
}
|
||||||
this->run(cfg["code"].str().c_str(), cfg["name"].str(), nArgs);
|
this->run(cfg["code"].str().c_str(), cfg["name"].str(), nArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config luaW_serialize_function(lua_State* L, int func)
|
||||||
|
{
|
||||||
|
if(lua_iscfunction(L, func)) {
|
||||||
|
throw luafunc_serialize_error("cannot serialize C function");
|
||||||
|
}
|
||||||
|
if(!lua_isfunction(L, func)) {
|
||||||
|
throw luafunc_serialize_error("cannot serialize callable non-function");
|
||||||
|
}
|
||||||
|
config data;
|
||||||
|
lua_Debug info;
|
||||||
|
lua_pushvalue(L, func); // push copy of function because lua_getinfo will pop it
|
||||||
|
lua_getinfo(L, ">u", &info);
|
||||||
|
data["params"] = info.nparams;
|
||||||
|
luaW_getglobal(L, "string", "dump");
|
||||||
|
lua_pushvalue(L, func);
|
||||||
|
lua_call(L, 1, 1);
|
||||||
|
data["code"] = lua_check<std::string>(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
config upvalues;
|
||||||
|
for(int i = 1; i <= info.nups; i++, lua_pop(L, 1)) {
|
||||||
|
std::string_view name = lua_getupvalue(L, func, i);
|
||||||
|
if(name == "_ENV") {
|
||||||
|
upvalues.add_child(name)["upvalue_type"] = "_ENV";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int idx = lua_absindex(L, -1);
|
||||||
|
switch(lua_type(L, idx)) {
|
||||||
|
case LUA_TBOOLEAN: case LUA_TNUMBER: case LUA_TSTRING:
|
||||||
|
luaW_toscalar(L, idx, upvalues[name]);
|
||||||
|
break;
|
||||||
|
case LUA_TFUNCTION:
|
||||||
|
upvalues.add_child(name, luaW_serialize_function(L, idx))["upvalue_type"] = "function";
|
||||||
|
break;
|
||||||
|
case LUA_TTABLE:
|
||||||
|
if(config cfg; luaW_toconfig(L, idx, cfg)) {
|
||||||
|
upvalues.add_child(name, cfg)["upvalue_type"] = "config";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
for(size_t i = 1; i <= lua_rawlen(L, -1); i++, lua_pop(L, 1)) {
|
||||||
|
lua_rawgeti(L, idx, i);
|
||||||
|
config& cfg = upvalues.add_child(name);
|
||||||
|
luaW_toscalar(L, -1, cfg["value"]);
|
||||||
|
cfg["upvalue_type"] = "array";
|
||||||
|
}
|
||||||
|
bool found_non_array = false;
|
||||||
|
for(lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) {
|
||||||
|
if(lua_type(L, -2) != LUA_TNUMBER) {
|
||||||
|
found_non_array = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found_non_array) break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "cannot serialize function with upvalue " << name << " = ";
|
||||||
|
luaW_getglobal(L, "wesnoth", "as_text");
|
||||||
|
lua_pushvalue(L, idx);
|
||||||
|
lua_call(L, 1, 1);
|
||||||
|
os << luaL_checkstring(L, -1);
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
throw luafunc_serialize_error(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!upvalues.empty()) data.add_child("upvalues", upvalues);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lua_kernel_base::load_binary(const config& cfg, error_handler eh)
|
||||||
|
{
|
||||||
|
if(!load_string(cfg["code"].str(), cfg["name"], eh, true)) return false;
|
||||||
|
if(auto upvalues = cfg.optional_child("upvalues")) {
|
||||||
|
lua_pushvalue(mState, -1); // duplicate function because lua_getinfo will pop it
|
||||||
|
lua_Debug info;
|
||||||
|
lua_getinfo(mState, ">u", &info);
|
||||||
|
int funcindex = lua_absindex(mState, -1);
|
||||||
|
for(int i = 1; i <= info.nups; i++) {
|
||||||
|
std::string_view name = lua_getupvalue(mState, funcindex, i);
|
||||||
|
lua_pop(mState, 1); // we only want the upvalue's name, not its value
|
||||||
|
if(name == "_ENV") {
|
||||||
|
lua_pushglobaltable(mState);
|
||||||
|
} else if(upvalues->has_attribute(name)) {
|
||||||
|
luaW_pushscalar(mState, (*upvalues)[name]);
|
||||||
|
} else if(upvalues->has_child(name)) {
|
||||||
|
const auto& child = upvalues->mandatory_child(name);
|
||||||
|
if(child["upvalue_type"] == "array") {
|
||||||
|
auto children = upvalues->child_range(name);
|
||||||
|
lua_createtable(mState, children.size(), 0);
|
||||||
|
for(const auto& cfg : children) {
|
||||||
|
luaW_pushscalar(mState, cfg["value"]);
|
||||||
|
lua_rawseti(mState, -2, lua_rawlen(mState, -2) + 1);
|
||||||
|
}
|
||||||
|
} else if(child["upvalue_type"] == "config") {
|
||||||
|
luaW_pushconfig(mState, child);
|
||||||
|
} else if(child["upvalue_type"] == "function") {
|
||||||
|
if(!load_binary(child, eh)) return false;
|
||||||
|
}
|
||||||
|
} else continue;
|
||||||
|
lua_setupvalue(mState, funcindex, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
config lua_kernel_base::run_binary_lua_tag(const config& cfg)
|
||||||
|
{
|
||||||
|
int top = lua_gettop(mState);
|
||||||
|
try {
|
||||||
|
error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
|
||||||
|
if(load_binary(cfg, eh)) {
|
||||||
|
lua_pushvalue(mState, -1);
|
||||||
|
protected_call(0, LUA_MULTRET, eh);
|
||||||
|
}
|
||||||
|
} catch (const game::lua_error & e) {
|
||||||
|
cmd_log_ << e.what() << "\n";
|
||||||
|
lua_kernel_base::log_error(e.what(), "In function lua_kernel::run()");
|
||||||
|
config error;
|
||||||
|
error["name"] = "execute_error";
|
||||||
|
error["error"] = e.what();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
config result;
|
||||||
|
result["ref"] = cfg["ref"];
|
||||||
|
result.add_child("executed") = luaW_serialize_function(mState, top + 1);
|
||||||
|
lua_remove(mState, top + 1);
|
||||||
|
result["name"] = "execute_result";
|
||||||
|
for(int i = top + 1; i < lua_gettop(mState); i++) {
|
||||||
|
std::string index = std::to_string(i - top);
|
||||||
|
switch(lua_type(mState, i)) {
|
||||||
|
case LUA_TNUMBER: case LUA_TBOOLEAN: case LUA_TSTRING:
|
||||||
|
luaW_toscalar(mState, i, result[index]);
|
||||||
|
break;
|
||||||
|
case LUA_TTABLE:
|
||||||
|
luaW_toconfig(mState, i, result.add_child(index));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
// Call load_string and protected call. Make them throw exceptions.
|
// Call load_string and protected call. Make them throw exceptions.
|
||||||
//
|
//
|
||||||
void lua_kernel_base::throwing_run(const char * prog, const std::string& name, int nArgs, bool in_interpreter)
|
void lua_kernel_base::throwing_run(const char * prog, const std::string& name, int nArgs, bool in_interpreter)
|
||||||
|
|
|
@ -32,6 +32,9 @@ public:
|
||||||
/** Runs a [lua] tag. Doesn't throw lua_error.*/
|
/** Runs a [lua] tag. Doesn't throw lua_error.*/
|
||||||
void run_lua_tag(const config& cfg);
|
void run_lua_tag(const config& cfg);
|
||||||
|
|
||||||
|
/** Runs a binary [lua] tag. Doesn't throw lua_error.*/
|
||||||
|
config run_binary_lua_tag(const config& cfg);
|
||||||
|
|
||||||
/** Runs a plain script. Doesn't throw lua_error.*/
|
/** Runs a plain script. Doesn't throw lua_error.*/
|
||||||
void run(char const *prog, const std::string& name, int nArgs = 0);
|
void run(char const *prog, const std::string& name, int nArgs = 0);
|
||||||
|
|
||||||
|
@ -125,7 +128,8 @@ protected:
|
||||||
// Execute a protected call, taking a lua_State as argument. For functions pushed into the lua environment, this version should be used, or the function cannot be used by coroutines without segfaulting (since they have a different lua_State pointer). This version is called by the above version.
|
// Execute a protected call, taking a lua_State as argument. For functions pushed into the lua environment, this version should be used, or the function cannot be used by coroutines without segfaulting (since they have a different lua_State pointer). This version is called by the above version.
|
||||||
static bool protected_call(lua_State * L, int nArgs, int nRets, error_handler);
|
static bool protected_call(lua_State * L, int nArgs, int nRets, error_handler);
|
||||||
// Load a string onto the stack as a function. Returns true if successful, error handler is called if not.
|
// Load a string onto the stack as a function. Returns true if successful, error handler is called if not.
|
||||||
bool load_string(char const * prog, const std::string& name, error_handler);
|
bool load_string(const std::string& prog, const std::string& name, error_handler, bool allow_unsafe = false);
|
||||||
|
bool load_binary(const config& func, error_handler);
|
||||||
|
|
||||||
virtual bool protected_call(int nArgs, int nRets); // select default error handler polymorphically
|
virtual bool protected_call(int nArgs, int nRets); // select default error handler polymorphically
|
||||||
virtual bool load_string(char const * prog, const std::string& name); // select default error handler polymorphically
|
virtual bool load_string(char const * prog, const std::string& name); // select default error handler polymorphically
|
||||||
|
@ -146,6 +150,12 @@ private:
|
||||||
std::vector<std::tuple<std::string, std::string>> registered_widget_definitions_;
|
std::vector<std::tuple<std::string, std::string>> registered_widget_definitions_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
config luaW_serialize_function(lua_State* L, int func);
|
||||||
|
|
||||||
|
struct luafunc_serialize_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<std::string> luaW_get_attributes(lua_State* L, int idx);
|
std::vector<std::string> luaW_get_attributes(lua_State* L, int idx);
|
||||||
|
|
||||||
struct game_config_tag {
|
struct game_config_tag {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "scripting/plugins/context.hpp"
|
#include "scripting/plugins/context.hpp"
|
||||||
|
|
||||||
#include "scripting/plugins/manager.hpp"
|
#include "scripting/plugins/manager.hpp"
|
||||||
|
#include "scripting/lua_kernel_base.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -103,3 +104,7 @@ void plugins_context::set_callback(const std::string & name, std::function<void(
|
||||||
{
|
{
|
||||||
set_callback(name, [func, preserves_context](config cfg) { func(cfg); return preserves_context; });
|
set_callback(name, [func, preserves_context](config cfg) { func(cfg); return preserves_context; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void plugins_context::set_callback_execute(lua_kernel_base& kernel) {
|
||||||
|
execute_kernel_ = &kernel;
|
||||||
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ public:
|
||||||
|
|
||||||
void set_callback(const std::string & name, callback_function);
|
void set_callback(const std::string & name, callback_function);
|
||||||
void set_callback(const std::string & name, std::function<void(config)> function, bool preserves_context);
|
void set_callback(const std::string & name, std::function<void(config)> function, bool preserves_context);
|
||||||
|
void set_callback_execute(class lua_kernel_base& kernel);
|
||||||
std::size_t erase_callback(const std::string & name);
|
std::size_t erase_callback(const std::string & name);
|
||||||
std::size_t clear_callbacks();
|
std::size_t clear_callbacks();
|
||||||
|
|
||||||
|
@ -67,4 +68,5 @@ private:
|
||||||
callback_list callbacks_;
|
callback_list callbacks_;
|
||||||
accessor_list accessors_;
|
accessor_list accessors_;
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
lua_kernel_base* execute_kernel_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,4 +10,14 @@
|
||||||
---Contains accessors for the current context
|
---Contains accessors for the current context
|
||||||
---@class plugin_info
|
---@class plugin_info
|
||||||
---@field name string The name of the current context.
|
---@field name string The name of the current context.
|
||||||
---@field [string] plugin_accessor An accessor takes a WML table as its argument and returns a string, integer, or WML table.
|
---@field [string] plugin_accessor An accessor takes a WML table as its argument and returns a string, integer, or WML table.
|
||||||
|
|
||||||
|
---Execute a function within the current context's game state, if supported.
|
||||||
|
---Functions returning a value can request that the result be returned in an event in the next slice.
|
||||||
|
---If the function raises an error, that too will be returned as an event in the next slice.
|
||||||
|
---@param context plugin_context The current plugin context.
|
||||||
|
---@param fcn function An arbitrary function to execute. The function will be run in a different Lua kernel and thus cannot access the wesnoth.plugin module.
|
||||||
|
---@param event_name? string The name to use for the event that contains the function's result
|
||||||
|
---@return boolean #True if the function will be executed; false if unsupported
|
||||||
|
---@return string? #If the first value is false, this will hold an explanatory string
|
||||||
|
function wesnoth.plugin.execute(context, fcn, event_name) end
|
||||||
|
|
Loading…
Add table
Reference in a new issue