Skip to content

Commit

Permalink
Add built-in enable and disable commands.
Browse files Browse the repository at this point in the history
  • Loading branch information
angavrilov committed Sep 30, 2013
1 parent a487ede commit d7e35c2
Showing 37 changed files with 607 additions and 114 deletions.
19 changes: 19 additions & 0 deletions Readme.rst
Original file line number Diff line number Diff line change
@@ -209,6 +209,25 @@ or is a prefix ending at a '/' boundary would be considered for execution, i.e.
for context ``foo/bar/baz``, possible matches are any of ``@foo/bar/baz``, ``@foo/bar``,
``@foo`` or none.

Enabling plugins
================

Many plugins can be in a distinct enabled or disabled state. Some of
them activate and deactivate automatically depending on the contents
of the world raws. Others store their state in world data. However a
number of them have to be enabled globally, and the init file is the
right place to do it.

Most of such plugins support the built-in ``enable`` and ``disable``
commands. Calling them at any time without arguments prints a list
of enabled and disabled plugins, and shows whether that can be changed
through the same commands.

To enable or disable plugins that support this, use their names as
arguments for the command::

enable manipulator search


========
Commands
20 changes: 18 additions & 2 deletions dfhack.init-example
Original file line number Diff line number Diff line change
@@ -150,8 +150,24 @@ tweak military-training
# prevent crash if bees die in a hive with ungathered products by insta-gathering them
tweak hive-crash

# enable autoSyndrome
autoSyndrome enable
###########################
# Globally acting plugins #
###########################

# Dwarf Manipulator (simple in-game Dwarf Therapist replacement)
enable manipulator

# Search tool in various screens (by falconne)
enable search

# Improved build material selection interface (by falconne)
enable automaterial

# Other interface improvement tools
#enable dwarfmonitor mousequery autotrade buildingplan resume zone

# Auto Syndrome
#autoSyndrome enable

###########
# Scripts #
51 changes: 51 additions & 0 deletions library/Core.cpp
Original file line number Diff line number Diff line change
@@ -544,6 +544,56 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
}
}
}
else if( first == "enable" || first == "disable" )
{
CoreSuspender suspend;
bool enable = (first == "enable");

if(parts.size())
{
command_result res = CR_OK;

for (size_t i = 0; i < parts.size(); i++)
{
Plugin * plug = plug_mgr->getPluginByName(parts[i]);

if(!plug)
{
res = CR_NOT_FOUND;
con.printerr("No such plugin: %s\n", parts[i].c_str());
}
else if (!plug->can_set_enabled())
{
res = CR_NOT_IMPLEMENTED;
con.printerr("Cannot %s plugin: %s\n", first.c_str(), parts[i].c_str());
}
else
{
res = plug->set_enabled(con, enable);

if (res != CR_OK || plug->is_enabled() != enable)
con.printerr("Could not %s plugin: %s\n", first.c_str(), parts[i].c_str());
}
}

return res;
}
else
{
for(size_t i = 0; i < plug_mgr->size();i++)
{
Plugin * plug = (plug_mgr->operator[](i));
if (!plug->can_be_enabled()) continue;

con.print(
"%20s\t%-3s%s\n",
(plug->getName()+":").c_str(),
plug->is_enabled() ? "on" : "off",
plug->can_set_enabled() ? "" : " (controlled elsewhere)"
);
}
}
}
else if(first == "ls" || first == "dir")
{
bool all = false;
@@ -584,6 +634,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
" load PLUGIN|all - Load a plugin by name or load all possible plugins.\n"
" unload PLUGIN|all - Unload a plugin or all loaded plugins.\n"
" reload PLUGIN|all - Reload a plugin or all loaded plugins.\n"
" enable/disable PLUGIN - Enable or disable a plugin if supported.\n"
"\n"
"plugins:\n"
);
75 changes: 75 additions & 0 deletions library/PluginManager.cpp
Original file line number Diff line number Diff line change
@@ -172,6 +172,8 @@ Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _f
plugin_onupdate = 0;
plugin_onstatechange = 0;
plugin_rpcconnect = 0;
plugin_enable = 0;
plugin_is_enabled = 0;
state = PS_UNLOADED;
access = new RefLock();
}
@@ -245,6 +247,8 @@ bool Plugin::load(color_ostream &con)
plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown");
plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange");
plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect");
plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable");
plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled");
plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby");
index_lua(plug);
this->name = *plug_name;
@@ -254,11 +258,15 @@ bool Plugin::load(color_ostream &con)
{
state = PS_LOADED;
parent->registerCommands(this);
if ((plugin_onupdate || plugin_enable) && !plugin_is_enabled)
con.printerr("Plugin %s has no enabled var!\n", name.c_str());
return true;
}
else
{
con.printerr("Plugin %s has failed to initialize properly.\n", filename.c_str());
plugin_is_enabled = 0;
plugin_onupdate = 0;
reset_lua();
ClosePlugin(plugin_lib);
state = PS_BROKEN;
@@ -294,6 +302,8 @@ bool Plugin::unload(color_ostream &con)
if(plugin_shutdown)
cr = plugin_shutdown(con);
// cleanup...
plugin_is_enabled = 0;
plugin_onupdate = 0;
reset_lua();
parent->unregisterCommands(this);
commands.clear();
@@ -408,6 +418,12 @@ bool Plugin::can_invoke_hotkey(const std::string & command, df::viewscreen *top

command_result Plugin::on_update(color_ostream &out)
{
// Check things that are implicitly protected by the suspend lock
if (!plugin_onupdate)
return CR_NOT_IMPLEMENTED;
if (plugin_is_enabled && !*plugin_is_enabled)
return CR_OK;
// Grab mutex and call the thing
command_result cr = CR_NOT_IMPLEMENTED;
access->lock_add();
if(state == PS_LOADED && plugin_onupdate)
@@ -419,6 +435,21 @@ command_result Plugin::on_update(color_ostream &out)
return cr;
}

command_result Plugin::set_enabled(color_ostream &out, bool enable)
{
command_result cr = CR_NOT_IMPLEMENTED;
access->lock_add();
if(state == PS_LOADED && plugin_is_enabled && plugin_enable)
{
cr = plugin_enable(out, enable);

if (cr == CR_OK && enable != is_enabled())
cr = CR_FAILURE;
}
access->lock_sub();
return cr;
}

command_result Plugin::on_state_change(color_ostream &out, state_change_event event)
{
command_result cr = CR_NOT_IMPLEMENTED;
@@ -524,6 +555,37 @@ void Plugin::reset_lua()
}
}

int Plugin::lua_is_enabled(lua_State *state)
{
auto obj = (Plugin*)lua_touserdata(state, lua_upvalueindex(1));

RefAutoinc lock(obj->access);
if (obj->state == PS_LOADED && obj->plugin_is_enabled)
lua_pushboolean(state, obj->is_enabled());
else
lua_pushnil(state);

return 1;
}

int Plugin::lua_set_enabled(lua_State *state)
{
lua_settop(state, 1);
bool val = lua_toboolean(state, 1);

auto obj = (Plugin*)lua_touserdata(state, lua_upvalueindex(1));
RefAutoinc lock(obj->access);

color_ostream *out = Lua::GetOutput(state);

if (obj->state == PS_LOADED && obj->plugin_enable)
lua_pushboolean(state, obj->set_enabled(*out, val) == CR_OK);
else
luaL_error(state, "plugin %s unloaded, cannot enable or disable", obj->name.c_str());

return 1;
}

int Plugin::lua_cmd_wrapper(lua_State *state)
{
auto cmd = (LuaCommand*)lua_touserdata(state, lua_upvalueindex(1));
@@ -561,6 +623,19 @@ void Plugin::open_lua(lua_State *state, int table)

RefAutolock lock(access);

if (plugin_is_enabled)
{
lua_pushlightuserdata(state, this);
lua_pushcclosure(state, lua_is_enabled, 1);
lua_setfield(state, table, "isEnabled");
}
if (plugin_enable)
{
lua_pushlightuserdata(state, this);
lua_pushcclosure(state, lua_set_enabled, 1);
lua_setfield(state, table, "setEnabled");
}

for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it)
{
lua_pushlightuserdata(state, it->second);
14 changes: 14 additions & 0 deletions library/include/PluginManager.h
Original file line number Diff line number Diff line change
@@ -144,6 +144,11 @@ namespace DFHack
bool unload(color_ostream &out);
bool reload(color_ostream &out);

bool can_be_enabled() { return plugin_is_enabled != 0; }
bool is_enabled() { return plugin_is_enabled && *plugin_is_enabled; }
bool can_set_enabled() { return plugin_is_enabled != 0 && plugin_enable; }
command_result set_enabled(color_ostream &out, bool enable);

command_result invoke(color_ostream &out, const std::string & command, std::vector <std::string> & parameters);
bool can_invoke_hotkey(const std::string & command, df::viewscreen *top );
plugin_state getState () const;
@@ -184,17 +189,22 @@ namespace DFHack
static int lua_fun_wrapper(lua_State *state);
void push_function(lua_State *state, LuaFunction *fn);

static int lua_is_enabled(lua_State *state);
static int lua_set_enabled(lua_State *state);

struct LuaEvent;
std::map<std::string, LuaEvent*> lua_events;

void index_lua(DFLibrary *lib);
void reset_lua();

bool *plugin_is_enabled;
command_result (*plugin_init)(color_ostream &, std::vector <PluginCommand> &);
command_result (*plugin_status)(color_ostream &, std::string &);
command_result (*plugin_shutdown)(color_ostream &);
command_result (*plugin_onupdate)(color_ostream &);
command_result (*plugin_onstatechange)(color_ostream &, state_change_event);
command_result (*plugin_enable)(color_ostream &, bool);
RPCService* (*plugin_rpcconnect)(color_ostream &);
command_result (*plugin_eval_ruby)(color_ostream &, const char*);
};
@@ -250,6 +260,10 @@ namespace DFHack
DFhackDataExport const char * name = plugin_name;\
DFhackDataExport Plugin *plugin_self = NULL;

#define DFHACK_PLUGIN_IS_ENABLED(varname) \
DFhackDataExport bool plugin_is_enabled = false; \
bool &varname = plugin_is_enabled;

#define DFHACK_PLUGIN_LUA_COMMANDS \
DFhackCExport const DFHack::CommandReg plugin_lua_commands[] =
#define DFHACK_PLUGIN_LUA_FUNCTIONS \
2 changes: 2 additions & 0 deletions plugins/add-spatter.cpp
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ using df::global::ui;
typedef df::reaction_product_item_improvementst improvement_product;

DFHACK_PLUGIN("add-spatter");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);

struct ReagentSource {
int idx;
@@ -390,6 +391,7 @@ static bool find_reactions(color_ostream &out)

static void enable_hooks(bool enable)
{
is_enabled = enable;
INTERPOSE_HOOK(item_hook, isImprovable).apply(enable);
INTERPOSE_HOOK(product_hook, produce).apply(enable);
}
2 changes: 1 addition & 1 deletion plugins/advtools.cpp
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )

df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap);

static bool in_transient_swap = false;
DFHACK_PLUGIN_IS_ENABLED(in_transient_swap);

DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
42 changes: 25 additions & 17 deletions plugins/autoSyndrome.cpp
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ reaction_duck
Next, start a new fort in a new world, build a duck workshop, then have someone become a duck.
*/

bool enabled = false;
DFHACK_PLUGIN_IS_ENABLED(enabled);

DFHACK_PLUGIN("autoSyndrome");

@@ -139,37 +139,45 @@ DFhackCExport command_result plugin_shutdown(color_ostream& out) {
return CR_OK;
}*/

DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (enabled == enable)
return CR_OK;

enabled = enable;

Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome");
if ( enabled ) {
EventManager::EventHandler handle(processJob, 5);
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me);
} else {
EventManager::unregisterAll(me);
}

return CR_OK;
}

command_result autoSyndrome(color_ostream& out, vector<string>& parameters) {
if ( parameters.size() > 1 )
return CR_WRONG_USAGE;

bool wasEnabled = enabled;
bool enable = false;
if ( parameters.size() == 1 ) {
if ( parameters[0] == "enable" ) {
enabled = true;
enable = true;
} else if ( parameters[0] == "disable" ) {
enabled = false;
enable = false;
} else {
int32_t a = atoi(parameters[0].c_str());
if ( a < 0 || a > 1 )
return CR_WRONG_USAGE;

enabled = (bool)a;
enable = (bool)a;
}
}

out.print("autoSyndrome is %s\n", enabled ? "enabled" : "disabled");
if ( enabled == wasEnabled )
return CR_OK;

Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome");
if ( enabled ) {
EventManager::EventHandler handle(processJob, 5);
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me);
} else {
EventManager::unregisterAll(me);
}
return CR_OK;
out.print("autoSyndrome is %s\n", enable ? "enabled" : "disabled");
return plugin_enable(out, enable);
}

bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df::unit* unit) {
Loading

0 comments on commit d7e35c2

Please sign in to comment.