develop
Quietust 2014-02-20 12:42:57 -06:00
commit 5ced804312
15 changed files with 292 additions and 81 deletions

@ -1119,6 +1119,12 @@ can be omitted.</p>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.TranslateName(name[,in_english,only_last_name])</span></tt></p>
<p>Convert a language_name or only the last name part to string.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.df2utf(string)</tt></p>
<p>Convert a string from DF's CP437 encoding to UTF-8.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.utf2df(string)</tt></p>
<p>Convert a string from UTF-8 to DF's CP437 encoding.</p>
</li>
</ul>
<div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id20">Gui module</a></h3>

@ -812,6 +812,14 @@ can be omitted.
Convert a language_name or only the last name part to string.
* ``dfhack.df2utf(string)``
Convert a string from DF's CP437 encoding to UTF-8.
* ``dfhack.utf2df(string)``
Convert a string from UTF-8 to DF's CP437 encoding.
Gui module
----------

@ -1,6 +1,11 @@
DFHack future
- Is not yet known.
Internals:
- support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua).
New commands:
- move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- 'plant create' - spawn a new shrub under the cursor
Misc improvements:
- digfort: improved csv parsing, add start() comment handling

@ -693,26 +693,30 @@ Options:
Beware that filling in hollow veins will trigger a demon invasion on top of
your miner when you dig into the region that used to be hollow.
extirpate
---------
A tool for getting rid of trees and shrubs. By default, it only kills
a tree/shrub under the cursor. The plants are turned into ashes instantly.
plant
-----
A tool for creating shrubs, growing, or getting rid of them.
Options:
Subcommands:
:create: Create a new shrub/sapling.
:grow: Make saplings grow into trees.
:extirpate: Kills trees and shrubs, turning them into ashes instantly.
:immolate: Similar to extirpate, but sets the plants on fire instead. The
fires can and *will* spread ;)
``create`` creates a new sapling under the cursor. Takes a raw ID as
argument (e.g. TOWER_CAP). The cursor must be located on a dirt or grass
floor tile.
``grow`` works on the sapling under the cursor, and turns it into a tree.
Works on all shrubs of the map if the cursor is hidden.
``extirpate`` and ``immolate`` work only on the plant under the cursor.
For mass effects, use one of the additional options:
:shrubs: affect all shrubs on the map
:trees: affect all trees on the map
:all: affect every plant!
grow
----
Makes all saplings present on the map grow into trees (almost) instantly.
immolate
--------
Very similar to extirpate, but additionally sets the plants on fire. The fires
can and *will* spread ;)
regrass
-------
Regrows grass. Not much to it ;)

@ -811,6 +811,15 @@ bool Core::loadScriptFile(color_ostream &out, string fname, bool silent)
}
}
// Load dfhack.init in a dedicated thread (non-interactive console mode)
void fInitthread(void * iodata)
{
IODATA * iod = ((IODATA*) iodata);
Core * core = iod->core;
color_ostream_proxy out(core->getConsole());
core->loadScriptFile(out, "dfhack.init", true);
}
// A thread function... for the interactive console.
void fIOthread(void * iodata)
{
@ -822,7 +831,7 @@ void fIOthread(void * iodata)
main_history.load("dfhack.history");
Console & con = core->getConsole();
if(plug_mgr == 0 || core == 0)
if (plug_mgr == 0)
{
con.printerr("Something horrible happened in Core's constructor...\n");
return;
@ -1008,16 +1017,24 @@ bool Core::Init()
IODATA *temp = new IODATA;
temp->core = this;
temp->plug_mgr = plug_mgr;
HotkeyMutex = new mutex();
HotkeyCond = new condition_variable();
if (!is_text_mode)
{
cerr << "Starting IO thread.\n";
// create IO thread
thread * IO = new thread(fIOthread, (void *) temp);
}
else
{
cerr << "Starting dfhack.init thread.\n";
thread * init = new thread(fInitthread, (void *) temp);
}
cerr << "Starting DF input capture thread.\n";
// set up hotkey capture
HotkeyMutex = new mutex();
HotkeyCond = new condition_variable();
thread * HK = new thread(fHKthread, (void *) temp);
screen_window = new Windows::top_level_window();
screen_window->addChild(new Windows::dfhack_dummy(5,10));

@ -1279,6 +1279,9 @@ static std::string getHackPath() { return Core::getInstance().getHackPath(); }
static bool isWorldLoaded() { return Core::getInstance().isWorldLoaded(); }
static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); }
static std::string df2utf(std::string s) { return DF2UTF(s); }
static std::string utf2df(std::string s) { return UTF2DF(s); }
static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(getOSType),
WRAP(getDFVersion),
@ -1288,6 +1291,8 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(isWorldLoaded),
WRAP(isMapLoaded),
WRAPM(Translation, TranslateName),
WRAP(df2utf),
WRAP(utf2df),
{ NULL, NULL }
};

@ -55,6 +55,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "modules/Units.h"
#include "modules/World.h"
#include "LuaTools.h"
#include "DataDefs.h"
#include "df/ui.h"
#include "df/ui_advmode.h"
@ -656,6 +658,8 @@ CoreService::CoreService() {
addMethod("CoreSuspend", &CoreService::CoreSuspend, SF_DONT_SUSPEND);
addMethod("CoreResume", &CoreService::CoreResume, SF_DONT_SUSPEND);
addMethod("RunLua", &CoreService::RunLua);
// Functions:
addFunction("GetVersion", GetVersion, SF_DONT_SUSPEND);
addFunction("GetDFVersion", GetDFVersion, SF_DONT_SUSPEND);
@ -730,3 +734,85 @@ command_result CoreService::CoreResume(color_ostream &stream, const EmptyMessage
cnt->set_value(--suspend_depth);
return CR_OK;
}
namespace {
struct LuaFunctionData {
command_result rv;
const dfproto::CoreRunLuaRequest *in;
StringListMessage *out;
};
}
command_result CoreService::RunLua(color_ostream &stream,
const dfproto::CoreRunLuaRequest *in,
StringListMessage *out)
{
auto L = Lua::Core::State;
LuaFunctionData data = { CR_FAILURE, in, out };
lua_pushcfunction(L, doRunLuaFunction);
lua_pushlightuserdata(L, &data);
if (!Lua::Core::SafeCall(stream, 1, 0))
return CR_FAILURE;
return data.rv;
}
int CoreService::doRunLuaFunction(lua_State *L)
{
color_ostream &out = *Lua::GetOutput(L);
auto &args = *(LuaFunctionData*)lua_touserdata(L, 1);
// Verify module name
std::string module = args.in->module();
size_t len = module.size();
bool valid = false;
if (len > 4)
{
if (module.substr(0,4) == "rpc.")
valid = true;
else if ((module[len-4] == '.' || module[len-4] == '-') && module.substr(len-3) != "rpc")
valid = true;
}
if (!valid)
{
args.rv = CR_WRONG_USAGE;
out.printerr("Only modules named rpc.* or *.rpc or *-rpc may be called.\n");
return 0;
}
// Prepare function and arguments
lua_settop(L, 0);
if (!Lua::PushModulePublic(out, L, module.c_str(), args.in->function().c_str())
|| lua_isnil(L, 1))
{
args.rv = CR_NOT_FOUND;
return 0;
}
luaL_checkstack(L, args.in->arguments_size(), "too many arguments");
for (int i = 0; i < args.in->arguments_size(); i++)
lua_pushstring(L, args.in->arguments(i).c_str());
// Call
lua_call(L, args.in->arguments_size(), LUA_MULTRET);
// Store results
int nresults = lua_gettop(L);
for (int i = 1; i <= nresults; i++)
{
size_t len;
const char *data = lua_tolstring(L, i, &len);
args.out->add_value(std::string(data, len));
}
args.rv = CR_OK;
return 0;
}

@ -72,20 +72,54 @@ int main (int argc, char *argv[])
if (!client.connect())
return 2;
// Call the command
std::vector<std::string> args;
for (int i = 2; i < argc; i++)
args.push_back(argv[i]);
command_result rv;
command_result rv = client.run_command(argv[1], args);
if (rv != CR_OK) {
if (rv == CR_NOT_IMPLEMENTED)
out.printerr("%s is not a recognized command.\n", argv[1]);
if (strcmp(argv[1], "--lua") == 0)
{
if (argc <= 3)
{
fprintf(stderr, "Usage: dfhack-run --lua <module> <function> [args...]\n");
return 2;
}
RemoteFunction<dfproto::CoreRunLuaRequest,dfproto::StringListMessage> run_call;
if (!run_call.bind(&client, "RunLua"))
{
fprintf(stderr, "No RunLua protocol function found.");
return 3;
}
run_call.in()->set_module(argv[2]);
run_call.in()->set_function(argv[3]);
for (int i = 4; i < argc; i++)
run_call.in()->add_arguments(argv[i]);
rv = run_call();
out.flush();
if (rv == CR_OK)
{
for (int i = 0; i < run_call.out()->value_size(); i++)
printf("%s%s", (i>0?"\t":""), run_call.out()->value(i).c_str());
printf("\n");
}
}
else
{
// Call the command
std::vector<std::string> args;
for (int i = 2; i < argc; i++)
args.push_back(argv[i]);
return 1;
rv = client.run_command(argv[1], args);
}
out.flush();
if (rv != CR_OK)
return 1;
return 0;
}

@ -48,6 +48,8 @@ namespace DFHack
DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
const std::vector<std::string> &vec);
using dfproto::StringListMessage;
/**
* Represent bitfield bits as a repeated string field.
*/
@ -131,6 +133,8 @@ namespace DFHack
class CoreService : public RPCService {
int suspend_depth;
static int doRunLuaFunction(lua_State *L);
public:
CoreService();
~CoreService();
@ -144,5 +148,9 @@ namespace DFHack
// For batching
command_result CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
command_result CoreResume(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
command_result RunLua(color_ostream &stream,
const dfproto::CoreRunLuaRequest *in,
StringListMessage *out);
};
}

@ -1143,7 +1143,7 @@ MapExtras::MapCache::MapCache()
memset(biomes[i].layer_stone, -1, sizeof(biomes[i].layer_stone));
for (size_t j = 0; j < std::min(BiomeInfo::MAX_LAYERS,layer_mats[i].size()); j++)
for (size_t j = 0; j < std::min<size_t>(BiomeInfo::MAX_LAYERS,layer_mats[i].size()); j++)
{
biomes[i].layer_stone[j] = layer_mats[i][j];

@ -81,3 +81,10 @@ message CoreRunCommandRequest {
// RPC CoreSuspend : EmptyMessage -> IntMessage
// RPC CoreResume : EmptyMessage -> IntMessage
// RPC RunLua : CoreRunLuaRequest -> StringListMessage
message CoreRunLuaRequest {
required string module = 1;
required string function = 2;
repeated string arguments = 3;
}

@ -4,6 +4,7 @@
#include <Export.h>
#include <PluginManager.h>
#include <string.h>
#include <stdexcept>
#include <VTableInterpose.h>

@ -65,10 +65,8 @@ static bool getoptions( vector <string> & parameters, bool & shrubs, bool & tree
* And he cursed the plants and trees for their bloodless wood, turning them into ash and smoldering ruin.
* Armok was pleased and great temples were built by the dwarves, for they shared his hatred for trees and plants.
*/
static command_result immolations (color_ostream &out, do_what what, bool shrubs, bool trees, bool help)
static command_result immolations (color_ostream &out, do_what what, bool shrubs, bool trees)
{
if(help)
return CR_WRONG_USAGE;
CoreSuspender suspend;
if (!Maps::IsValid())
{
@ -132,32 +130,32 @@ static command_result immolations (color_ostream &out, do_what what, bool shrubs
return CR_OK;
}
command_result df_immolate (color_ostream &out, vector <string> & parameters)
command_result df_immolate (color_ostream &out, vector <string> & parameters, do_what what)
{
bool shrubs = false, trees = false, help = false;
if(getoptions(parameters,shrubs,trees,help))
if (getoptions(parameters, shrubs, trees, help) && !help)
{
return immolations(out,do_immolate,shrubs,trees,help);
return immolations(out, what, shrubs, trees);
}
else
{
out.printerr("Invalid parameter!\n");
return CR_WRONG_USAGE;
}
}
command_result df_extirpate (color_ostream &out, vector <string> & parameters)
{
bool shrubs = false, trees = false, help = false;
if(getoptions(parameters,shrubs,trees,help))
{
return immolations(out,do_extirpate,shrubs,trees,help);
}
string mode;
if (what == do_immolate)
mode = "Set plants on fire";
else
{
mode = "Kill plants";
if (!help)
out.printerr("Invalid parameter!\n");
return CR_WRONG_USAGE;
}
out << "Usage:\n" <<
mode << " (under cursor, 'shrubs', 'trees' or 'all').\n"
"Without any options, this command acts on the plant under the cursor.\n"
"Options:\n"
"shrubs - affect all shrubs\n"
"trees - affect all trees\n"
"all - affect all plants\n";
return CR_OK;
}
command_result df_grow (color_ostream &out, vector <string> & parameters)
@ -165,8 +163,14 @@ command_result df_grow (color_ostream &out, vector <string> & parameters)
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
return CR_WRONG_USAGE;
{
out << "Usage:\n"
"This command turns all living saplings on the map into full-grown trees.\n"
"With active cursor, work on the targetted one only.\n";
return CR_OK;
}
}
CoreSuspender suspend;
if (!Maps::IsValid())
@ -217,7 +221,13 @@ command_result df_grow (color_ostream &out, vector <string> & parameters)
command_result df_createplant (color_ostream &out, vector <string> & parameters)
{
if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?"))
return CR_WRONG_USAGE;
{
out << "Usage:\n"
"Create a new plant at the cursor.\n"
"Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
"Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n";
return CR_OK;
}
CoreSuspender suspend;
@ -308,29 +318,43 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
return CR_OK;
}
command_result df_plant (color_ostream &out, vector <string> & parameters)
{
if (parameters.size() >= 1)
{
if (parameters[0] == "grow") {
parameters.erase(parameters.begin());
return df_grow(out, parameters);
} else
if (parameters[0] == "immolate") {
parameters.erase(parameters.begin());
return df_immolate(out, parameters, do_immolate);
} else
if (parameters[0] == "extirpate") {
parameters.erase(parameters.begin());
return df_immolate(out, parameters, do_extirpate);
} else
if (parameters[0] == "create") {
parameters.erase(parameters.begin());
return df_createplant(out, parameters);
}
}
return CR_WRONG_USAGE;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand("grow", "Grows saplings into trees (with active cursor, only the targetted one).", df_grow, false,
"This command turns all living saplings on the map into full-grown trees.\n"));
commands.push_back(PluginCommand("immolate", "Set plants on fire (under cursor, 'shrubs', 'trees' or 'all').", df_immolate, false,
"Without any options, this command burns a plant under the cursor.\n"
"Options:\n"
"shrubs - affect all shrubs\n"
"trees - affect all trees\n"
"all - affect all plants\n"));
commands.push_back(PluginCommand("extirpate", "Kill plants (same mechanics as immolate).", df_extirpate, false,
"Without any options, this command destroys a plant under the cursor.\n"
"Options:\n"
"shrubs - affect all shrubs\n"
"trees - affect all trees\n"
"all - affect all plants\n"));
commands.push_back(PluginCommand("createplant", "Create a new plant at the cursor.", df_createplant, false,
"Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
"Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n"));
commands.push_back(PluginCommand("plant", "Plant creation and removal.", df_plant, false,
"Command to create, grow or remove plants on the map. For more details, check the subcommand help :\n"
"plant grow help - Grows saplings into trees.\n"
"plant immolate help - Set plants on fire.\n"
"plant extirpate help - Kill plants.\n"
"plant create help - Create a new plant.\n"));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
}

@ -169,7 +169,13 @@ command_result df_showmood (color_ostream &out, vector <string> & parameters)
// total amount of stuff fetched so far
int count_got = 0;
for (size_t i = 0; i < job->items.size(); i++)
count_got += 1; // XXX thread may need job->items[i]->item->getTotalDimension()
{
df::item_type type = job->job_items[i]->item_type;
if (type == item_type::BAR || type == item_type::CLOTH)
count_got += job->items[i]->item->getTotalDimension();
else
count_got += 1;
}
for (size_t i = 0; i < job->job_items.size(); i++)
{

@ -3839,12 +3839,12 @@ void unbutcherRace(int race)
static bool autobutcher_isEnabled() { return enable_autobutcher; }
static bool autowatch_isEnabled() { return enable_autobutcher_autowatch; }
static size_t autobutcher_getSleep(color_ostream &out)
static unsigned autobutcher_getSleep(color_ostream &out)
{
return sleep_autobutcher;
}
static void autobutcher_setSleep(color_ostream &out, size_t ticks)
static void autobutcher_setSleep(color_ostream &out, unsigned ticks)
{
sleep_autobutcher = ticks;
if(config_autobutcher.isValid())
@ -3892,7 +3892,7 @@ static void autowatch_setEnabled(color_ostream &out, bool enable)
// set all data for a watchlist race in one go
// if race is not already on watchlist it will be added
// params: (id, fk, mk, fa, ma, watched)
static void autobutcher_setWatchListRace(color_ostream &out, size_t id, size_t fk, size_t mk, size_t fa, size_t ma, bool watched)
static void autobutcher_setWatchListRace(color_ostream &out, unsigned id, unsigned fk, unsigned mk, unsigned fa, unsigned ma, bool watched)
{
int watched_index = getWatchedIndex(id);
if(watched_index != -1)
@ -3921,7 +3921,7 @@ static void autobutcher_setWatchListRace(color_ostream &out, size_t id, size_t f
}
// remove entry from watchlist
static void autobutcher_removeFromWatchList(color_ostream &out, size_t id)
static void autobutcher_removeFromWatchList(color_ostream &out, unsigned id)
{
int watched_index = getWatchedIndex(id);
if(watched_index != -1)
@ -3940,7 +3940,7 @@ static void autobutcher_sortWatchList(color_ostream &out)
}
// set default target values for new races
static void autobutcher_setDefaultTargetNew(color_ostream &out, size_t fk, size_t mk, size_t fa, size_t ma)
static void autobutcher_setDefaultTargetNew(color_ostream &out, unsigned fk, unsigned mk, unsigned fa, unsigned ma)
{
default_fk = fk;
default_mk = mk;
@ -3956,9 +3956,9 @@ static void autobutcher_setDefaultTargetNew(color_ostream &out, size_t fk, size_
}
// set default target values for ALL races (update watchlist and set new default)
static void autobutcher_setDefaultTargetAll(color_ostream &out, size_t fk, size_t mk, size_t fa, size_t ma)
static void autobutcher_setDefaultTargetAll(color_ostream &out, unsigned fk, unsigned mk, unsigned fa, unsigned ma)
{
for(size_t i=0; i<watched_races.size(); i++)
for(unsigned i=0; i<watched_races.size(); i++)
{
WatchedRace * w = watched_races[i];
w->fk = fk;
@ -3970,12 +3970,12 @@ static void autobutcher_setDefaultTargetAll(color_ostream &out, size_t fk, size_
autobutcher_setDefaultTargetNew(out, fk, mk, fa, ma);
}
static void autobutcher_butcherRace(color_ostream &out, size_t id)
static void autobutcher_butcherRace(color_ostream &out, unsigned id)
{
butcherRace(id);
}
static void autobutcher_unbutcherRace(color_ostream &out, size_t id)
static void autobutcher_unbutcherRace(color_ostream &out, unsigned id)
{
unbutcherRace(id);
}