Wrap a few utility functions defined on the c++ side for lua.

develop
Alexander Gavrilov 2012-04-05 19:55:59 +04:00
parent 28a741082f
commit 9eed9f0d24
10 changed files with 268 additions and 10 deletions

@ -497,3 +497,64 @@ Since the data is hidden in data structures owned by the DF world,
and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.
C++ function wrappers
=====================
Thin wrappers around C++ functions, similar to the ones for virtual methods.
Gui module
----------
* ``dfhack.gui.getSelectedWorkshopJob(silent)``
When a job is selected in *'q'* mode, returns the job, else
prints error unless silent and returns *nil*.
* ``dfhack.gui.getSelectedJob(silent)``
Returns the job selected in a workshop or unit/jobs screen.
* ``dfhack.gui.getSelectedUnit(silent)``
Returns the unit selected via *'v'*, *'k'*, unit/jobs, or
a full-screen item view of a cage or suchlike.
* ``dfhack.gui.getSelectedItem(silent)``
Returns the item selected via *'v'* ->inventory, *'k'*, *'t'*, or
a full-screen item view of a container. Note that in the
last case, the highlighted *contained item* is returned, not
the container itself.
* ``dfhack.gui.showAnnouncement(text,color,is_bright)``
Adds a regular announcement with given text, color, and brightness.
The is_bright boolean actually seems to invert the brightness.
* ``dfhack.gui.showPopupAnnouncement(text,color,is_bright)``
Pops up a titan-style modal announcement window.
Job module
----------
* ``dfhack.job.cloneJobStruct(job)``
Creates a deep copy of the given job.
* ``dfhack.job.printJobDetails(job)``
Prints info about the job.
* ``dfhack.job.getJobHolder(job)``
Returns the building holding the job.
* ``dfhack.job.is_equal(job1,job2)``
Compares important fields in the job and nested item structures.
* ``dfhack.job.is_item_equal(job_item1,job_item2)``
Compares important fields in the job item structures.

@ -335,6 +335,11 @@ ul.auto-toc {
</li>
<li><a class="reference internal" href="#dfhack-utilities" id="id10">DFHack utilities</a><ul>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id11">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#c-function-wrappers" id="id12">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#gui-module" id="id13">Gui module</a></li>
<li><a class="reference internal" href="#job-module" id="id14">Job module</a></li>
</ul>
</li>
</ul>
</li>
</ul>
@ -753,6 +758,59 @@ and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.</p>
</div>
<div class="section" id="c-function-wrappers">
<h2><a class="toc-backref" href="#id12">C++ function wrappers</a></h2>
<p>Thin wrappers around C++ functions, similar to the ones for virtual methods.</p>
<div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id13">Gui module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedWorkshopJob(silent)</tt></p>
<p>When a job is selected in <em>'q'</em> mode, returns the job, else
prints error unless silent and returns <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedJob(silent)</tt></p>
<p>Returns the job selected in a workshop or unit/jobs screen.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedUnit(silent)</tt></p>
<p>Returns the unit selected via <em>'v'</em>, <em>'k'</em>, unit/jobs, or
a full-screen item view of a cage or suchlike.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedItem(silent)</tt></p>
<p>Returns the item selected via <em>'v'</em> -&gt;inventory, <em>'k'</em>, <em>'t'</em>, or
a full-screen item view of a container. Note that in the
last case, the highlighted <em>contained item</em> is returned, not
the container itself.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.showAnnouncement(text,color,is_bright)</tt></p>
<p>Adds a regular announcement with given text, color, and brightness.
The is_bright boolean actually seems to invert the brightness.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.showPopupAnnouncement(text,color,is_bright)</tt></p>
<p>Pops up a titan-style modal announcement window.</p>
</li>
</ul>
</div>
<div class="section" id="job-module">
<h3><a class="toc-backref" href="#id14">Job module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p>
<p>Creates a deep copy of the given job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.printJobDetails(job)</tt></p>
<p>Prints info about the job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.getJobHolder(job)</tt></p>
<p>Returns the building holding the job.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.is_equal(job1,job2)</tt></p>
<p>Compares important fields in the job and nested item structures.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.is_item_equal(job_item1,job_item2)</tt></p>
<p>Compares important fields in the job item structures.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
</body>

@ -39,6 +39,7 @@ distribution.
#include "modules/World.h"
#include "modules/Gui.h"
#include "modules/Job.h"
#include "LuaWrapper.h"
#include "LuaTools.h"
@ -46,6 +47,10 @@ distribution.
#include "MiscUtils.h"
#include "df/job.h"
#include "df/job_item.h"
#include "df/building.h"
#include "df/unit.h"
#include "df/item.h"
#include <lua.h>
#include <lauxlib.h>
@ -74,6 +79,13 @@ color_ostream *DFHack::Lua::GetOutput(lua_State *L)
return rv;
}
df::cur_lua_ostream_argument::cur_lua_ostream_argument(lua_State *state)
{
out = DFHack::Lua::GetOutput(state);
if (!out)
LuaWrapper::field_error(state, UPVAL_METHOD_NAME, "no output stream", "invoke");
}
static void set_dfhack_output(lua_State *L, color_ostream *p)
{
lua_pushlightuserdata(L, p);
@ -844,6 +856,39 @@ static void OpenPersistent(lua_State *state)
lua_pop(state, 1);
}
static void OpenModule(lua_State *state, const char *mname, const LuaWrapper::FunctionReg *reg)
{
luaL_getsubtable(state, lua_gettop(state), mname);
LuaWrapper::SetFunctionWrappers(state, reg);
lua_pop(state, 1);
}
#define WRAPM(module, function) { #function, df::wrap_function(&module::function) }
#define WRAP(function) { #function, df::wrap_function(&function) }
#define WRAPN(name, function) { #name, df::wrap_function(&function) }
static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
WRAPM(Gui, getSelectedWorkshopJob),
WRAPM(Gui, getSelectedJob),
WRAPM(Gui, getSelectedUnit),
WRAPM(Gui, getSelectedItem),
WRAPM(Gui, showAnnouncement),
WRAPM(Gui, showPopupAnnouncement),
{ NULL, NULL }
};
static bool jobEqual(df::job *job1, df::job *job2) { return *job1 == *job2; }
static bool jobItemEqual(df::job_item *job1, df::job_item *job2) { return *job1 == *job2; }
static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAP(cloneJobStruct),
WRAP(printJobDetails),
WRAP(getJobHolder),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }
};
lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
{
if (!state)
@ -872,7 +917,8 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
OpenPersistent(state);
LuaWrapper::AddMethodWrapper(state, 0, -1, "getSelectedJob", df::wrap_function(&Gui::getSelectedJob));
OpenModule(state, "gui", dfhack_gui_module);
OpenModule(state, "job", dfhack_job_module);
lua_setglobal(state, "dfhack");

@ -30,6 +30,7 @@ distribution.
#include "MemAccess.h"
#include "Core.h"
#include "Error.h"
#include "VersionInfo.h"
#include "tinythread.h"
// must be last due to MS stupidity
@ -1022,18 +1023,29 @@ static int meta_call_function(lua_State *state)
auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID);
if (lua_gettop(state) != id->getNumArgs())
field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke");
try {
id->invoke(state, 1);
}
catch (Error::NullPointer &e) {
const char *vn = e.varname();
std::string tmp = stl_sprintf("NULL pointer: %s", vn ? vn : "?");
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
}
catch (std::exception &e) {
std::string tmp = stl_sprintf("C++ exception: %s", e.what());
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
}
return 1;
}
/**
* Create a closure invoking the given function, and add it to the field table.
*/
void LuaWrapper::AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
static void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
const char *name, function_identity_base *fun)
{
field_idx = lua_absindex(state, field_idx);
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
if (meta_idx)
lua_pushvalue(state, meta_idx);
@ -1046,6 +1058,17 @@ void LuaWrapper::AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
lua_setfield(state, field_idx, name);
}
/**
* Wrap functions and add them to the table on the top of the stack.
*/
void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg)
{
int base = lua_gettop(state);
for (; reg && reg->name; ++reg)
AddMethodWrapper(state, 0, base, reg->name, reg->identity);
}
/**
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
*/

@ -25,6 +25,7 @@ distribution.
#include "Internal.h"
#include "Export.h"
#include "MiscUtils.h"
#include "Error.h"
#ifndef LINUX_BUILD
#include <Windows.h>
@ -39,6 +40,10 @@ distribution.
#include <sstream>
#include <map>
const char *DFHack::Error::NullPointer::what() const throw() {
return "NULL pointer access";
}
std::string stl_sprintf(const char *fmt, ...) {
va_list lst;
va_start(lst, fmt);

@ -50,6 +50,13 @@ namespace df {
template<class T, bool isvoid = is_same_type<typename return_type<T>::type,void>::value>
struct function_wrapper {};
class cur_lua_ostream_argument {
DFHack::color_ostream *out;
public:
cur_lua_ostream_argument(lua_State *state);
operator DFHack::color_ostream& () { return *out; }
};
/*
* Since templates can't match variable arg count,
* a separate specialization is needed for every
@ -69,7 +76,7 @@ namespace df {
type v##type; df::identity_traits<type>::get()->lua_write(state, UPVAL_METHOD_NAME, &v##type, base++);
#define OSTREAM_ARG DFHack::color_ostream&
#define LOAD_OSTREAM(name) \
DFHack::color_ostream_proxy name(DFHack::Core::getInstance().getConsole());
cur_lua_ostream_argument name(state);
#define INSTANTIATE_RETURN_TYPE(FArgs) \
template<FW_TARGSC class RT> struct return_type<RT (*) FArgs> { typedef RT type; }; \

@ -39,6 +39,18 @@ namespace DFHack
* the whole array of DFHack exceptions from the rest
*/
class DFHACK_EXPORT All : public std::exception{};
class DFHACK_EXPORT NullPointer : public All {
const char *varname_;
public:
NullPointer(const char *varname_ = NULL) : varname_(varname_) {}
const char *varname() const { return varname_; }
virtual const char *what() const throw();
};
#define CHECK_NULL_POINTER(var) \
{ if (var == NULL) throw DFHack::Error::NullPointer(#var); }
class DFHACK_EXPORT AllSymbols : public All{};
// Syntax errors and whatnot, the xml can't be read
class DFHACK_EXPORT SymbolsXmlParse : public AllSymbols

@ -220,11 +220,16 @@ namespace DFHack { namespace LuaWrapper {
* and the enum itself to the _enum metafield. Pushes the key table on the stack.
*/
void AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum);
struct FunctionReg {
const char *name;
function_identity_base *identity;
};
/**
* Create a closure invoking the given function, and add it to the field table.
* Wrap functions and add them to the table on the top of the stack.
*/
void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
const char *name, function_identity_base *fun);
void SetFunctionWrappers(lua_State *state, const FunctionReg *reg);
void IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct);

@ -1,12 +1,36 @@
-- Common startup file for all dfhack plugins with lua support
-- The global dfhack table is already created by C++ init code.
-- Console color constants
COLOR_RESET = -1
COLOR_BLACK = 0
COLOR_BLUE = 1
COLOR_GREEN = 2
COLOR_CYAN = 3
COLOR_RED = 4
COLOR_MAGENTA = 5
COLOR_BROWN = 6
COLOR_GREY = 7
COLOR_DARKGREY = 8
COLOR_LIGHTBLUE = 9
COLOR_LIGHTGREEN = 10
COLOR_LIGHTCYAN = 11
COLOR_LIGHTRED = 12
COLOR_LIGHTMAGENTA = 13
COLOR_YELLOW = 14
COLOR_WHITE = 15
-- Error handling
safecall = dfhack.safecall
function dfhack.pcall(f, ...)
return xpcall(f, dfhack.onerror, ...)
end
-- Module loading
function mkmodule(module,env)
local pkg = package.loaded[module]
if pkg == nil then
@ -31,6 +55,8 @@ function reload(module)
dofile(path)
end
-- Misc functions
function printall(table)
if table == nil then return end
for k,v in pairs(table) do

@ -32,6 +32,7 @@ distribution.
using namespace std;
#include "Core.h"
#include "Error.h"
#include "PluginManager.h"
#include "MiscUtils.h"
@ -54,6 +55,8 @@ using namespace df::enums;
df::job *DFHack::cloneJobStruct(df::job *job)
{
CHECK_NULL_POINTER(job);
df::job *pnew = new df::job(*job);
// Clean out transient fields
@ -105,6 +108,9 @@ void DFHack::deleteJobStruct(df::job *job)
bool DFHack::operator== (const df::job_item &a, const df::job_item &b)
{
CHECK_NULL_POINTER(&a);
CHECK_NULL_POINTER(&b);
if (!(CMP(item_type) && CMP(item_subtype) &&
CMP(mat_type) && CMP(mat_index) &&
CMP(flags1.whole) && CMP(quantity) && CMP(vector_id) &&
@ -125,6 +131,9 @@ bool DFHack::operator== (const df::job_item &a, const df::job_item &b)
bool DFHack::operator== (const df::job &a, const df::job &b)
{
CHECK_NULL_POINTER(&a);
CHECK_NULL_POINTER(&b);
if (!(CMP(job_type) && CMP(unk2) &&
CMP(mat_type) && CMP(mat_index) &&
CMP(item_subtype) && CMP(item_category.whole) &&
@ -141,6 +150,8 @@ bool DFHack::operator== (const df::job &a, const df::job &b)
static void print_job_item_details(color_ostream &out, df::job *job, unsigned idx, df::job_item *item)
{
CHECK_NULL_POINTER(item);
ItemTypeInfo info(item);
out << " Input Item " << (idx+1) << ": " << info.toString();
@ -175,6 +186,8 @@ static void print_job_item_details(color_ostream &out, df::job *job, unsigned id
void DFHack::printJobDetails(color_ostream &out, df::job *job)
{
CHECK_NULL_POINTER(job);
out.color(job->flags.bits.suspend ? Console::COLOR_DARKGREY : Console::COLOR_GREY);
out << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type);
if (job->flags.whole)
@ -216,6 +229,8 @@ void DFHack::printJobDetails(color_ostream &out, df::job *job)
df::building *DFHack::getJobHolder(df::job *job)
{
CHECK_NULL_POINTER(job);
for (size_t i = 0; i < job->references.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]);