From 0e0740fddf26a849c97c26bd8e6dcb6cf58b9bf9 Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Tue, 10 Apr 2012 10:34:03 +0400
Subject: [PATCH 1/3] Stop printall(df.global) from breaking if there are
unknown addresses.
---
LUA_API.rst | 5 +++++
Lua API.html | 4 ++++
library/LuaTypes.cpp | 15 +++++++++------
3 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/LUA_API.rst b/LUA_API.rst
index 71cb6030f..3ce10c766 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -68,6 +68,11 @@ All typed objects have the following built-in features:
and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.
+ **WARNING**: a few of the data structures (like ui_look_list)
+ contain unions with pointers to different types with vtables.
+ Using pairs on such structs is an almost sure way to crash with
+ an access violation.
+
* ``ref._kind``
Returns one of: ``primitive``, ``struct``, ``container``,
diff --git a/Lua API.html b/Lua API.html
index 569940f56..82943fadf 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -395,6 +395,10 @@ Every structured field access produces a new userdata instance.
Returns an iterator for the sequence of actual C++ field names
and values. Fields are enumerated in memory order. Methods and
lua wrapper properties are not included in the iteration.
+WARNING: a few of the data structures (like ui_look_list)
+contain unions with pointers to different types with vtables.
+Using pairs on such structs is an almost sure way to crash with
+an access violation.
ref._kind
Returns one of: primitive, struct, container,
diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp
index 4213499eb..2f9ef9e81 100644
--- a/library/LuaTypes.cpp
+++ b/library/LuaTypes.cpp
@@ -1072,10 +1072,10 @@ void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg)
/**
* Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
*/
-static void IndexFields(lua_State *state, int base, struct_identity *pstruct)
+static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bool globals)
{
if (pstruct->getParent())
- IndexFields(state, base, pstruct->getParent());
+ IndexFields(state, base, pstruct->getParent(), globals);
auto fields = pstruct->getFields();
if (!fields)
@@ -1105,7 +1105,10 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct)
break;
default:
- AssociateId(state, base+3, ++cnt, name.c_str());
+ // Do not add invalid globals to the enumeration order
+ if (!globals || *(void**)fields[i].offset)
+ AssociateId(state, base+3, ++cnt, name.c_str());
+
lua_pushlightuserdata(state, (void*)&fields[i]);
lua_setfield(state, base+2, name.c_str());
break;
@@ -1143,7 +1146,7 @@ void LuaWrapper::IndexStatics(lua_State *state, int meta_idx, int ftable_idx, st
* Make a struct-style object metatable.
*/
static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
- lua_CFunction reader, lua_CFunction writer)
+ lua_CFunction reader, lua_CFunction writer, bool globals = false)
{
int base = lua_gettop(state);
@@ -1152,7 +1155,7 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
// Index the fields
lua_newtable(state);
- IndexFields(state, base, pstruct);
+ IndexFields(state, base, pstruct, globals);
// Add the iteration metamethods
PushStructMethod(state, base+1, base+3, meta_struct_next);
@@ -1304,7 +1307,7 @@ void struct_identity::build_metatable(lua_State *state)
void global_identity::build_metatable(lua_State *state)
{
- MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex);
+ MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex, true);
}
/**
From 93c795cfc3eebf60157cc56e5aa38c521a60b0b3 Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Tue, 10 Apr 2012 11:43:36 +0400
Subject: [PATCH 2/3] Job module api tweaks: add a namespace to match others
and some funcs.
---
LUA_API.rst | 16 +++++++++-
Lua API.html | 13 +++++++-
library/LuaApi.cpp | 45 ++++++++++++++++++++++++----
library/include/modules/Job.h | 26 ++++++++++------
library/modules/Job.cpp | 56 ++++++++++++++++++++++++++++++-----
plugins/jobutils.cpp | 16 +++++-----
plugins/workflow.cpp | 22 +++++++-------
7 files changed, 152 insertions(+), 42 deletions(-)
diff --git a/LUA_API.rst b/LUA_API.rst
index 3ce10c766..ee3b1077c 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -634,10 +634,18 @@ Job module
Prints info about the job.
-* ``dfhack.job.getJobHolder(job)``
+* ``dfhack.job.printItemDetails(jobitem,idx)``
+
+ Prints info about the job item.
+
+* ``dfhack.job.getHolder(job)``
Returns the building holding the job.
+* ``dfhack.job.getWorker(job)``
+
+ Returns the unit performing the job.
+
* ``dfhack.job.is_equal(job1,job2)``
Compares important fields in the job and nested item structures.
@@ -646,6 +654,12 @@ Job module
Compares important fields in the job item structures.
+* ``dfhack.job.listNewlyCreated(first_id)``
+
+ Returns the current value of ``df.global.job_next_id``, and
+ if there are any jobs with ``first_id <= id < job_next_id``,
+ a lua list containing them.
+
Units module
------------
diff --git a/Lua API.html b/Lua API.html
index 82943fadf..89e158037 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -873,15 +873,26 @@ The is_bright boolean actually seems to invert the brightness.
dfhack.job.printJobDetails(job)
Prints info about the job.
-dfhack.job.getJobHolder(job)
+dfhack.job.printItemDetails(jobitem,idx)
+Prints info about the job item.
+
+dfhack.job.getHolder(job)
Returns the building holding the job.
+dfhack.job.getWorker(job)
+Returns the unit performing 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.
+dfhack.job.listNewlyCreated(first_id)
+Returns the current value of df.global.job_next_id, and
+if there are any jobs with first_id <= id < job_next_id,
+a lua list containing them.
+
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 5e55460d7..c336e309c 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -517,10 +517,13 @@ static void OpenMatinfo(lua_State *state)
* Wrappers for C++ API *
************************/
-static void OpenModule(lua_State *state, const char *mname, const LuaWrapper::FunctionReg *reg)
+static void OpenModule(lua_State *state, const char *mname,
+ const LuaWrapper::FunctionReg *reg, const luaL_Reg *reg2 = NULL)
{
luaL_getsubtable(state, lua_gettop(state), mname);
LuaWrapper::SetFunctionWrappers(state, reg);
+ if (reg2)
+ luaL_setfuncs(state, reg2, 0);
lua_pop(state, 1);
}
@@ -547,14 +550,46 @@ 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),
+ WRAPM(Job,cloneJobStruct),
+ WRAPM(Job,printItemDetails),
+ WRAPM(Job,printJobDetails),
+ WRAPM(Job,getHolder),
+ WRAPM(Job,getWorker),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }
};
+static int job_listNewlyCreated(lua_State *state)
+{
+ int nxid = luaL_checkint(state, 1);
+
+ lua_settop(state, 1);
+
+ std::vector
pvec;
+ if (Job::listNewlyCreated(&pvec, &nxid))
+ {
+ lua_pushinteger(state, nxid);
+ lua_newtable(state);
+
+ for (size_t i = 0; i < pvec.size(); i++)
+ {
+ Lua::PushDFObject(state, pvec[i]);
+ lua_rawseti(state, -2, i+1);
+ }
+
+ return 2;
+ }
+ else
+ return 1;
+}
+
+static const luaL_Reg dfhack_job_funcs[] = {
+ { "listNewlyCreated", job_listNewlyCreated },
+ { NULL, NULL }
+};
+
+
static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, setNickname),
WRAPM(Units, getVisibleName),
@@ -576,6 +611,6 @@ void OpenDFHackApi(lua_State *state)
LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module);
- OpenModule(state, "job", dfhack_job_module);
+ OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs);
OpenModule(state, "units", dfhack_units_module);
}
diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h
index 8a82958ee..d733c542d 100644
--- a/library/include/modules/Job.h
+++ b/library/include/modules/Job.h
@@ -36,24 +36,32 @@ namespace df
struct job_item;
struct job_item_filter;
struct building;
+ struct unit;
}
namespace DFHack
{
- // Duplicate the job structure. It is not linked into any DF lists.
- DFHACK_EXPORT df::job *cloneJobStruct(df::job *job);
+ namespace Job {
+ // Duplicate the job structure. It is not linked into any DF lists.
+ DFHACK_EXPORT df::job *cloneJobStruct(df::job *job);
- // Delete a cloned structure.
- DFHACK_EXPORT void deleteJobStruct(df::job *job);
+ // Delete a cloned structure.
+ DFHACK_EXPORT void deleteJobStruct(df::job *job);
- DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);
- DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b);
+ DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx);
+ DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);
+
+ DFHACK_EXPORT df::building *getHolder(df::job *job);
+ DFHACK_EXPORT df::unit *getWorker(df::job *job);
- DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);
+ DFHACK_EXPORT bool linkIntoWorld(df::job *job, bool new_id = true);
- DFHACK_EXPORT df::building *getJobHolder(df::job *job);
+ // lists jobs with ids >= *id_var, and sets *id_var = *job_next_id;
+ DFHACK_EXPORT bool listNewlyCreated(std::vector *pvec, int *id_var);
+ }
- DFHACK_EXPORT bool linkJobIntoWorld(df::job *job, bool new_id = true);
+ DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b);
+ DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b);
}
#endif
diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp
index 679ecc0e7..5ce6647d9 100644
--- a/library/modules/Job.cpp
+++ b/library/modules/Job.cpp
@@ -53,7 +53,7 @@ using namespace std;
using namespace DFHack;
using namespace df::enums;
-df::job *DFHack::cloneJobStruct(df::job *job)
+df::job *DFHack::Job::cloneJobStruct(df::job *job)
{
CHECK_NULL_POINTER(job);
@@ -87,7 +87,7 @@ df::job *DFHack::cloneJobStruct(df::job *job)
return pnew;
}
-void DFHack::deleteJobStruct(df::job *job)
+void DFHack::Job::deleteJobStruct(df::job *job)
{
if (!job)
return;
@@ -148,7 +148,7 @@ bool DFHack::operator== (const df::job &a, const df::job &b)
return true;
}
-static void print_job_item_details(color_ostream &out, df::job *job, unsigned idx, df::job_item *item)
+void DFHack::Job::printItemDetails(color_ostream &out, df::job_item *item, int idx)
{
CHECK_NULL_POINTER(item);
@@ -184,7 +184,7 @@ static void print_job_item_details(color_ostream &out, df::job *job, unsigned id
out << " tool use: " << ENUM_KEY_STR(tool_uses, item->has_tool_use) << endl;
}
-void DFHack::printJobDetails(color_ostream &out, df::job *job)
+void DFHack::Job::printJobDetails(color_ostream &out, df::job *job)
{
CHECK_NULL_POINTER(job);
@@ -224,10 +224,10 @@ void DFHack::printJobDetails(color_ostream &out, df::job *job)
out << " reaction: " << job->reaction_name << endl;
for (size_t i = 0; i < job->job_items.size(); i++)
- print_job_item_details(out, job, i, job->job_items[i]);
+ printItemDetails(out, job->job_items[i], i);
}
-df::building *DFHack::getJobHolder(df::job *job)
+df::building *DFHack::Job::getHolder(df::job *job)
{
CHECK_NULL_POINTER(job);
@@ -241,7 +241,21 @@ df::building *DFHack::getJobHolder(df::job *job)
return NULL;
}
-bool DFHack::linkJobIntoWorld(df::job *job, bool new_id)
+df::unit *DFHack::Job::getWorker(df::job *job)
+{
+ CHECK_NULL_POINTER(job);
+
+ for (size_t i = 0; i < job->references.size(); i++)
+ {
+ VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->references[i]);
+ if (ref)
+ return ref->getUnit();
+ }
+
+ return NULL;
+}
+
+bool DFHack::Job::linkIntoWorld(df::job *job, bool new_id)
{
using df::global::world;
using df::global::job_next_id;
@@ -269,3 +283,31 @@ bool DFHack::linkJobIntoWorld(df::job *job, bool new_id)
return true;
}
}
+
+bool DFHack::Job::listNewlyCreated(std::vector *pvec, int *id_var)
+{
+ using df::global::world;
+ using df::global::job_next_id;
+
+ pvec->clear();
+
+ if (!job_next_id || *job_next_id <= *id_var)
+ return false;
+
+ int old_id = *id_var;
+ int cur_id = *job_next_id;
+
+ *id_var = cur_id;
+
+ pvec->reserve(std::min(20,cur_id - old_id));
+
+ df::job_list_link *link = world->job_list.next;
+ for (; link; link = link->next)
+ {
+ int id = link->item->id;
+ if (id >= old_id)
+ pvec->push_back(link->item);
+ }
+
+ return true;
+}
diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp
index 603346c33..511095943 100644
--- a/plugins/jobutils.cpp
+++ b/plugins/jobutils.cpp
@@ -289,9 +289,9 @@ static command_result job_duplicate(color_ostream &out, vector & parame
}
// Actually clone
- df::job *pnew = cloneJobStruct(job);
+ df::job *pnew = Job::cloneJobStruct(job);
- linkJobIntoWorld(pnew);
+ Job::linkIntoWorld(pnew);
vector_insert_at(building->jobs, ++*ui_workshop_job_cursor, pnew);
return CR_OK;
@@ -325,14 +325,14 @@ static command_result job_cmd(color_ostream &out, vector & parameters)
return CR_WRONG_USAGE;
if (cmd == "query") {
- printJobDetails(out, job);
+ Job::printJobDetails(out, job);
} else {
if (!Gui::workshop_job_hotkey(Core::getTopViewscreen()))
return CR_WRONG_USAGE;
df::building *selected = world->selected_building;
for (size_t i = 0; i < selected->jobs.size(); i++)
- printJobDetails(out, selected->jobs[i]);
+ Job::printJobDetails(out, selected->jobs[i]);
}
}
else if (cmd == "item-material")
@@ -355,7 +355,7 @@ static command_result job_cmd(color_ostream &out, vector & parameters)
if (minfo.isValid() && !iinfo.matches(*item, &minfo)) {
out.printerr("Material does not match the requirements.\n");
- printJobDetails(out, job);
+ Job::printJobDetails(out, job);
return CR_FAILURE;
}
@@ -376,7 +376,7 @@ static command_result job_cmd(color_ostream &out, vector & parameters)
out.printerr("WARNING: Due to a probable bug, creature & plant material subtype\n"
" is ignored unless the item type is also specified.\n");
- printJobDetails(out, job);
+ Job::printJobDetails(out, job);
return CR_OK;
}
else if (cmd == "item-type")
@@ -399,7 +399,7 @@ static command_result job_cmd(color_ostream &out, vector & parameters)
if (iinfo.isValid() && !iinfo.matches(*item, &minfo)) {
out.printerr("Item type does not match the requirements.\n");
- printJobDetails(out, job);
+ Job::printJobDetails(out, job);
return CR_FAILURE;
}
@@ -407,7 +407,7 @@ static command_result job_cmd(color_ostream &out, vector & parameters)
item->item_subtype = iinfo.subtype;
out << "Job item updated." << endl;
- printJobDetails(out, job);
+ Job::printJobDetails(out, job);
return CR_OK;
}
else
diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp
index 5bdeb5d58..480379261 100644
--- a/plugins/workflow.cpp
+++ b/plugins/workflow.cpp
@@ -184,9 +184,9 @@ public:
ProtectedJob(df::job *job) : id(job->id)
{
tick_idx = cur_tick_idx;
- holder = getJobHolder(job);
+ holder = Job::getHolder(job);
building_id = holder ? holder->id : -1;
- job_copy = cloneJobStruct(job);
+ job_copy = Job::cloneJobStruct(job);
actual_job = job;
reaction_id = -1;
@@ -196,7 +196,7 @@ public:
~ProtectedJob()
{
- deleteJobStruct(job_copy);
+ Job::deleteJobStruct(job_copy);
}
bool isActuallyResumed() {
@@ -214,8 +214,8 @@ public:
return;
reaction_id = -1;
- deleteJobStruct(job_copy);
- job_copy = cloneJobStruct(job);
+ Job::deleteJobStruct(job_copy);
+ job_copy = Job::cloneJobStruct(job);
}
void tick_job(df::job *job, int ticks)
@@ -365,7 +365,7 @@ static ProtectedJob *get_known(int id)
static bool isSupportedJob(df::job *job)
{
return job->misc_links.empty() &&
- getJobHolder(job) &&
+ Job::getHolder(job) &&
(!job->job_items.empty() ||
job->job_type == job_type::CollectClay ||
job->job_type == job_type::CollectSand);
@@ -526,11 +526,11 @@ static bool recover_job(color_ostream &out, ProtectedJob *pj)
}
// Create and link in the actual job structure
- df::job *recovered = cloneJobStruct(pj->job_copy);
+ df::job *recovered = Job::cloneJobStruct(pj->job_copy);
- if (!linkJobIntoWorld(recovered, false)) // reuse same id
+ if (!Job::linkIntoWorld(recovered, false)) // reuse same id
{
- deleteJobStruct(recovered);
+ Job::deleteJobStruct(recovered);
out.printerr("Inconsistency: job %d (%s) already in list.\n",
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type).c_str());
@@ -1435,7 +1435,7 @@ static void print_job(color_ostream &out, ProtectedJob *pj)
df::job *job = pj->isLive() ? pj->actual_job : pj->job_copy;
- printJobDetails(out, job);
+ Job::printJobDetails(out, job);
if (job->job_type == job_type::MeltMetalObject &&
isOptionEnabled(CF_AUTOMELT))
@@ -1560,7 +1560,7 @@ static command_result workflow_cmd(color_ostream &out, vector & paramet
pending = true;
}
- printJobDetails(out, pending_recover[i]->job_copy);
+ Job::printJobDetails(out, pending_recover[i]->job_copy);
}
}
From 249be0c1a0e267e806a8b0b1342f1f58f53e44fd Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Tue, 10 Apr 2012 12:11:00 +0400
Subject: [PATCH 3/3] Change SC_MAP_LOADED handling: only NULL/not NULL change
is meaningful.
---
library/Core.cpp | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/library/Core.cpp b/library/Core.cpp
index b3c91034f..a752ae628 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -893,26 +893,31 @@ int Core::Update()
if (new_wdata != last_world_data_ptr)
{
// we check for map change too
- bool mapchange = new_mapdata != last_local_map_ptr;
+ bool had_map = isMapLoaded();
last_world_data_ptr = new_wdata;
last_local_map_ptr = new_mapdata;
getWorld()->ClearPersistentCache();
// and if the world is going away, we report the map change first
- if(!new_wdata && mapchange)
- plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
+ if(had_map)
+ plug_mgr->OnStateChange(out, SC_MAP_UNLOADED);
// and if the world is appearing, we report map change after that
plug_mgr->OnStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED);
- if(new_wdata && mapchange)
- plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
+ if(isMapLoaded())
+ plug_mgr->OnStateChange(out, SC_MAP_LOADED);
}
// otherwise just check for map change...
else if (new_mapdata != last_local_map_ptr)
{
+ bool had_map = isMapLoaded();
last_local_map_ptr = new_mapdata;
- getWorld()->ClearPersistentCache();
- plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
+
+ if (isMapLoaded() != had_map)
+ {
+ getWorld()->ClearPersistentCache();
+ plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED);
+ }
}
// detect if the viewscreen changed