Merge pull request #330 from cbeck88/new_lua_kernel_i

New lua kernel i
This commit is contained in:
Chris Beck 2014-11-09 21:31:53 -05:00
commit ca62a8f7bf
33 changed files with 1039 additions and 431 deletions

View file

@ -935,9 +935,13 @@ set(wesnoth-main_SRC
save_index.cpp
saved_game.cpp
savegame.cpp
scripting/application_lua_kernel.cpp
scripting/debug_lua.cpp
scripting/lua.cpp
scripting/game_lua_kernel.cpp
scripting/lua_api.cpp
scripting/lua_common.cpp
scripting/lua_game_launcher.cpp
scripting/lua_kernel_base.cpp
scripting/lua_types.cpp
settings.cpp
side_filter.cpp

View file

@ -519,9 +519,13 @@ wesnoth_sources = Split("""
save_index.cpp
saved_game.cpp
savegame.cpp
scripting/application_lua_kernel.cpp
scripting/debug_lua.cpp
scripting/lua.cpp
scripting/game_lua_kernel.cpp
scripting/lua_api.cpp
scripting/lua_common.cpp
scripting/lua_game_launcher.cpp
scripting/lua_kernel_base.cpp
scripting/lua_types.cpp
settings.cpp
side_filter.cpp

View file

@ -23,7 +23,7 @@
#include "value_translator.hpp"
#include "../lua/lua_object.hpp"
#include "../lua/core.hpp"
#include "../../scripting/lua.hpp"
#include "../../scripting/game_lua_kernel.hpp"
#include "../../log.hpp"

View file

@ -30,7 +30,7 @@
#include "../../resources.hpp"
#include "../lua/core.hpp"
#include "../lua/lua_object.hpp"
#include "../../scripting/lua.hpp"
#include "../../scripting/game_lua_kernel.hpp"
#include "../../util.hpp"
#include "../../unit.hpp"
#include "../../unit_map.hpp"

View file

@ -29,7 +29,7 @@
#include "log.hpp"
#include "map_location.hpp"
#include "resources.hpp"
#include "scripting/lua.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "team.hpp"
#include "terrain_filter.hpp"
#include "unit.hpp"

View file

@ -26,7 +26,7 @@
#include <cstring>
#include "core.hpp"
#include "../../scripting/lua.hpp"
#include "../../scripting/game_lua_kernel.hpp"
#include "../../scripting/lua_api.hpp"
#include "lua_object.hpp" // (Nephro)

View file

@ -15,6 +15,12 @@
#include "commandline_options.hpp"
#include "global.hpp"
#include "config.hpp"
#include "formatter.hpp"
#include "log.hpp" // for logger, set_strict_severity, etc
#include "serialization/string_utils.hpp" // for split
#include "util.hpp" // for lexical_cast
#include <boost/any.hpp> // for any
#include <boost/foreach.hpp> // for auto_any_base, etc
#include <boost/program_options/cmdline.hpp>
@ -25,11 +31,6 @@
#include <boost/program_options/variables_map.hpp> // for variables_map, etc
#include <boost/version.hpp> // for BOOST_VERSION
#include <iostream> // for operator<<, basic_ostream, etc
#include "formatter.hpp"
#include "log.hpp" // for logger, set_strict_severity, etc
#include "serialization/string_utils.hpp" // for split
#include "util.hpp" // for lexical_cast
namespace po = boost::program_options;
@ -132,6 +133,7 @@ commandline_options::commandline_options (const std::vector<std::string>& args)
screenshot(false),
screenshot_map_file(),
screenshot_output_file(),
script_unsafe_mode(false),
strict_validation(false),
test(),
unit_test(),
@ -184,6 +186,8 @@ commandline_options::commandline_options (const std::vector<std::string>& args)
("render-image", po::value<two_strings>()->multitoken(), "takes two arguments: <image> <output>. Like screenshot, but instead of a map, takes a valid wesnoth 'image path string' with image path functions, and outputs to a windows .bmp file")
("rng-seed", po::value<unsigned int>(), "seeds the random number generator with number <arg>. Example: --rng-seed 0")
("screenshot", po::value<two_strings>()->multitoken(), "takes two arguments: <map> <output>. Saves a screenshot of <map> to <output> without initializing a screen. Editor must be compiled in for this to work.")
("script", po::value<std::string>(), "file containing a lua script to control the client")
("unsafe-scripts", "makes the \'package\' package available to lua scripts, so that they can load arbitrary packages. Do not do this with untrusted scripts! This action gives lua the same permissions as the wesnoth executable.")
("server,s", po::value<std::string>()->implicit_value(std::string()), "connects to the host <arg> if specified or to the first host in your preferences.")
("username", po::value<std::string>(), "uses <username> when connecting to a server, ignoring other preferences.")
("password", po::value<std::string>(), "uses <password> when connecting to a server, ignoring other preferences.")
@ -429,6 +433,10 @@ commandline_options::commandline_options (const std::vector<std::string>& args)
screenshot_map_file = vm["screenshot"].as<two_strings>().get<0>();
screenshot_output_file = vm["screenshot"].as<two_strings>().get<1>();
}
if (vm.count("script"))
script_file = vm["script"].as<std::string>();
if (vm.count("unsafe-scripts"))
script_unsafe_mode = true;
if (vm.count("server"))
server = vm["server"].as<std::string>();
if (vm.count("username"))
@ -576,3 +584,17 @@ std::ostream& operator<<(std::ostream &os, const commandline_options& cmdline_op
os << cmdline_opts.visible_;
return os;
}
config commandline_options::to_config() const {
config ret;
if (server) {
ret["server"] = *server;
}
if (username) {
ret["username"] = *username;
}
if (password) {
ret["password"] = *password;
}
return ret;
}

View file

@ -35,6 +35,8 @@ public:
const std::string& expected_format);
};
class config;
class commandline_options
{
/// To be used for printing help to the commandline.
@ -43,6 +45,8 @@ friend std::ostream& operator<<(std::ostream &os, const commandline_options& cmd
public:
commandline_options(const std::vector<std::string>& args);
config to_config() const; /* Used by lua scrips. Not all of the options need to be exposed here, just those exposed to lua */
/// BitsPerPixel specified by --bpp option.
boost::optional<int> bpp;
/// Non-empty if --bunzip2 was given on the command line. Uncompresses a .bz2 file and exits.
@ -184,6 +188,10 @@ public:
boost::optional<std::string> screenshot_map_file;
/// Output file to put screenshot in. Second parameter given after --screenshot.
boost::optional<std::string> screenshot_output_file;
/// File to load lua script (mp-bot) from.
boost::optional<std::string> script_file;
/// Whether to load the "package" package for the scripting environment. (This allows to load arbitrary lua packages, and gives untrusted lua the same permissions as wesnoth executable)
bool script_unsafe_mode;
/// True if --strict-validation was given on the command line. Makes Wesnoth trust validation errors as fatal WML errors and create WML exception, if so.
bool strict_validation;
/// Non-empty if --test was given on the command line. Goes directly into test mode, into a scenario, if specified.

View file

@ -28,7 +28,7 @@
#include "log.hpp"
#include "preferences.hpp"
#include "resources.hpp"
#include "scripting/lua.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "terrain_builder.hpp"
#include "terrain_type_data.hpp"
#include "unit_types.hpp"

View file

@ -29,7 +29,7 @@
#include "../log.hpp"
#include "../reports.hpp"
#include "../resources.hpp"
#include "../scripting/lua.hpp"
#include "../scripting/game_lua_kernel.hpp"
#include "../serialization/string_utils.hpp"
#include "../soundsource.hpp"
#include "../util.hpp"

View file

@ -31,7 +31,7 @@
#include "../log.hpp"
#include "../play_controller.hpp"
#include "../resources.hpp"
#include "../scripting/lua.hpp"
#include "../scripting/game_lua_kernel.hpp"
#include "../side_filter.hpp"
#include "../unit.hpp"
#include "../unit_map.hpp"

View file

@ -50,6 +50,7 @@
#include "preferences_display.hpp" // for detect_video_settings, etc
#include "resources.hpp" // for config_manager
#include "savegame.hpp" // for clean_saves, etc
#include "scripting/application_lua_kernel.hpp"
#include "sdl/utils.hpp" // for surface
#include "serialization/compression.hpp" // for format::NONE
#include "serialization/string_utils.hpp" // for split
@ -443,6 +444,40 @@ bool game_launcher::init_video()
return true;
}
bool game_launcher::init_lua_script()
{
// start the application lua kernel, register it in resources, and load script file, if script file is present
if (cmdline_opts_.script_file)
{
filesystem::scoped_istream sf = filesystem::istream_file(*cmdline_opts_.script_file);
if (!sf->fail()) {
/* Cancel all "jumps" to editor / campaign / multiplayer */
jump_to_multiplayer_ = false;
jump_to_editor_ = false;
jump_to_campaign_.jump_ = false;
std::string full_script((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
std::cerr << "\nRunning lua script: " << *cmdline_opts_.script_file << std::endl;
resources::app_lua_kernel = new application_lua_kernel();
resources::app_lua_kernel->initialize(this);
if (cmdline_opts_.script_unsafe_mode) {
resources::app_lua_kernel->load_package(); //load the "package" package, so that scripts can get what packages they want
}
resources::app_lua_kernel->run(full_script.c_str());
return true;
} else {
std::cerr << "Scripting disabled, encountered failure when opening " << *cmdline_opts_.script_file << std::endl;
}
}
return false;
}
bool game_launcher::play_test()
{
static bool first_time = true;

View file

@ -62,6 +62,7 @@ public:
bool init_video();
bool init_language();
bool init_joystick();
bool init_lua_script();
bool play_test();
bool play_screenshot_mode();
@ -94,6 +95,8 @@ public:
editor::EXIT_STATUS start_editor() { return start_editor(""); }
void start_wesnothd();
const commandline_options & opts() const { return cmdline_opts_; }
private:
game_launcher(const game_launcher&);
void operator=(const game_launcher&);

View file

@ -64,7 +64,7 @@
#include "resources.hpp"
#include "savegame.hpp"
#include "save_index.hpp"
#include "scripting/lua.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "sound.hpp"
#include "statistics_dialog.hpp"
#include "synced_context.hpp"

View file

@ -46,7 +46,7 @@
#include "savegame.hpp"
#include "saved_game.hpp"
#include "save_blocker.hpp"
#include "scripting/lua.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "sound.hpp"
#include "soundsource.hpp"
#include "synced_context.hpp"

View file

@ -23,6 +23,7 @@ namespace resources
game_data *gamedata = NULL;
filter_context *filter_con = NULL;
LuaKernel *lua_kernel = NULL;
application_lua_kernel *app_lua_kernel = NULL;
persist_manager *persist = NULL;
game_display *screen = NULL;
soundsource::manager *soundsources = NULL;

View file

@ -25,6 +25,7 @@ class gamemap;
class game_data;
class filter_context;
class LuaKernel;
class application_lua_kernel;
class play_controller;
class team;
class fake_unit_manager;
@ -50,6 +51,7 @@ namespace resources
extern game_board *gameboard;
extern game_data *gamedata;
extern LuaKernel *lua_kernel; // Set by game_events::manager.
extern application_lua_kernel *app_lua_kernel;
extern persist_manager *persist;
extern game_classification *classification;
extern game_display *screen;

View file

@ -0,0 +1,112 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Provides a Lua interpreter, to drive the game_controller.
*
* @note Naming conventions:
* - intf_ functions are exported in the wesnoth domain,
* - impl_ functions are hidden inside metatables,
* - cfun_ functions are closures,
* - luaW_ functions are helpers in Lua style.
*/
#include "scripting/application_lua_kernel.hpp"
#include "global.hpp"
#include "log.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
#include "lua/lualib.h"
#include "scripting/lua_api.hpp"
#include "scripting/lua_common.hpp"
#include "scripting/lua_game_launcher.hpp"
#include "scripting/lua_kernel_base.hpp"
#include "scripting/lua_types.hpp"
#include "log.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
#include "lua/lualib.h"
#ifdef DEBUG_LUA
#include "scripting/debug_lua.hpp"
#endif
#include "scripting/lua_api.hpp"
#include <cstring>
#include <string>
static lg::log_domain log_scripting_lua("scripting/lua");
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
#define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
application_lua_kernel::application_lua_kernel()
: lua_kernel_base()
{}
bool application_lua_kernel::initialize(game_launcher * gl)
{
//if (resources::app_lua_kernel && resources::app_lua_kernel != this) {
// throw "you appear to have multiple application lua kernels, this is bad";
//}
lua_State * L = mState;
lua_game_launcher::define_metatable(L);
lua_game_launcher::add_table(L, gl);
return true;
}
/**
* Sets the current lua script for the wesnoth application
* - Arg 1: A lua function. This should be a closure, essentially an input iterator
* which takes as input tables corresponding to configs. Whenever an event occurs
* this function will be called with the config of that event passed as its argument.
*/
int application_lua_kernel::intf_set_script(lua_State *L) {
lua_pushlightuserdata(L , currentscriptKey); // stack is now [fcn], [key]
lua_insert(L,-2); // stack is now [key], [fcn]
lua_rawset(L, LUA_REGISTRYINDEX); // pair is stored in registry
return 0;
}
/**
* Calls the current script, with given config translated to a table and passed as arg.
*/
void application_lua_kernel::call_script(const config & event_cfg) {
lua_State * L = mState;
lua_pushlightuserdata(L , currentscriptKey);
lua_rawget(L, LUA_REGISTRYINDEX); //get the script from the registry, on the top of the stack
if (lua_type(L, -1) != LUA_TFUNCTION) {
WRN_LUA << "Tried to execute script from registry, but did not retrieve a function. Aborting." << std::endl;
return ;
}
luaW_pushconfig(L, event_cfg); //push the config as an argument
pcall_fcn_ptr pcall = pcall_fcn();
if (!pcall(L, 1, 0)) //call the script from protected mode, there is one argument and we expect no return values.
{
WRN_LUA << "Got an error when executing script:\n" << lua_tostring(L,-1) << std::endl;
}
}

View file

@ -0,0 +1,36 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef SCRIPTING_APP_LUA_KERNEL_HPP
#define SCRIPTING_APP_LUA_KERNEL_HPP
#include "scripting/lua_kernel_base.hpp"
#include <cstring>
#include <string>
class config;
class game_launcher;
class application_lua_kernel : public lua_kernel_base {
public:
application_lua_kernel();
bool initialize(game_launcher* gl);
static int intf_set_script(lua_State * L); /* Registers a lua function as the current script */
void call_script(const config & cfg); /* Call the current script, with config passed as argument */
};
#endif

View file

@ -14,7 +14,7 @@
/**
* @file
* Provides a Lua interpreter.
* Provides a Lua interpreter, to be embedded in WML.
*
* @note Naming conventions:
* - intf_ functions are exported in the wesnoth domain,
@ -23,7 +23,7 @@
* - luaW_ functions are helpers in Lua style.
*/
#include "scripting/lua.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "global.hpp"
@ -84,6 +84,7 @@
#include "reports.hpp" // for register_generator, etc
#include "resources.hpp" // for teams, gameboard, units, etc
#include "scripting/lua_api.hpp" // for luaW_toboolean, etc
#include "scripting/lua_common.hpp"
#include "scripting/lua_types.hpp" // for getunitKey, dlgclbkKey, etc
#include "sdl/utils.hpp" // for surface
#include "serialization/string_utils.hpp" // for string_map
@ -180,316 +181,6 @@ namespace {
("_from_lua", map_location(), map_location(), config());
}//unnamed namespace for queued_event_context
/**
* Creates a t_string object (__call metamethod).
* - Arg 1: userdata containing the domain.
* - Arg 2: string to translate.
* - Ret 1: string containing the translatable string.
*/
static int impl_gettext(lua_State *L)
{
char const *m = luaL_checkstring(L, 2);
char const *d = static_cast<char *>(lua_touserdata(L, 1));
// Hidden metamethod, so d has to be a string. Use it to create a t_string.
luaW_pushtstring(L, t_string(m, d));
return 1;
}
/**
* Creates an interface for gettext
* - Arg 1: string containing the domain.
* - Ret 1: a full userdata with __call pointing to lua_gettext.
*/
static int intf_textdomain(lua_State *L)
{
size_t l;
char const *m = luaL_checklstring(L, 1, &l);
void *p = lua_newuserdata(L, l + 1);
memcpy(p, m, l + 1);
lua_pushlightuserdata(L
, gettextKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_setmetatable(L, -2);
return 1;
}
/**
* Converts a Lua value at position @a src and appends it to @a dst.
* @note This function is private to lua_tstring_concat. It expects two things.
* First, the t_string metatable is at the top of the stack on entry. (It
* is still there on exit.) Second, the caller hasn't any valuable object
* with dynamic lifetime, since they would be leaked on error.
*/
static void tstring_concat_aux(lua_State *L, t_string &dst, int src)
{
switch (lua_type(L, src)) {
case LUA_TNUMBER:
case LUA_TSTRING:
dst += lua_tostring(L, src);
break;
case LUA_TUSERDATA:
// Compare its metatable with t_string's metatable.
if (!lua_getmetatable(L, src) || !lua_rawequal(L, -1, -2))
luaL_typerror(L, src, "string");
dst += *static_cast<t_string *>(lua_touserdata(L, src));
lua_pop(L, 1);
break;
default:
luaL_typerror(L, src, "string");
}
}
/**
* Appends a scalar to a t_string object (__concat metamethod).
*/
static int impl_tstring_concat(lua_State *L)
{
// Create a new t_string.
t_string *t = new(lua_newuserdata(L, sizeof(t_string))) t_string;
lua_pushlightuserdata(L
, tstringKey);
lua_rawget(L, LUA_REGISTRYINDEX);
// Append both arguments to t.
tstring_concat_aux(L, *t, 1);
tstring_concat_aux(L, *t, 2);
lua_setmetatable(L, -2);
return 1;
}
/**
* Destroys a t_string object before it is collected (__gc metamethod).
*/
static int impl_tstring_collect(lua_State *L)
{
t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
t->t_string::~t_string();
return 0;
}
/**
* Converts a t_string object to a string (__tostring metamethod);
* that is, performs a translation.
*/
static int impl_tstring_tostring(lua_State *L)
{
t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
lua_pushstring(L, t->c_str());
return 1;
}
/**
* Gets the parsed field of a vconfig object (_index metamethod).
* Special fields __literal, __shallow_literal, __parsed, and
* __shallow_parsed, return Lua tables.
*/
static int impl_vconfig_get(lua_State *L)
{
vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
if (lua_isnumber(L, 2))
{
vconfig::all_children_iterator i = v->ordered_begin();
unsigned len = std::distance(i, v->ordered_end());
unsigned pos = lua_tointeger(L, 2) - 1;
if (pos >= len) return 0;
std::advance(i, pos);
lua_createtable(L, 2, 0);
lua_pushstring(L, i.get_key().c_str());
lua_rawseti(L, -2, 1);
new(lua_newuserdata(L, sizeof(vconfig))) vconfig(i.get_child());
lua_pushlightuserdata(L
, vconfigKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_setmetatable(L, -2);
lua_rawseti(L, -2, 2);
return 1;
}
char const *m = luaL_checkstring(L, 2);
if (strcmp(m, "__literal") == 0) {
luaW_pushconfig(L, v->get_config());
return 1;
}
if (strcmp(m, "__parsed") == 0) {
luaW_pushconfig(L, v->get_parsed_config());
return 1;
}
bool shallow_literal = strcmp(m, "__shallow_literal") == 0;
if (shallow_literal || strcmp(m, "__shallow_parsed") == 0)
{
lua_newtable(L);
BOOST_FOREACH(const config::attribute &a, v->get_config().attribute_range()) {
if (shallow_literal)
luaW_pushscalar(L, a.second);
else
luaW_pushscalar(L, v->expand(a.first));
lua_setfield(L, -2, a.first.c_str());
}
vconfig::all_children_iterator i = v->ordered_begin(),
i_end = v->ordered_end();
if (shallow_literal) {
i.disable_insertion();
i_end.disable_insertion();
}
for (int j = 1; i != i_end; ++i, ++j)
{
lua_createtable(L, 2, 0);
lua_pushstring(L, i.get_key().c_str());
lua_rawseti(L, -2, 1);
luaW_pushvconfig(L, i.get_child());
lua_rawseti(L, -2, 2);
lua_rawseti(L, -2, j);
}
return 1;
}
if (v->null() || !v->has_attribute(m)) return 0;
luaW_pushscalar(L, (*v)[m]);
return 1;
}
/**
* Returns the number of a child of a vconfig object.
*/
static int impl_vconfig_size(lua_State *L)
{
vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
lua_pushinteger(L, v->null() ? 0 :
std::distance(v->ordered_begin(), v->ordered_end()));
return 1;
}
/**
* Destroys a vconfig object before it is collected (__gc metamethod).
*/
static int impl_vconfig_collect(lua_State *L)
{
vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
v->vconfig::~vconfig();
return 0;
}
#define return_tstring_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
luaW_pushtstring(L, accessor); \
return 1; \
}
#define return_cstring_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushstring(L, accessor); \
return 1; \
}
#define return_string_attrib(name, accessor) \
return_cstring_attrib(name, accessor.c_str())
#define return_int_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushinteger(L, accessor); \
return 1; \
}
#define return_float_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushnumber(L, accessor); \
return 1; \
}
#define return_bool_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushboolean(L, accessor); \
return 1; \
}
#define return_cfg_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
config cfg; \
accessor; \
luaW_pushconfig(L, cfg); \
return 1; \
}
#define return_cfgref_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
luaW_pushconfig(L, accessor); \
return 1; \
}
#define return_vector_string_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
const std::vector<std::string>& vector = accessor; \
lua_createtable(L, vector.size(), 0); \
int i = 1; \
BOOST_FOREACH(const std::string& s, vector) { \
lua_pushstring(L, s.c_str()); \
lua_rawseti(L, -2, i); \
++i; \
} \
return 1; \
}
#define modify_tstring_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
t_string value = luaW_checktstring(L, 3); \
accessor; \
return 0; \
}
#define modify_string_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
const char *value = luaL_checkstring(L, 3); \
accessor; \
return 0; \
}
#define modify_int_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
int value = luaL_checkinteger(L, 3); \
accessor; \
return 0; \
}
#define modify_int_attrib_check_range(name, accessor, allowed_min, allowed_max) \
if (strcmp(m, name) == 0) { \
int value = luaL_checkinteger(L, 3); \
if (value < allowed_min || allowed_max < value) return luaL_argerror(L, 3, "out of bounds"); \
accessor; \
return 0; \
}
#define modify_bool_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
bool value = luaW_toboolean(L, 3); \
accessor; \
return 0; \
}
#define modify_vector_string_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
std::vector<std::string> vector; \
char const* message = "table with unnamed indices holding strings expected"; \
if (!lua_istable(L, 3)) return luaL_argerror(L, 3, message); \
unsigned length = lua_rawlen(L, 3); \
for (unsigned i = 1; i <= length; ++i) { \
lua_rawgeti(L, 3, i); \
char const* string = lua_tostring(L, 4); \
if(!string) return luaL_argerror(L, 2 + i, message); \
vector.push_back(string); \
lua_pop(L, 1); \
} \
accessor; \
return 0; \
}
/**
* Gets some data on a unit type (__index metamethod).
* - Arg 1: table containing an "id" field.
@ -3764,27 +3455,10 @@ static int intf_get_all_vars(lua_State *L) {
}
LuaKernel::LuaKernel(const config &cfg)
: mState(luaL_newstate()), level_(cfg)
: lua_kernel_base(), level_(cfg)
{
lua_State *L = mState;
// Open safe libraries.
// Debug and OS are not, but most of their functions will be disabled below.
static const luaL_Reg safe_libs[] = {
{ "", luaopen_base },
{ "table", luaopen_table },
{ "string", luaopen_string },
{ "math", luaopen_math },
{ "debug", luaopen_debug },
{ "os", luaopen_os },
{ NULL, NULL }
};
for (luaL_Reg const *lib = safe_libs; lib->func; ++lib)
{
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1); /* remove lib */
}
// Put some callback functions in the scripting environment.
static luaL_Reg const callbacks[] = {
{ "add_known_unit", &intf_add_known_unit },
@ -3856,8 +3530,8 @@ LuaKernel::LuaKernel(const config &cfg)
{ "show_dialog", &intf_show_dialog },
{ "simulate_combat", &intf_simulate_combat },
{ "synchronize_choice", &intf_synchronize_choice },
{ "textdomain", &intf_textdomain },
{ "tovconfig", &intf_tovconfig },
{ "textdomain", &lua_common::intf_textdomain },
{ "tovconfig", &intf_tovconfig },
{ "transform_unit", &intf_transform_unit },
{ "unit_ability", &intf_unit_ability },
{ "unit_defense", &intf_unit_defense },
@ -3884,7 +3558,7 @@ LuaKernel::LuaKernel(const config &cfg)
lua_pushlightuserdata(L
, gettextKey);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, impl_gettext);
lua_pushcfunction(L, lua_common::impl_gettext);
lua_setfield(L, -2, "__call");
lua_pushstring(L, "message domain");
lua_setfield(L, -2, "__metatable");
@ -3930,11 +3604,11 @@ LuaKernel::LuaKernel(const config &cfg)
lua_pushlightuserdata(L
, tstringKey);
lua_createtable(L, 0, 4);
lua_pushcfunction(L, impl_tstring_concat);
lua_pushcfunction(L, lua_common::impl_tstring_concat);
lua_setfield(L, -2, "__concat");
lua_pushcfunction(L, impl_tstring_collect);
lua_pushcfunction(L, lua_common::impl_tstring_collect);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, impl_tstring_tostring);
lua_pushcfunction(L, lua_common::impl_tstring_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushstring(L, "translatable string");
lua_setfield(L, -2, "__metatable");
@ -3968,11 +3642,11 @@ LuaKernel::LuaKernel(const config &cfg)
lua_pushlightuserdata(L
, vconfigKey);
lua_createtable(L, 0, 4);
lua_pushcfunction(L, impl_vconfig_collect);
lua_pushcfunction(L, lua_common::impl_vconfig_collect);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, impl_vconfig_get);
lua_pushcfunction(L, lua_common::impl_vconfig_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, impl_vconfig_size);
lua_pushcfunction(L, lua_common::impl_vconfig_size);
lua_setfield(L, -2, "__len");
lua_pushstring(L, "wml object");
lua_setfield(L, -2, "__metatable");
@ -4051,31 +3725,6 @@ LuaKernel::LuaKernel(const config &cfg)
lua_remove(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
// Disable functions from os which we don't want.
lua_getglobal(L, "os");
lua_pushnil(L);
while(lua_next(L, -2) != 0) {
lua_pop(L, 1);
char const* function = lua_tostring(L, -1);
if(strcmp(function, "clock") == 0 || strcmp(function, "date") == 0
|| strcmp(function, "time") == 0 || strcmp(function, "difftime") == 0) continue;
lua_pushnil(L);
lua_setfield(L, -3, function);
}
lua_pop(L, 1);
// Disable functions from debug which we don't want.
lua_getglobal(L, "debug");
lua_pushnil(L);
while(lua_next(L, -2) != 0) {
lua_pop(L, 1);
char const* function = lua_tostring(L, -1);
if(strcmp(function, "traceback") == 0) continue;
lua_pushnil(L);
lua_setfield(L, -3, function);
}
lua_pop(L, 1);
lua_settop(L, 0);
}
@ -4263,11 +3912,6 @@ bool LuaKernel::run_event(game_events::queued_event const &ev)
return true;
}
LuaKernel::~LuaKernel()
{
lua_close(mState);
}
/**
* Executes its upvalue as a wml action.
*/
@ -4354,45 +3998,12 @@ bool LuaKernel::run_filter(char const *name, unit const &u)
return b;
}
/**
* Runs a script on a stack containing @a nArgs arguments.
* @return true if the script was successful and @a nRets return values are available.
*/
bool LuaKernel::execute(char const *prog, int nArgs, int nRets)
{
lua_State *L = mState;
// Compile script into a variadic function.
int res = luaL_loadstring(L, prog);
if (res)
{
char const *m = lua_tostring(L, -1);
chat_message("Lua error", m);
ERR_LUA << m << '\n';
lua_pop(L, 1);
return false;
}
// Place the function before its arguments.
if (nArgs)
lua_insert(L, -1 - nArgs);
return luaW_pcall(L, nArgs, nRets);
// This is needed because default args don't work through function pointers
static int luaW_pcall_default_args(lua_State * L, int a, int b) {
return luaW_pcall(L,a,b,false);
}
/**
* Loads the "package" package into the Lua environment.
* This action is inherently unsafe, as Lua scripts will now be able to
* load C libraries on their own, hence granting them the same privileges
* as the Wesnoth binary itsef.
*/
void LuaKernel::load_package()
{
lua_State *L = mState;
lua_pushcfunction(L, luaopen_package);
lua_pushstring(L, "package");
lua_call(L, 1, 0);
}
pcall_fcn_ptr LuaKernel::pcall_fcn() { return &luaW_pcall_default_args; } //this causes the run() function to use luaW_pcall instead of lua_pcall
ai::lua_ai_context* LuaKernel::create_lua_ai_context(char const *code, ai::engine_lua *engine)
{

View file

@ -15,6 +15,8 @@
#ifndef SCRIPTING_LUA_HPP
#define SCRIPTING_LUA_HPP
#include "scripting/lua_kernel_base.hpp" // for lua_kernel_base
#include "game_events/action_wml.hpp" // for wml_action, etc
#include <string> // for string
@ -30,14 +32,12 @@ struct lua_State;
void extract_preload_scripts(config const &);
class LuaKernel
class LuaKernel : public lua_kernel_base
{
lua_State *mState;
const config &level_;
bool execute(char const *, int, int);
public:
LuaKernel(const config &);
~LuaKernel();
void initialize();
void save_game(config &);
void load_game();
@ -46,11 +46,11 @@ public:
bool run_wml_action(std::string const &, vconfig const &,
game_events::queued_event const &);
bool run_filter(char const *name, unit const &u);
/** Runs a plain script. */
void run(char const *prog) { execute(prog, 0, 0); }
virtual pcall_fcn_ptr pcall_fcn();
ai::lua_ai_context* create_lua_ai_context(char const *code, ai::engine_lua *engine);
ai::lua_ai_action_handler* create_lua_ai_action_handler(char const *code, ai::lua_ai_context &context);
void load_package();
};
#endif

View file

@ -41,6 +41,7 @@ static lg::log_domain log_scripting_lua("scripting/lua");
void chat_message(std::string const &caption, std::string const &msg)
{
if (!resources::screen) return;
resources::screen->get_chat_manager().add_chat_message(time(NULL), caption, 0, msg,
events::chat_handler::MESSAGE_PUBLIC, false);
}

View file

@ -0,0 +1,235 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Contains code common to the application and game lua kernels which
* cannot or should not go into the lua kernel base files.
*
* Currently contains implementation functions related to vconfig and
* gettext, also some macros to assist in writing C lua callbacks.
*/
#include "scripting/lua_common.hpp"
#include "global.hpp"
#include "config.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
#include "scripting/lua_api.hpp"
#include "variable.hpp" // for vconfig
#include <boost/foreach.hpp>
#include <cstring>
namespace lua_common {
/**
* Creates a t_string object (__call metamethod).
* - Arg 1: userdata containing the domain.
* - Arg 2: string to translate.
* - Ret 1: string containing the translatable string.
*/
int impl_gettext(lua_State *L)
{
char const *m = luaL_checkstring(L, 2);
char const *d = static_cast<char *>(lua_touserdata(L, 1));
// Hidden metamethod, so d has to be a string. Use it to create a t_string.
luaW_pushtstring(L, t_string(m, d));
return 1;
}
/**
* Creates an interface for gettext
* - Arg 1: string containing the domain.
* - Ret 1: a full userdata with __call pointing to lua_gettext.
*/
int intf_textdomain(lua_State *L)
{
size_t l;
char const *m = luaL_checklstring(L, 1, &l);
void *p = lua_newuserdata(L, l + 1);
memcpy(p, m, l + 1);
lua_pushlightuserdata(L
, gettextKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_setmetatable(L, -2);
return 1;
}
/**
* Converts a Lua value at position @a src and appends it to @a dst.
* @note This function is private to lua_tstring_concat. It expects two things.
* First, the t_string metatable is at the top of the stack on entry. (It
* is still there on exit.) Second, the caller hasn't any valuable object
* with dynamic lifetime, since they would be leaked on error.
*/
static void tstring_concat_aux(lua_State *L, t_string &dst, int src)
{
switch (lua_type(L, src)) {
case LUA_TNUMBER:
case LUA_TSTRING:
dst += lua_tostring(L, src);
break;
case LUA_TUSERDATA:
// Compare its metatable with t_string's metatable.
if (!lua_getmetatable(L, src) || !lua_rawequal(L, -1, -2))
luaL_typerror(L, src, "string");
dst += *static_cast<t_string *>(lua_touserdata(L, src));
lua_pop(L, 1);
break;
default:
luaL_typerror(L, src, "string");
}
}
/**
* Appends a scalar to a t_string object (__concat metamethod).
*/
int impl_tstring_concat(lua_State *L)
{
// Create a new t_string.
t_string *t = new(lua_newuserdata(L, sizeof(t_string))) t_string;
lua_pushlightuserdata(L
, tstringKey);
lua_rawget(L, LUA_REGISTRYINDEX);
// Append both arguments to t.
tstring_concat_aux(L, *t, 1);
tstring_concat_aux(L, *t, 2);
lua_setmetatable(L, -2);
return 1;
}
/**
* Destroys a t_string object before it is collected (__gc metamethod).
*/
int impl_tstring_collect(lua_State *L)
{
t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
t->t_string::~t_string();
return 0;
}
/**
* Converts a t_string object to a string (__tostring metamethod);
* that is, performs a translation.
*/
int impl_tstring_tostring(lua_State *L)
{
t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
lua_pushstring(L, t->c_str());
return 1;
}
/**
* Gets the parsed field of a vconfig object (_index metamethod).
* Special fields __literal, __shallow_literal, __parsed, and
* __shallow_parsed, return Lua tables.
*/
int impl_vconfig_get(lua_State *L)
{
vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
if (lua_isnumber(L, 2))
{
vconfig::all_children_iterator i = v->ordered_begin();
unsigned len = std::distance(i, v->ordered_end());
unsigned pos = lua_tointeger(L, 2) - 1;
if (pos >= len) return 0;
std::advance(i, pos);
lua_createtable(L, 2, 0);
lua_pushstring(L, i.get_key().c_str());
lua_rawseti(L, -2, 1);
new(lua_newuserdata(L, sizeof(vconfig))) vconfig(i.get_child());
lua_pushlightuserdata(L
, vconfigKey);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_setmetatable(L, -2);
lua_rawseti(L, -2, 2);
return 1;
}
char const *m = luaL_checkstring(L, 2);
if (strcmp(m, "__literal") == 0) {
luaW_pushconfig(L, v->get_config());
return 1;
}
if (strcmp(m, "__parsed") == 0) {
luaW_pushconfig(L, v->get_parsed_config());
return 1;
}
bool shallow_literal = strcmp(m, "__shallow_literal") == 0;
if (shallow_literal || strcmp(m, "__shallow_parsed") == 0)
{
lua_newtable(L);
BOOST_FOREACH(const config::attribute &a, v->get_config().attribute_range()) {
if (shallow_literal)
luaW_pushscalar(L, a.second);
else
luaW_pushscalar(L, v->expand(a.first));
lua_setfield(L, -2, a.first.c_str());
}
vconfig::all_children_iterator i = v->ordered_begin(),
i_end = v->ordered_end();
if (shallow_literal) {
i.disable_insertion();
i_end.disable_insertion();
}
for (int j = 1; i != i_end; ++i, ++j)
{
lua_createtable(L, 2, 0);
lua_pushstring(L, i.get_key().c_str());
lua_rawseti(L, -2, 1);
luaW_pushvconfig(L, i.get_child());
lua_rawseti(L, -2, 2);
lua_rawseti(L, -2, j);
}
return 1;
}
if (v->null() || !v->has_attribute(m)) return 0;
luaW_pushscalar(L, (*v)[m]);
return 1;
}
/**
* Returns the number of a child of a vconfig object.
*/
int impl_vconfig_size(lua_State *L)
{
vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
lua_pushinteger(L, v->null() ? 0 :
std::distance(v->ordered_begin(), v->ordered_end()));
return 1;
}
/**
* Destroys a vconfig object before it is collected (__gc metamethod).
*/
int impl_vconfig_collect(lua_State *L)
{
vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
v->vconfig::~vconfig();
return 0;
}
} // end namespace lua_common

View file

@ -0,0 +1,140 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
struct lua_State;
namespace lua_common {
int impl_gettext(lua_State *L);
int intf_textdomain(lua_State *L);
int impl_tstring_concat(lua_State *L);
int impl_tstring_collect(lua_State *L);
int impl_tstring_tostring(lua_State *L);
int impl_vconfig_get(lua_State *L);
int impl_vconfig_size(lua_State *L);
int impl_vconfig_collect(lua_State *L);
}
#define return_tstring_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
luaW_pushtstring(L, accessor); \
return 1; \
}
#define return_cstring_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushstring(L, accessor); \
return 1; \
}
#define return_string_attrib(name, accessor) \
return_cstring_attrib(name, accessor.c_str())
#define return_int_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushinteger(L, accessor); \
return 1; \
}
#define return_float_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushnumber(L, accessor); \
return 1; \
}
#define return_bool_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
lua_pushboolean(L, accessor); \
return 1; \
}
#define return_cfg_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
config cfg; \
accessor; \
luaW_pushconfig(L, cfg); \
return 1; \
}
#define return_cfgref_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
luaW_pushconfig(L, accessor); \
return 1; \
}
#define return_vector_string_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
const std::vector<std::string>& vector = accessor; \
lua_createtable(L, vector.size(), 0); \
int i = 1; \
BOOST_FOREACH(const std::string& s, vector) { \
lua_pushstring(L, s.c_str()); \
lua_rawseti(L, -2, i); \
++i; \
} \
return 1; \
}
#define modify_tstring_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
t_string value = luaW_checktstring(L, 3); \
accessor; \
return 0; \
}
#define modify_string_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
const char *value = luaL_checkstring(L, 3); \
accessor; \
return 0; \
}
#define modify_int_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
int value = luaL_checkinteger(L, 3); \
accessor; \
return 0; \
}
#define modify_int_attrib_check_range(name, accessor, allowed_min, allowed_max) \
if (strcmp(m, name) == 0) { \
int value = luaL_checkinteger(L, 3); \
if (value < allowed_min || allowed_max < value) return luaL_argerror(L, 3, "out of bounds"); \
accessor; \
return 0; \
}
#define modify_bool_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
bool value = luaW_toboolean(L, 3); \
accessor; \
return 0; \
}
#define modify_vector_string_attrib(name, accessor) \
if (strcmp(m, name) == 0) { \
std::vector<std::string> vector; \
char const* message = "table with unnamed indices holding strings expected"; \
if (!lua_istable(L, 3)) return luaL_argerror(L, 3, message); \
unsigned length = lua_rawlen(L, 3); \
for (unsigned i = 1; i <= length; ++i) { \
lua_rawgeti(L, 3, i); \
char const* string = lua_tostring(L, 4); \
if(!string) return luaL_argerror(L, 2 + i, message); \
vector.push_back(string); \
lua_pop(L, 1); \
} \
accessor; \
return 0; \
}

View file

@ -0,0 +1,147 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Provides a lua proxy for the game launcher object.
*
* @note Naming conventions:
* - intf_ functions are exported in the wesnoth domain,
* - impl_ functions are hidden inside metatables,
* - cfun_ functions are closures,
* - luaW_ functions are helpers in Lua style.
*
* This lua object is arranged as follows:
*
* - Type is an empty table
* - metatable set to the gamelauncher metatable, which holds all of the callbacks,
* and the __newindex method. It is set to be it's own __index, so the callbacks are accessible.
* - gamelauncher metatable has as its metatable a single "helper" metatable, whose __index is
* a C function defined below.
*
* The game_launcher object is not light user data and does not itself hold a pointer to the
* underlying C object. There is a static pointer in this file that points to the most recent
* game launcher. This slightly eases the syntax when using the lua gamelauncher object -- it's
* never necessary to use a : when calling member functions, and still it allows us to have all of the
* callbacks and metamethods have access to the pointer when they are called.
*/
#include "scripting/lua_game_launcher.hpp"
#include "global.hpp"
#include "commandline_options.hpp"
#include "game_launcher.hpp"
#include "log.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
#include "lua/lualib.h"
#include "scripting/application_lua_kernel.hpp"
#include "scripting/lua_api.hpp"
#include "scripting/lua_common.hpp"
#include "scripting/lua_types.hpp"
static lg::log_domain log_scripting_lua("scripting/lua");
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
#define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
namespace lua_game_launcher {
static const char* metatable_name = "gamelauncher";
static game_launcher * gl_ = NULL;
/**
* Get info from game launcher (_index metamethod)*
* - Arg 1: table corresponding to gamelauncher.
* - Arg 2: string containing the name of the property.
* - Ret 1: something containing the attribute.
*/
static int impl_gamelauncher_get(lua_State* L) {
if (!gl_) return 0;
char const *m = luaL_checkstring(L, -1);
return_string_attrib("version", game_config::version);
return_cfgref_attrib("commandline_opts", gl_->opts().to_config());
return 0;
}
static int impl_gamelauncher_set(lua_State* L) {
ERR_LUA << "you cannot write to the fields of gamelauncher" << std::endl;
lua_error(L);
return 0;
}
/* Open a server connection */
static int intf_play_multiplayer(lua_State* /*L*/) {
if (!gl_) return 0;
gl_->play_multiplayer();
return 0;
}
static void push_helper_metatable(lua_State* L)
{
lua_createtable(L, 0, 2);
lua_pushcfunction(L, impl_gamelauncher_get);
lua_setfield(L, -2, "__index");
lua_pushstring(L, "game launcher helper");
lua_setfield(L, -2, "__metatable");
}
void define_metatable(lua_State* L)
{
// Create the gamelauncher metatable.
luaL_newmetatable(L, metatable_name);
//Put some callback functions in the scripting environment.
static luaL_Reg const callbacks[] = {
{ "play_multiplayer", &intf_play_multiplayer},
{ "set_script", &application_lua_kernel::intf_set_script},
{ "__newindex", &impl_gamelauncher_set},
{ NULL, NULL }
};
luaL_setfuncs(L, callbacks, 0);
lua_pushvalue(L, -1); //make a copy of this table, set it to be its own __index table
lua_setfield(L, -2, "__index");
push_helper_metatable(L);
lua_setmetatable(L, -2);
lua_pop(L,1);
}
/* Adds an instance of game launcher to the lua environment */
void add_table(lua_State* L, game_launcher * gl)
{
gl_ = gl;
//Set gl as the _pointer field
lua_createtable(L, 0, 1);
luaL_getmetatable(L,metatable_name);
lua_setmetatable(L,-2);
//if (!lua_islightuserdata(L, -1)) {
// ERR_LUA << "pointer was not light userdata! this indicates something wrong with lua api, pushlightuserdata or islightuserdata" << std::endl;
//}
lua_setglobal(L, "game_launcher");
}
} // end namespace lua_game_launcher

View file

@ -0,0 +1,26 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef SCRIPTING_LUA_GAME_LAUNCHER_HPP
#define SCRIPTING_LUA_GAME_LAUNCHER_HPP
class game_launcher;
struct lua_State;
namespace lua_game_launcher {
void define_metatable(lua_State* L); /* Defines the metatable of the game launcher and adds to the registry */
void add_table(lua_State* L, game_launcher * gl); /* Adds an instance of game launcher to the lua environment */
}
#endif

View file

@ -0,0 +1,154 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "scripting/lua_kernel_base.hpp"
#include "global.hpp"
#include "log.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
#include "lua/lualib.h"
#ifdef DEBUG_LUA
#include "scripting/debug_lua.hpp"
#endif
#include "scripting/lua_api.hpp"
#include <cstring>
#include <string>
static lg::log_domain log_scripting_lua("scripting/lua");
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
#define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
lua_kernel_base::lua_kernel_base()
: mState(luaL_newstate())
{
lua_State *L = mState;
// Open safe libraries.
// Debug and OS are not, but most of their functions will be disabled below.
static const luaL_Reg safe_libs[] = {
{ "", luaopen_base },
{ "table", luaopen_table },
{ "string", luaopen_string },
{ "math", luaopen_math },
{ "debug", luaopen_debug },
{ "os", luaopen_os },
{ NULL, NULL }
};
for (luaL_Reg const *lib = safe_libs; lib->func; ++lib)
{
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1); /* remove lib */
}
// Disable functions from os which we don't want.
lua_getglobal(L, "os");
lua_pushnil(L);
while(lua_next(L, -2) != 0) {
lua_pop(L, 1);
char const* function = lua_tostring(L, -1);
if(strcmp(function, "clock") == 0 || strcmp(function, "date") == 0
|| strcmp(function, "time") == 0 || strcmp(function, "difftime") == 0) continue;
lua_pushnil(L);
lua_setfield(L, -3, function);
}
lua_pop(L, 1);
// Disable functions from debug which we don't want.
lua_getglobal(L, "debug");
lua_pushnil(L);
while(lua_next(L, -2) != 0) {
lua_pop(L, 1);
char const* function = lua_tostring(L, -1);
if(strcmp(function, "traceback") == 0) continue;
lua_pushnil(L);
lua_setfield(L, -3, function);
}
lua_pop(L, 1);
lua_settop(L, 0);
}
lua_kernel_base::~lua_kernel_base()
{
lua_close(mState);
}
// This is needed because lua_pcall is actually a macro
static int lua_pcall_fcn(lua_State * L, int a, int b)
{
return lua_pcall(L,a,b,0);
}
// In the "base" configuration we just want to call lua_pcall when running scripts.
pcall_fcn_ptr lua_kernel_base::pcall_fcn() { return &lua_pcall_fcn; }
/**
* Runs a script on a stack containing @a nArgs arguments.
* @return true if the script was successful and @a nRets return values are available.
*/
bool lua_kernel_base::execute(char const *prog, int nArgs, int nRets)
{
lua_State *L = mState;
// Compile script into a variadic function.
int res = luaL_loadstring(L, prog);
if (res)
{
char const *m = lua_tostring(L, -1);
chat_message("Lua error", m);
ERR_LUA << m << '\n';
lua_pop(L, 1);
return true;
}
// Place the function before its arguments.
if (nArgs)
lua_insert(L, -1 - nArgs);
pcall_fcn_ptr f = pcall_fcn();
return f(L, nArgs, nRets);
}
void lua_kernel_base::run(const char * prog) {
if (execute(prog, 0, 0)) {
lua_State *L = mState;
char const *m = lua_tostring(L, -1);
ERR_LUA << "lua_kernel::run(): " << m << '\n';
lua_pop(L,1);
//execute("print(debug.traceback())",0,0);
}
}
/**
* Loads the "package" package into the Lua environment.
* This action is inherently unsafe, as Lua scripts will now be able to
* load C libraries on their own, hence granting them the same privileges
* as the Wesnoth binary itsef.
*/
void lua_kernel_base::load_package()
{
lua_State *L = mState;
lua_pushcfunction(L, luaopen_package);
lua_pushstring(L, "package");
lua_call(L, 1, 0);
}

View file

@ -0,0 +1,40 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef SCRIPTING_LUA_KERNEL_BASE_HPP
#define SCRIPTING_LUA_KERNEL_BASE_HPP
#include <string> // for string
struct lua_State;
typedef int (*pcall_fcn_ptr)(lua_State *, int, int);
class lua_kernel_base {
protected:
lua_State *mState;
bool execute(char const *, int, int);
public:
lua_kernel_base();
virtual ~lua_kernel_base();
/** Runs a plain script. */
void run(char const *prog);
void load_package();
virtual pcall_fcn_ptr pcall_fcn(); //when running scripts, in the "base" kernel type we should just use pcall. But for the in-game kernel, we want to call the luaW_pcall function instead which extends it using things specific to that api, and returns errors on a WML channel
};
#endif

View file

@ -26,6 +26,7 @@ static char const v_tstringKey = 0;
static char const v_unitvarKey = 0;
static char const v_ustatusKey = 0;
static char const v_vconfigKey = 0;
static char const v_currentscriptKey = 0;
luatypekey const dlgclbkKey = static_cast<void *>(const_cast<char *>(&v_dlgclbkKey));
@ -39,3 +40,4 @@ luatypekey const tstringKey = static_cast<void *>(const_cast<char *>(&v_tstringK
luatypekey const unitvarKey = static_cast<void *>(const_cast<char *>(&v_unitvarKey));
luatypekey const ustatusKey = static_cast<void *>(const_cast<char *>(&v_ustatusKey));
luatypekey const vconfigKey = static_cast<void *>(const_cast<char *>(&v_vconfigKey));
luatypekey const currentscriptKey = static_cast<void *>(const_cast<char *>(&v_currentscriptKey));

View file

@ -12,6 +12,9 @@
See the COPYING file for more details.
*/
#ifndef SCRIPTING_LUA_TYPES_HPP
#define SCRIPTING_LUA_TYPES_HPP
typedef void* luatypekey;
// i dont want to cast to void* each time ....
@ -27,3 +30,6 @@ extern luatypekey const tstringKey;
extern luatypekey const unitvarKey;
extern luatypekey const ustatusKey;
extern luatypekey const vconfigKey;
extern luatypekey const currentscriptKey;
#endif

View file

@ -34,7 +34,7 @@
#include "map.hpp" // for gamemap
#include "random_new.hpp" // for generator, rng
#include "resources.hpp" // for units, gameboard, teams, etc
#include "scripting/lua.hpp" // for LuaKernel
#include "scripting/game_lua_kernel.hpp" // for LuaKernel
#include "side_filter.hpp" // for side_filter
#include "team.hpp" // for team, get_teams, etc
#include "terrain_filter.hpp" // for terrain_filter

View file

@ -24,7 +24,7 @@
#include "make_enum.hpp"
#include "map_location.hpp"
#include "resources.hpp" //Needed for lua kernel pointer
#include "scripting/lua.hpp" //Needed for lua kernel
#include "scripting/game_lua_kernel.hpp" //Needed for lua kernel
#include "side_filter.hpp"
#include "team.hpp"
#include "terrain_filter.hpp"

View file

@ -508,6 +508,23 @@ static void warn_early_init_failure()
<< "in the command line with the --data-dir switch or as the only argument.\n";
}
/**
* Handles the lua script command line arguments if present.
* This function will only run once.
*/
static void handle_lua_script_args(game_launcher * game, commandline_options & cmdline_opts)
{
static bool first_time = true;
if (!first_time) return;
first_time = false;
if (cmdline_opts.script_file && !game->init_lua_script()) {
std::cerr << "could not load lua script: " << *cmdline_opts.script_file << std::endl;
}
}
/**
* Setups the game environment and enters
* the titlescreen or game loops.
@ -625,6 +642,8 @@ static int do_gameloop(const std::vector<std::string>& args)
loadscreen_manager.reset();
handle_lua_script_args(&*game,cmdline_opts);
if(cmdline_opts.unit_test) {
if(cmdline_opts.timeout) {
std::cerr << "The wesnoth built-in timeout feature has been removed.\n" << std::endl;