Skip to content

Commit

Permalink
add application lua kernel and game launcher lua wrapper object
Browse files Browse the repository at this point in the history
The application lua kernel is meant to interpret scripts to drive
the client. Its main features are, it stores a script in its
registry and provides a C++ function to call it with a config
as its argument. Its initialization also creates a "game_launcher"
object, which stands as a limited lua proxy for the C++
game_launcher object.

The game_launcher object is a table, holding a pointer to the
actual game_launcher it represents, and with metamethods to return
version info and the command line arguments. It also holds
callbacks to set the script for the current application lua kernel,
and to call the "play_multiplayer" function of game_launcher.
Currently it is readonly and you cannot write to its fields,
although that may change to allow the target server to be reset,
etc.

Some minor changes that are included in this commit:
- the actual C++ game launcher object provides an accessor to the
command line options.
- the scripting/lua_types.hpp file now has include guards
  • Loading branch information
cbeck88 committed Nov 10, 2014
1 parent cd3e8cf commit ecc714e
Show file tree
Hide file tree
Showing 14 changed files with 376 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -935,10 +935,12 @@ set(wesnoth-main_SRC
save_index.cpp
saved_game.cpp
savegame.cpp
scripting/application_lua_kernel.cpp
scripting/debug_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
Expand Down
2 changes: 2 additions & 0 deletions src/SConscript
Expand Up @@ -519,10 +519,12 @@ wesnoth_sources = Split("""
save_index.cpp
saved_game.cpp
savegame.cpp
scripting/application_lua_kernel.cpp
scripting/debug_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
Expand Down
2 changes: 2 additions & 0 deletions src/game_launcher.hpp
Expand Up @@ -94,6 +94,8 @@ class game_launcher
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&);
Expand Down
112 changes: 112 additions & 0 deletions src/scripting/application_lua_kernel.cpp
@@ -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;
}
}
36 changes: 36 additions & 0 deletions src/scripting/application_lua_kernel.hpp
@@ -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
7 changes: 7 additions & 0 deletions src/scripting/game_lua_kernel.cpp
Expand Up @@ -3998,6 +3998,13 @@ bool LuaKernel::run_filter(char const *name, unit const &u)
return b;
}

// 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);
}

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)
{
return ai::lua_ai_context::create(mState,code,engine);
Expand Down
2 changes: 2 additions & 0 deletions src/scripting/game_lua_kernel.hpp
Expand Up @@ -47,6 +47,8 @@ class LuaKernel : public lua_kernel_base
game_events::queued_event const &);
bool run_filter(char const *name, unit const &u);

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);
};
Expand Down
1 change: 1 addition & 0 deletions src/scripting/lua_api.cpp
Expand Up @@ -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);
}
Expand Down
147 changes: 147 additions & 0 deletions src/scripting/lua_game_launcher.cpp
@@ -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

26 changes: 26 additions & 0 deletions src/scripting/lua_game_launcher.hpp
@@ -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

0 comments on commit ecc714e

Please sign in to comment.