Allow plugins to export functions to lua with safe reload support.

- To ensure reload safety functions have to be wrapped. Every call
  checks the loaded state and locks a mutex in Plugin. If the plugin
  is unloaded, calling its functions throws a lua error. Therefore,
  plugins may not create closures or export yieldable functions.

- The set of function argument and return types supported by
  LuaWrapper is severely limited when compared to being compiled
  inside the main library.
  Currently supported types: numbers, bool, std::string, df::foo,
  df::foo*, std::vector<bool>, std::vector<df::foo*>.

- To facilitate postponing initialization until after all plugins
  have been loaded, the core sends a SC_CORE_INITIALIZED event.

- As an example, the burrows plugin now exports its functions.
develop
Alexander Gavrilov 2012-04-14 19:44:07 +04:00
parent 7a34a89f53
commit cb49c92b99
13 changed files with 298 additions and 39 deletions

@ -861,13 +861,9 @@ int Core::TileUpdate()
// should always be from simulation thread! // should always be from simulation thread!
int Core::Update() int Core::Update()
{ {
if(!started)
Init();
if(errorstate) if(errorstate)
return -1; return -1;
color_ostream_proxy out(con);
// Pretend this thread has suspended the core in the usual way // Pretend this thread has suspended the core in the usual way
{ {
lock_guard<mutex> lock(d->AccessMutex); lock_guard<mutex> lock(d->AccessMutex);
@ -877,6 +873,22 @@ int Core::Update()
d->df_suspend_depth = 1000; d->df_suspend_depth = 1000;
} }
// Initialize the core
bool first_update = false;
if(!started)
{
first_update = true;
Init();
if(errorstate)
return -1;
}
color_ostream_proxy out(con);
if (first_update)
plug_mgr->OnStateChange(out, SC_CORE_INITIALIZED);
// detect if the game was loaded or unloaded in the meantime // detect if the game was loaded or unloaded in the meantime
void *new_wdata = NULL; void *new_wdata = NULL;
void *new_mapdata = NULL; void *new_mapdata = NULL;

@ -745,6 +745,22 @@ static int lua_dfhack_with_suspend(lua_State *L)
return lua_gettop(L); return lua_gettop(L);
} }
static int dfhack_open_plugin(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checktype(L, 2, LUA_TSTRING);
const char *name = lua_tostring(L, 2);
PluginManager *pmgr = Core::getInstance().getPluginManager();
Plugin *plugin = pmgr->getPluginByName(name);
if (!plugin)
luaL_error(L, "plugin not found: '%s'", name);
plugin->open_lua(L, 1);
return 0;
}
static const luaL_Reg dfhack_funcs[] = { static const luaL_Reg dfhack_funcs[] = {
{ "print", lua_dfhack_print }, { "print", lua_dfhack_print },
{ "println", lua_dfhack_println }, { "println", lua_dfhack_println },
@ -757,6 +773,7 @@ static const luaL_Reg dfhack_funcs[] = {
{ "onerror", dfhack_onerror }, { "onerror", dfhack_onerror },
{ "call_with_finalizer", dfhack_call_with_finalizer }, { "call_with_finalizer", dfhack_call_with_finalizer },
{ "with_suspend", lua_dfhack_with_suspend }, { "with_suspend", lua_dfhack_with_suspend },
{ "open_plugin", dfhack_open_plugin },
{ NULL, NULL } { NULL, NULL }
}; };

@ -1030,6 +1030,12 @@ static int meta_global_newindex(lua_State *state)
static int meta_call_function(lua_State *state) static int meta_call_function(lua_State *state)
{ {
auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID); auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID);
return method_wrapper_core(state, id);
}
int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id)
{
if (lua_gettop(state) != id->getNumArgs()) if (lua_gettop(state) != id->getNumArgs())
field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke"); field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke");

@ -32,6 +32,8 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
#include "MiscUtils.h" #include "MiscUtils.h"
#include "LuaWrapper.h"
using namespace DFHack; using namespace DFHack;
#include <string> #include <string>
@ -107,8 +109,8 @@ struct Plugin::RefLock
void lock_sub() void lock_sub()
{ {
mut->lock(); mut->lock();
refcount --; if (--refcount == 0)
wakeup->notify_one(); wakeup->notify_one();
mut->unlock(); mut->unlock();
} }
void wait() void wait()
@ -130,6 +132,13 @@ struct Plugin::RefAutolock
~RefAutolock(){ lock->unlock(); }; ~RefAutolock(){ lock->unlock(); };
}; };
struct Plugin::RefAutoinc
{
RefLock * lock;
RefAutoinc(RefLock * lck):lock(lck){ lock->lock_add(); };
~RefAutoinc(){ lock->lock_sub(); };
};
Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm) Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm)
{ {
filename = filepath; filename = filepath;
@ -210,6 +219,7 @@ bool Plugin::load(color_ostream &con)
plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown"); plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown");
plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange"); plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange");
plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect"); plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect");
index_lua(plug);
this->name = *plug_name; this->name = *plug_name;
plugin_lib = plug; plugin_lib = plug;
commands.clear(); commands.clear();
@ -222,6 +232,7 @@ bool Plugin::load(color_ostream &con)
else else
{ {
con.printerr("Plugin %s has failed to initialize properly.\n", filename.c_str()); con.printerr("Plugin %s has failed to initialize properly.\n", filename.c_str());
reset_lua();
ClosePlugin(plugin_lib); ClosePlugin(plugin_lib);
state = PS_BROKEN; state = PS_BROKEN;
return false; return false;
@ -235,13 +246,22 @@ bool Plugin::unload(color_ostream &con)
// if we are actually loaded // if we are actually loaded
if(state == PS_LOADED) if(state == PS_LOADED)
{ {
// notify the plugin about an attempt to shutdown
if (plugin_onstatechange &&
plugin_onstatechange(con, SC_BEGIN_UNLOAD) == CR_NOT_FOUND)
{
con.printerr("Plugin %s has refused to be unloaded.\n", name.c_str());
access->unlock();
return false;
}
// wait for all calls to finish
access->wait();
// notify plugin about shutdown, if it has a shutdown function // notify plugin about shutdown, if it has a shutdown function
command_result cr = CR_OK; command_result cr = CR_OK;
if(plugin_shutdown) if(plugin_shutdown)
cr = plugin_shutdown(con); cr = plugin_shutdown(con);
// wait for all calls to finish
access->wait();
// cleanup... // cleanup...
reset_lua();
parent->unregisterCommands(this); parent->unregisterCommands(this);
commands.clear(); commands.clear();
if(cr == CR_OK) if(cr == CR_OK)
@ -418,6 +438,90 @@ Plugin::plugin_state Plugin::getState() const
return state; return state;
} }
void Plugin::index_lua(DFLibrary *lib)
{
if (auto cmdlist = (CommandReg*)LookupPlugin(lib, "plugin_lua_commands"))
{
for (; cmdlist->name; ++cmdlist)
{
auto &cmd = lua_commands[cmdlist->name];
if (!cmd) cmd = new LuaCommand;
cmd->owner = this;
cmd->name = cmdlist->name;
cmd->command = cmdlist->command;
}
}
if (auto funlist = (FunctionReg*)LookupPlugin(lib, "plugin_lua_functions"))
{
for (; funlist->name; ++funlist)
{
auto &cmd = lua_functions[funlist->name];
if (!cmd) cmd = new LuaFunction;
cmd->owner = this;
cmd->name = funlist->name;
cmd->identity = funlist->identity;
}
}
}
void Plugin::reset_lua()
{
for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it)
it->second->command = NULL;
for (auto it = lua_functions.begin(); it != lua_functions.end(); ++it)
it->second->identity = NULL;
}
int Plugin::lua_cmd_wrapper(lua_State *state)
{
auto cmd = (LuaCommand*)lua_touserdata(state, lua_upvalueindex(1));
RefAutoinc lock(cmd->owner->access);
if (!cmd->command)
luaL_error(state, "plugin command %s() has been unloaded",
(cmd->owner->name+"."+cmd->name).c_str());
return cmd->command(state);
}
int Plugin::lua_fun_wrapper(lua_State *state)
{
auto cmd = (LuaFunction*)lua_touserdata(state, UPVAL_CONTAINER_ID);
RefAutoinc lock(cmd->owner->access);
if (!cmd->identity)
luaL_error(state, "plugin function %s() has been unloaded",
(cmd->owner->name+"."+cmd->name).c_str());
return LuaWrapper::method_wrapper_core(state, cmd->identity);
}
void Plugin::open_lua(lua_State *state, int table)
{
table = lua_absindex(state, table);
RefAutolock lock(access);
for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it)
{
lua_pushlightuserdata(state, it->second);
lua_pushcclosure(state, lua_cmd_wrapper, 1);
lua_setfield(state, table, it->first.c_str());
}
for (auto it = lua_functions.begin(); it != lua_functions.end(); ++it)
{
lua_rawgetp(state, LUA_REGISTRYINDEX, &LuaWrapper::DFHACK_TYPETABLE_TOKEN);
lua_pushlightuserdata(state, NULL);
lua_pushfstring(state, "%s.%s()", name.c_str(), it->second->name.c_str());
lua_pushlightuserdata(state, it->second);
lua_pushcclosure(state, lua_fun_wrapper, 4);
lua_setfield(state, table, it->first.c_str());
}
}
PluginManager::PluginManager(Core * core) PluginManager::PluginManager(Core * core)
{ {
#ifdef LINUX_BUILD #ifdef LINUX_BUILD

@ -139,6 +139,8 @@ namespace DFHack
static void print(const char *format, ...); static void print(const char *format, ...);
static void printerr(const char *format, ...); static void printerr(const char *format, ...);
PluginManager *getPluginManager() { return plug_mgr; }
private: private:
DFHack::Console con; DFHack::Console con;

@ -32,10 +32,6 @@ distribution.
#include "DataIdentity.h" #include "DataIdentity.h"
#include "LuaWrapper.h" #include "LuaWrapper.h"
#ifndef BUILD_DFHACK_LIB
#error Due to export issues this header is internal to the main library.
#endif
namespace df { namespace df {
// A very simple and stupid implementation of some stuff from boost // A very simple and stupid implementation of some stuff from boost
template<class U, class V> struct is_same_type { static const bool value = false; }; template<class U, class V> struct is_same_type { static const bool value = false; };
@ -50,7 +46,7 @@ namespace df {
template<class T, bool isvoid = is_same_type<typename return_type<T>::type,void>::value> template<class T, bool isvoid = is_same_type<typename return_type<T>::type,void>::value>
struct function_wrapper {}; struct function_wrapper {};
class cur_lua_ostream_argument { class DFHACK_EXPORT cur_lua_ostream_argument {
DFHack::color_ostream *out; DFHack::color_ostream *out;
public: public:
cur_lua_ostream_argument(lua_State *state); cur_lua_ostream_argument(lua_State *state);

@ -160,8 +160,6 @@ namespace DFHack
}; };
} }
// Due to export issues, this stuff can only work in the main dll
#ifdef BUILD_DFHACK_LIB
namespace df namespace df
{ {
using DFHack::function_identity_base; using DFHack::function_identity_base;
@ -171,7 +169,7 @@ namespace df
using DFHack::ptr_container_identity; using DFHack::ptr_container_identity;
using DFHack::bit_container_identity; using DFHack::bit_container_identity;
class number_identity_base : public primitive_identity { class DFHACK_EXPORT number_identity_base : public primitive_identity {
const char *name; const char *name;
public: public:
@ -197,7 +195,7 @@ namespace df
virtual void write(void *ptr, double val) { *(T*)ptr = T(val); } virtual void write(void *ptr, double val) { *(T*)ptr = T(val); }
}; };
class bool_identity : public primitive_identity { class DFHACK_EXPORT bool_identity : public primitive_identity {
public: public:
bool_identity() : primitive_identity(sizeof(bool)) {}; bool_identity() : primitive_identity(sizeof(bool)) {};
@ -207,7 +205,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
}; };
class ptr_string_identity : public primitive_identity { class DFHACK_EXPORT ptr_string_identity : public primitive_identity {
public: public:
ptr_string_identity() : primitive_identity(sizeof(char*)) {}; ptr_string_identity() : primitive_identity(sizeof(char*)) {};
@ -217,7 +215,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
}; };
class stl_string_identity : public DFHack::constructed_identity { class DFHACK_EXPORT stl_string_identity : public DFHack::constructed_identity {
public: public:
stl_string_identity() stl_string_identity()
: constructed_identity(sizeof(std::string), &allocator_fn<std::string>) : constructed_identity(sizeof(std::string), &allocator_fn<std::string>)
@ -233,7 +231,7 @@ namespace df
virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
}; };
class stl_ptr_vector_identity : public ptr_container_identity { class DFHACK_EXPORT stl_ptr_vector_identity : public ptr_container_identity {
public: public:
typedef std::vector<void*> container; typedef std::vector<void*> container;
@ -276,6 +274,8 @@ namespace df
} }
}; };
// Due to export issues, this stuff can only work in the main dll
#ifdef BUILD_DFHACK_LIB
class buffer_container_identity : public container_identity { class buffer_container_identity : public container_identity {
int size; int size;
@ -370,8 +370,9 @@ namespace df
((container*)ptr)->set(idx, val); ((container*)ptr)->set(idx, val);
} }
}; };
#endif
class stl_bit_vector_identity : public bit_container_identity { class DFHACK_EXPORT stl_bit_vector_identity : public bit_container_identity {
public: public:
typedef std::vector<bool> container; typedef std::vector<bool> container;
@ -400,6 +401,7 @@ namespace df
} }
}; };
#ifdef BUILD_DFHACK_LIB
template<class T> template<class T>
class enum_list_attr_identity : public container_identity { class enum_list_attr_identity : public container_identity {
public: public:
@ -421,9 +423,10 @@ namespace df
return (void*)&((container*)ptr)->items[idx]; return (void*)&((container*)ptr)->items[idx];
} }
}; };
#endif
#define NUMBER_IDENTITY_TRAITS(type) \ #define NUMBER_IDENTITY_TRAITS(type) \
template<> struct identity_traits<type> { \ template<> struct DFHACK_EXPORT identity_traits<type> { \
static number_identity<type> identity; \ static number_identity<type> identity; \
static number_identity_base *get() { return &identity; } \ static number_identity_base *get() { return &identity; } \
}; };
@ -439,37 +442,37 @@ namespace df
NUMBER_IDENTITY_TRAITS(uint64_t); NUMBER_IDENTITY_TRAITS(uint64_t);
NUMBER_IDENTITY_TRAITS(float); NUMBER_IDENTITY_TRAITS(float);
template<> struct identity_traits<bool> { template<> struct DFHACK_EXPORT identity_traits<bool> {
static bool_identity identity; static bool_identity identity;
static bool_identity *get() { return &identity; } static bool_identity *get() { return &identity; }
}; };
template<> struct identity_traits<std::string> { template<> struct DFHACK_EXPORT identity_traits<std::string> {
static stl_string_identity identity; static stl_string_identity identity;
static stl_string_identity *get() { return &identity; } static stl_string_identity *get() { return &identity; }
}; };
template<> struct identity_traits<char*> { template<> struct DFHACK_EXPORT identity_traits<char*> {
static ptr_string_identity identity; static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; } static ptr_string_identity *get() { return &identity; }
}; };
template<> struct identity_traits<const char*> { template<> struct DFHACK_EXPORT identity_traits<const char*> {
static ptr_string_identity identity; static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; } static ptr_string_identity *get() { return &identity; }
}; };
template<> struct identity_traits<void*> { template<> struct DFHACK_EXPORT identity_traits<void*> {
static pointer_identity identity; static pointer_identity identity;
static pointer_identity *get() { return &identity; } static pointer_identity *get() { return &identity; }
}; };
template<> struct identity_traits<std::vector<void*> > { template<> struct DFHACK_EXPORT identity_traits<std::vector<void*> > {
static stl_ptr_vector_identity identity; static stl_ptr_vector_identity identity;
static stl_ptr_vector_identity *get() { return &identity; } static stl_ptr_vector_identity *get() { return &identity; }
}; };
template<> struct identity_traits<std::vector<bool> > { template<> struct DFHACK_EXPORT identity_traits<std::vector<bool> > {
static stl_bit_vector_identity identity; static stl_bit_vector_identity identity;
static stl_bit_vector_identity *get() { return &identity; } static stl_bit_vector_identity *get() { return &identity; }
}; };
@ -478,14 +481,17 @@ namespace df
// Container declarations // Container declarations
#ifdef BUILD_DFHACK_LIB
template<class Enum, class FT> struct identity_traits<enum_field<Enum,FT> > { template<class Enum, class FT> struct identity_traits<enum_field<Enum,FT> > {
static primitive_identity *get(); static primitive_identity *get();
}; };
#endif
template<class T> struct identity_traits<T *> { template<class T> struct identity_traits<T *> {
static pointer_identity *get(); static pointer_identity *get();
}; };
#ifdef BUILD_DFHACK_LIB
template<class T, int sz> struct identity_traits<T [sz]> { template<class T, int sz> struct identity_traits<T [sz]> {
static container_identity *get(); static container_identity *get();
}; };
@ -493,11 +499,13 @@ namespace df
template<class T> struct identity_traits<std::vector<T> > { template<class T> struct identity_traits<std::vector<T> > {
static container_identity *get(); static container_identity *get();
}; };
#endif
template<class T> struct identity_traits<std::vector<T*> > { template<class T> struct identity_traits<std::vector<T*> > {
static stl_ptr_vector_identity *get(); static stl_ptr_vector_identity *get();
}; };
#ifdef BUILD_DFHACK_LIB
template<class T> struct identity_traits<std::deque<T> > { template<class T> struct identity_traits<std::deque<T> > {
static container_identity *get(); static container_identity *get();
}; };
@ -518,13 +526,16 @@ namespace df
template<class T> struct identity_traits<enum_list_attr<T> > { template<class T> struct identity_traits<enum_list_attr<T> > {
static container_identity *get(); static container_identity *get();
}; };
#endif
// Container definitions // Container definitions
#ifdef BUILD_DFHACK_LIB
template<class Enum, class FT> template<class Enum, class FT>
inline primitive_identity *identity_traits<enum_field<Enum,FT> >::get() { inline primitive_identity *identity_traits<enum_field<Enum,FT> >::get() {
return identity_traits<FT>::get(); return identity_traits<FT>::get();
} }
#endif
template<class T> template<class T>
inline pointer_identity *identity_traits<T *>::get() { inline pointer_identity *identity_traits<T *>::get() {
@ -532,6 +543,7 @@ namespace df
return &identity; return &identity;
} }
#ifdef BUILD_DFHACK_LIB
template<class T, int sz> template<class T, int sz>
inline container_identity *identity_traits<T [sz]>::get() { inline container_identity *identity_traits<T [sz]>::get() {
static buffer_container_identity identity(sz, identity_traits<T>::get()); static buffer_container_identity identity(sz, identity_traits<T>::get());
@ -544,6 +556,7 @@ namespace df
static stl_container_identity<container> identity("vector", identity_traits<T>::get()); static stl_container_identity<container> identity("vector", identity_traits<T>::get());
return &identity; return &identity;
} }
#endif
template<class T> template<class T>
inline stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() { inline stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() {
@ -551,6 +564,7 @@ namespace df
return &identity; return &identity;
} }
#ifdef BUILD_DFHACK_LIB
template<class T> template<class T>
inline container_identity *identity_traits<std::deque<T> >::get() { inline container_identity *identity_traits<std::deque<T> >::get() {
typedef std::deque<T> container; typedef std::deque<T> container;
@ -576,5 +590,5 @@ namespace df
static enum_list_attr_identity<T> identity(identity_traits<T>::get()); static enum_list_attr_identity<T> identity(identity_traits<T>::get());
return &identity; return &identity;
} }
}
#endif #endif
}

@ -30,6 +30,7 @@ distribution.
#include <map> #include <map>
#include "DataDefs.h" #include "DataDefs.h"
#include "PluginManager.h"
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
@ -156,7 +157,7 @@ namespace DFHack { namespace LuaWrapper {
* Verify that the object is a DF ref with UPVAL_METATABLE. * Verify that the object is a DF ref with UPVAL_METATABLE.
* If everything ok, extract the address. * If everything ok, extract the address.
*/ */
uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode); DFHACK_EXPORT uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode);
bool is_type_compatible(lua_State *state, type_identity *type1, int meta1, bool is_type_compatible(lua_State *state, type_identity *type1, int meta1,
type_identity *type2, int meta2, bool exact_equal); type_identity *type2, int meta2, bool exact_equal);
@ -221,16 +222,14 @@ namespace DFHack { namespace LuaWrapper {
*/ */
void AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum); void AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum);
struct FunctionReg {
const char *name;
function_identity_base *identity;
};
/** /**
* Wrap functions and add them to the table on the top of the stack. * Wrap functions and add them to the table on the top of the stack.
*/ */
using DFHack::FunctionReg;
void SetFunctionWrappers(lua_State *state, const FunctionReg *reg); void SetFunctionWrappers(lua_State *state, const FunctionReg *reg);
int method_wrapper_core(lua_State *state, function_identity_base *id);
void IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct); void IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct);
void AttachDFGlobals(lua_State *state); void AttachDFGlobals(lua_State *state);

@ -33,6 +33,8 @@ distribution.
#include "RemoteClient.h" #include "RemoteClient.h"
typedef struct lua_State lua_State;
struct DFLibrary; struct DFLibrary;
namespace tthread namespace tthread
{ {
@ -49,6 +51,7 @@ namespace DFHack
class PluginManager; class PluginManager;
class virtual_identity; class virtual_identity;
class RPCService; class RPCService;
class function_identity_base;
enum state_change_event enum state_change_event
{ {
@ -56,7 +59,17 @@ namespace DFHack
SC_WORLD_UNLOADED, SC_WORLD_UNLOADED,
SC_MAP_LOADED, SC_MAP_LOADED,
SC_MAP_UNLOADED, SC_MAP_UNLOADED,
SC_VIEWSCREEN_CHANGED SC_VIEWSCREEN_CHANGED,
SC_CORE_INITIALIZED,
SC_BEGIN_UNLOAD
};
struct DFHACK_EXPORT CommandReg {
const char *name;
int (*command)(lua_State*);
};
struct DFHACK_EXPORT FunctionReg {
const char *name;
function_identity_base *identity;
}; };
struct DFHACK_EXPORT PluginCommand struct DFHACK_EXPORT PluginCommand
{ {
@ -102,6 +115,7 @@ namespace DFHack
{ {
struct RefLock; struct RefLock;
struct RefAutolock; struct RefAutolock;
struct RefAutoinc;
enum plugin_state enum plugin_state
{ {
PS_UNLOADED, PS_UNLOADED,
@ -138,6 +152,9 @@ namespace DFHack
{ {
return name; return name;
} }
void open_lua(lua_State *state, int table);
private: private:
RefLock * access; RefLock * access;
std::vector <PluginCommand> commands; std::vector <PluginCommand> commands;
@ -147,6 +164,26 @@ namespace DFHack
DFLibrary * plugin_lib; DFLibrary * plugin_lib;
PluginManager * parent; PluginManager * parent;
plugin_state state; plugin_state state;
struct LuaCommand {
Plugin *owner;
std::string name;
int (*command)(lua_State *state);
};
std::map<std::string, LuaCommand*> lua_commands;
static int lua_cmd_wrapper(lua_State *state);
struct LuaFunction {
Plugin *owner;
std::string name;
function_identity_base *identity;
};
std::map<std::string, LuaFunction*> lua_functions;
static int lua_fun_wrapper(lua_State *state);
void index_lua(DFLibrary *lib);
void reset_lua();
command_result (*plugin_init)(color_ostream &, std::vector <PluginCommand> &); command_result (*plugin_init)(color_ostream &, std::vector <PluginCommand> &);
command_result (*plugin_status)(color_ostream &, std::string &); command_result (*plugin_status)(color_ostream &, std::string &);
command_result (*plugin_shutdown)(color_ostream &); command_result (*plugin_shutdown)(color_ostream &);
@ -199,5 +236,15 @@ namespace DFHack
}; };
/// You have to have this in every plugin you write - just once. Ideally on top of the main file. /// You have to have this in every plugin you write - just once. Ideally on top of the main file.
#define DFHACK_PLUGIN(plugin_name) DFhackDataExport const char * version = DFHACK_VERSION;\ #define DFHACK_PLUGIN(plugin_name) \
DFhackDataExport const char * name = plugin_name; DFhackDataExport const char * version = DFHACK_VERSION;\
DFhackDataExport const char * name = plugin_name;
#define DFHACK_PLUGIN_LUA_COMMANDS \
DFhackCExport const DFHack::CommandReg plugin_lua_commands[] =
#define DFHACK_PLUGIN_LUA_FUNCTIONS \
DFhackCExport const DFHack::FunctionReg plugin_lua_functions[] =
#define DFHACK_LUA_COMMAND(name) { #name, name }
#define DFHACK_LUA_FUNCTION(name) { #name, df::wrap_function(name) }
#define DFHACK_LUA_END { NULL, NULL }

@ -55,6 +55,10 @@ function mkmodule(module,env)
error("Not a table in package.loaded["..module.."]") error("Not a table in package.loaded["..module.."]")
end end
end end
local plugname = string.match(module,'^plugins%.(%w+)$')
if plugname then
dfhack.open_plugin(pkg,plugname)
end
setmetatable(pkg, { __index = (env or _G) }) setmetatable(pkg, { __index = (env or _G) })
return pkg return pkg
end end

@ -35,6 +35,10 @@ if (BUILD_DWARFEXPORT)
add_subdirectory (dwarfexport) add_subdirectory (dwarfexport)
endif() endif()
install(DIRECTORY lua/
DESTINATION ${DFHACK_LUA_DESTINATION}/plugins
FILES_MATCHING PATTERN "*.lua")
# Protobuf # Protobuf
FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto) FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto)

@ -2,6 +2,9 @@
#include "Console.h" #include "Console.h"
#include "Export.h" #include "Export.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "Error.h"
#include "DataFuncs.h"
#include "modules/Gui.h" #include "modules/Gui.h"
#include "modules/Job.h" #include "modules/Job.h"
@ -420,6 +423,9 @@ static df::burrow *findByName(color_ostream &out, std::string name, bool silent
static void copyUnits(df::burrow *target, df::burrow *source, bool enable) static void copyUnits(df::burrow *target, df::burrow *source, bool enable)
{ {
CHECK_NULL_POINTER(target);
CHECK_NULL_POINTER(source);
if (source == target) if (source == target)
{ {
if (!enable) if (!enable)
@ -439,6 +445,9 @@ static void copyUnits(df::burrow *target, df::burrow *source, bool enable)
static void copyTiles(df::burrow *target, df::burrow *source, bool enable) static void copyTiles(df::burrow *target, df::burrow *source, bool enable)
{ {
CHECK_NULL_POINTER(target);
CHECK_NULL_POINTER(source);
if (source == target) if (source == target)
{ {
if (!enable) if (!enable)
@ -480,6 +489,8 @@ static void copyTiles(df::burrow *target, df::burrow *source, bool enable)
static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mask, static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mask,
df::tile_designation d_value, bool enable) df::tile_designation d_value, bool enable)
{ {
CHECK_NULL_POINTER(target);
auto &blocks = world->map.map_blocks; auto &blocks = world->map.map_blocks;
for (size_t i = 0; i < blocks.size(); i++) for (size_t i = 0; i < blocks.size(); i++)
@ -512,6 +523,8 @@ static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mas
static bool setTilesByKeyword(df::burrow *target, std::string name, bool enable) static bool setTilesByKeyword(df::burrow *target, std::string name, bool enable)
{ {
CHECK_NULL_POINTER(target);
df::tile_designation mask(0); df::tile_designation mask(0);
df::tile_designation value(0); df::tile_designation value(0);
@ -538,6 +551,14 @@ static bool setTilesByKeyword(df::burrow *target, std::string name, bool enable)
return true; return true;
} }
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(findByName),
DFHACK_LUA_FUNCTION(copyUnits),
DFHACK_LUA_FUNCTION(copyTiles),
DFHACK_LUA_FUNCTION(setTilesByKeyword),
DFHACK_LUA_END
};
static command_result burrow(color_ostream &out, vector <string> &parameters) static command_result burrow(color_ostream &out, vector <string> &parameters)
{ {
CoreSuspender suspend; CoreSuspender suspend;

@ -0,0 +1,33 @@
local _ENV = mkmodule('plugins.burrows')
--[[
Native functions:
* findByName(name) -> burrow
* copyUnits(dest,src,enable)
* copyTiles(dest,src,enable)
* setTilesByKeyword(dest,kwd,enable) -> success
'enable' selects between add and remove modes
--]]
clearUnits = dfhack.units.clearBurrowMembers
function isBurrowUnit(burrow,unit)
return dfhack.units.isInBurrow(unit,burrow)
end
function setBurrowUnit(burrow,unit,enable)
return dfhack.units.setInBurrow(unit,burrow,enable)
end
clearTiles = dfhack.maps.clearBurrowTiles
listBlocks = dfhack.maps.listBurrowBlocks
isBurrowTile = dfhack.maps.isBurrowTile
setBurrowTile = dfhack.maps.setBurrowTile
isBlockBurrowTile = dfhack.maps.isBlockBurrowTile
setBlockBurrowTile = dfhack.maps.setBlockBurrowTile
return _ENV