Skip to content

Commit 9eed9f0

Browse files
committed
Wrap a few utility functions defined on the c++ side for lua.
1 parent 28a7410 commit 9eed9f0

10 files changed

Lines changed: 268 additions & 10 deletions

File tree

LUA_API.rst

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,64 @@ Since the data is hidden in data structures owned by the DF world,
497497
and automatically stored in the save game, these save and retrieval
498498
functions can just copy values in memory without doing any actual I/O.
499499
However, currently every entry has a 180+-byte dead-weight overhead.
500+
501+
C++ function wrappers
502+
=====================
503+
504+
Thin wrappers around C++ functions, similar to the ones for virtual methods.
505+
506+
Gui module
507+
----------
508+
509+
* ``dfhack.gui.getSelectedWorkshopJob(silent)``
510+
511+
When a job is selected in *'q'* mode, returns the job, else
512+
prints error unless silent and returns *nil*.
513+
514+
* ``dfhack.gui.getSelectedJob(silent)``
515+
516+
Returns the job selected in a workshop or unit/jobs screen.
517+
518+
* ``dfhack.gui.getSelectedUnit(silent)``
519+
520+
Returns the unit selected via *'v'*, *'k'*, unit/jobs, or
521+
a full-screen item view of a cage or suchlike.
522+
523+
* ``dfhack.gui.getSelectedItem(silent)``
524+
525+
Returns the item selected via *'v'* ->inventory, *'k'*, *'t'*, or
526+
a full-screen item view of a container. Note that in the
527+
last case, the highlighted *contained item* is returned, not
528+
the container itself.
529+
530+
* ``dfhack.gui.showAnnouncement(text,color,is_bright)``
531+
532+
Adds a regular announcement with given text, color, and brightness.
533+
The is_bright boolean actually seems to invert the brightness.
534+
535+
* ``dfhack.gui.showPopupAnnouncement(text,color,is_bright)``
536+
537+
Pops up a titan-style modal announcement window.
538+
539+
Job module
540+
----------
541+
542+
* ``dfhack.job.cloneJobStruct(job)``
543+
544+
Creates a deep copy of the given job.
545+
546+
* ``dfhack.job.printJobDetails(job)``
547+
548+
Prints info about the job.
549+
550+
* ``dfhack.job.getJobHolder(job)``
551+
552+
Returns the building holding the job.
553+
554+
* ``dfhack.job.is_equal(job1,job2)``
555+
556+
Compares important fields in the job and nested item structures.
557+
558+
* ``dfhack.job.is_item_equal(job_item1,job_item2)``
559+
560+
Compares important fields in the job item structures.

Lua API.html

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,11 @@ <h1 class="title">DFHack Lua API</h1>
335335
</li>
336336
<li><a class="reference internal" href="#dfhack-utilities" id="id10">DFHack utilities</a><ul>
337337
<li><a class="reference internal" href="#persistent-configuration-storage" id="id11">Persistent configuration storage</a></li>
338+
<li><a class="reference internal" href="#c-function-wrappers" id="id12">C++ function wrappers</a><ul>
339+
<li><a class="reference internal" href="#gui-module" id="id13">Gui module</a></li>
340+
<li><a class="reference internal" href="#job-module" id="id14">Job module</a></li>
341+
</ul>
342+
</li>
338343
</ul>
339344
</li>
340345
</ul>
@@ -753,6 +758,59 @@ <h2><a class="toc-backref" href="#id11">Persistent configuration storage</a></h2
753758
functions can just copy values in memory without doing any actual I/O.
754759
However, currently every entry has a 180+-byte dead-weight overhead.</p>
755760
</div>
761+
<div class="section" id="c-function-wrappers">
762+
<h2><a class="toc-backref" href="#id12">C++ function wrappers</a></h2>
763+
<p>Thin wrappers around C++ functions, similar to the ones for virtual methods.</p>
764+
<div class="section" id="gui-module">
765+
<h3><a class="toc-backref" href="#id13">Gui module</a></h3>
766+
<ul>
767+
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedWorkshopJob(silent)</tt></p>
768+
<p>When a job is selected in <em>'q'</em> mode, returns the job, else
769+
prints error unless silent and returns <em>nil</em>.</p>
770+
</li>
771+
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedJob(silent)</tt></p>
772+
<p>Returns the job selected in a workshop or unit/jobs screen.</p>
773+
</li>
774+
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedUnit(silent)</tt></p>
775+
<p>Returns the unit selected via <em>'v'</em>, <em>'k'</em>, unit/jobs, or
776+
a full-screen item view of a cage or suchlike.</p>
777+
</li>
778+
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedItem(silent)</tt></p>
779+
<p>Returns the item selected via <em>'v'</em> -&gt;inventory, <em>'k'</em>, <em>'t'</em>, or
780+
a full-screen item view of a container. Note that in the
781+
last case, the highlighted <em>contained item</em> is returned, not
782+
the container itself.</p>
783+
</li>
784+
<li><p class="first"><tt class="docutils literal">dfhack.gui.showAnnouncement(text,color,is_bright)</tt></p>
785+
<p>Adds a regular announcement with given text, color, and brightness.
786+
The is_bright boolean actually seems to invert the brightness.</p>
787+
</li>
788+
<li><p class="first"><tt class="docutils literal">dfhack.gui.showPopupAnnouncement(text,color,is_bright)</tt></p>
789+
<p>Pops up a titan-style modal announcement window.</p>
790+
</li>
791+
</ul>
792+
</div>
793+
<div class="section" id="job-module">
794+
<h3><a class="toc-backref" href="#id14">Job module</a></h3>
795+
<ul>
796+
<li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p>
797+
<p>Creates a deep copy of the given job.</p>
798+
</li>
799+
<li><p class="first"><tt class="docutils literal">dfhack.job.printJobDetails(job)</tt></p>
800+
<p>Prints info about the job.</p>
801+
</li>
802+
<li><p class="first"><tt class="docutils literal">dfhack.job.getJobHolder(job)</tt></p>
803+
<p>Returns the building holding the job.</p>
804+
</li>
805+
<li><p class="first"><tt class="docutils literal">dfhack.job.is_equal(job1,job2)</tt></p>
806+
<p>Compares important fields in the job and nested item structures.</p>
807+
</li>
808+
<li><p class="first"><tt class="docutils literal">dfhack.job.is_item_equal(job_item1,job_item2)</tt></p>
809+
<p>Compares important fields in the job item structures.</p>
810+
</li>
811+
</ul>
812+
</div>
813+
</div>
756814
</div>
757815
</div>
758816
</body>

library/LuaTools.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,18 @@ distribution.
3939

4040
#include "modules/World.h"
4141
#include "modules/Gui.h"
42+
#include "modules/Job.h"
4243

4344
#include "LuaWrapper.h"
4445
#include "LuaTools.h"
4546

4647
#include "MiscUtils.h"
4748

4849
#include "df/job.h"
50+
#include "df/job_item.h"
51+
#include "df/building.h"
52+
#include "df/unit.h"
53+
#include "df/item.h"
4954

5055
#include <lua.h>
5156
#include <lauxlib.h>
@@ -74,6 +79,13 @@ color_ostream *DFHack::Lua::GetOutput(lua_State *L)
7479
return rv;
7580
}
7681

82+
df::cur_lua_ostream_argument::cur_lua_ostream_argument(lua_State *state)
83+
{
84+
out = DFHack::Lua::GetOutput(state);
85+
if (!out)
86+
LuaWrapper::field_error(state, UPVAL_METHOD_NAME, "no output stream", "invoke");
87+
}
88+
7789
static void set_dfhack_output(lua_State *L, color_ostream *p)
7890
{
7991
lua_pushlightuserdata(L, p);
@@ -844,6 +856,39 @@ static void OpenPersistent(lua_State *state)
844856
lua_pop(state, 1);
845857
}
846858

859+
static void OpenModule(lua_State *state, const char *mname, const LuaWrapper::FunctionReg *reg)
860+
{
861+
luaL_getsubtable(state, lua_gettop(state), mname);
862+
LuaWrapper::SetFunctionWrappers(state, reg);
863+
lua_pop(state, 1);
864+
}
865+
866+
#define WRAPM(module, function) { #function, df::wrap_function(&module::function) }
867+
#define WRAP(function) { #function, df::wrap_function(&function) }
868+
#define WRAPN(name, function) { #name, df::wrap_function(&function) }
869+
870+
static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
871+
WRAPM(Gui, getSelectedWorkshopJob),
872+
WRAPM(Gui, getSelectedJob),
873+
WRAPM(Gui, getSelectedUnit),
874+
WRAPM(Gui, getSelectedItem),
875+
WRAPM(Gui, showAnnouncement),
876+
WRAPM(Gui, showPopupAnnouncement),
877+
{ NULL, NULL }
878+
};
879+
880+
static bool jobEqual(df::job *job1, df::job *job2) { return *job1 == *job2; }
881+
static bool jobItemEqual(df::job_item *job1, df::job_item *job2) { return *job1 == *job2; }
882+
883+
static const LuaWrapper::FunctionReg dfhack_job_module[] = {
884+
WRAP(cloneJobStruct),
885+
WRAP(printJobDetails),
886+
WRAP(getJobHolder),
887+
WRAPN(is_equal, jobEqual),
888+
WRAPN(is_item_equal, jobItemEqual),
889+
{ NULL, NULL }
890+
};
891+
847892
lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
848893
{
849894
if (!state)
@@ -872,7 +917,8 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
872917

873918
OpenPersistent(state);
874919

875-
LuaWrapper::AddMethodWrapper(state, 0, -1, "getSelectedJob", df::wrap_function(&Gui::getSelectedJob));
920+
OpenModule(state, "gui", dfhack_gui_module);
921+
OpenModule(state, "job", dfhack_job_module);
876922

877923
lua_setglobal(state, "dfhack");
878924

library/LuaTypes.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ distribution.
3030

3131
#include "MemAccess.h"
3232
#include "Core.h"
33+
#include "Error.h"
3334
#include "VersionInfo.h"
3435
#include "tinythread.h"
3536
// must be last due to MS stupidity
@@ -1022,18 +1023,29 @@ static int meta_call_function(lua_State *state)
10221023
auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID);
10231024
if (lua_gettop(state) != id->getNumArgs())
10241025
field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke");
1025-
id->invoke(state, 1);
1026+
1027+
try {
1028+
id->invoke(state, 1);
1029+
}
1030+
catch (Error::NullPointer &e) {
1031+
const char *vn = e.varname();
1032+
std::string tmp = stl_sprintf("NULL pointer: %s", vn ? vn : "?");
1033+
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
1034+
}
1035+
catch (std::exception &e) {
1036+
std::string tmp = stl_sprintf("C++ exception: %s", e.what());
1037+
field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke");
1038+
}
1039+
10261040
return 1;
10271041
}
10281042

10291043
/**
10301044
* Create a closure invoking the given function, and add it to the field table.
10311045
*/
1032-
void LuaWrapper::AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
1033-
const char *name, function_identity_base *fun)
1046+
static void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
1047+
const char *name, function_identity_base *fun)
10341048
{
1035-
field_idx = lua_absindex(state, field_idx);
1036-
10371049
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPETABLE_TOKEN);
10381050
if (meta_idx)
10391051
lua_pushvalue(state, meta_idx);
@@ -1046,6 +1058,17 @@ void LuaWrapper::AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
10461058
lua_setfield(state, field_idx, name);
10471059
}
10481060

1061+
/**
1062+
* Wrap functions and add them to the table on the top of the stack.
1063+
*/
1064+
void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg)
1065+
{
1066+
int base = lua_gettop(state);
1067+
1068+
for (; reg && reg->name; ++reg)
1069+
AddMethodWrapper(state, 0, base, reg->name, reg->identity);
1070+
}
1071+
10491072
/**
10501073
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
10511074
*/

library/MiscUtils.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ distribution.
2525
#include "Internal.h"
2626
#include "Export.h"
2727
#include "MiscUtils.h"
28+
#include "Error.h"
2829

2930
#ifndef LINUX_BUILD
3031
#include <Windows.h>
@@ -39,6 +40,10 @@ distribution.
3940
#include <sstream>
4041
#include <map>
4142

43+
const char *DFHack::Error::NullPointer::what() const throw() {
44+
return "NULL pointer access";
45+
}
46+
4247
std::string stl_sprintf(const char *fmt, ...) {
4348
va_list lst;
4449
va_start(lst, fmt);

library/include/DataFuncs.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ namespace df {
5050
template<class T, bool isvoid = is_same_type<typename return_type<T>::type,void>::value>
5151
struct function_wrapper {};
5252

53+
class cur_lua_ostream_argument {
54+
DFHack::color_ostream *out;
55+
public:
56+
cur_lua_ostream_argument(lua_State *state);
57+
operator DFHack::color_ostream& () { return *out; }
58+
};
59+
5360
/*
5461
* Since templates can't match variable arg count,
5562
* a separate specialization is needed for every
@@ -69,7 +76,7 @@ namespace df {
6976
type v##type; df::identity_traits<type>::get()->lua_write(state, UPVAL_METHOD_NAME, &v##type, base++);
7077
#define OSTREAM_ARG DFHack::color_ostream&
7178
#define LOAD_OSTREAM(name) \
72-
DFHack::color_ostream_proxy name(DFHack::Core::getInstance().getConsole());
79+
cur_lua_ostream_argument name(state);
7380

7481
#define INSTANTIATE_RETURN_TYPE(FArgs) \
7582
template<FW_TARGSC class RT> struct return_type<RT (*) FArgs> { typedef RT type; }; \

library/include/Error.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ namespace DFHack
3939
* the whole array of DFHack exceptions from the rest
4040
*/
4141
class DFHACK_EXPORT All : public std::exception{};
42+
43+
class DFHACK_EXPORT NullPointer : public All {
44+
const char *varname_;
45+
public:
46+
NullPointer(const char *varname_ = NULL) : varname_(varname_) {}
47+
const char *varname() const { return varname_; }
48+
virtual const char *what() const throw();
49+
};
50+
51+
#define CHECK_NULL_POINTER(var) \
52+
{ if (var == NULL) throw DFHack::Error::NullPointer(#var); }
53+
4254
class DFHACK_EXPORT AllSymbols : public All{};
4355
// Syntax errors and whatnot, the xml can't be read
4456
class DFHACK_EXPORT SymbolsXmlParse : public AllSymbols

library/include/LuaWrapper.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,16 @@ namespace DFHack { namespace LuaWrapper {
220220
* and the enum itself to the _enum metafield. Pushes the key table on the stack.
221221
*/
222222
void AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum);
223+
224+
struct FunctionReg {
225+
const char *name;
226+
function_identity_base *identity;
227+
};
228+
223229
/**
224-
* Create a closure invoking the given function, and add it to the field table.
230+
* Wrap functions and add them to the table on the top of the stack.
225231
*/
226-
void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx,
227-
const char *name, function_identity_base *fun);
232+
void SetFunctionWrappers(lua_State *state, const FunctionReg *reg);
228233

229234
void IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct);
230235

0 commit comments

Comments
 (0)