Add support for exporting functions from plugins, with example in rename.

TODO: test by actually calling them remotely.
develop
Alexander Gavrilov 2012-03-15 13:01:23 +04:00
parent e7851f5abd
commit 87f925e72e
13 changed files with 248 additions and 67 deletions

@ -165,7 +165,7 @@ void fHKthread(void * iodata)
string first = args[0]; string first = args[0];
args.erase(args.begin()); args.erase(args.begin());
command_result cr = plug_mgr->InvokeCommand(out, first, args, false); command_result cr = plug_mgr->InvokeCommand(out, first, args);
if(cr == CR_WOULD_BREAK) if(cr == CR_WOULD_BREAK)
{ {
@ -272,7 +272,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
for(size_t i = 0; i < plug_mgr->size();i++) for(size_t i = 0; i < plug_mgr->size();i++)
{ {
Plugin * plug = (plug_mgr->operator[](i)); Plugin * plug = (plug_mgr->operator[](i));
plug->load(); plug->load(con);
} }
} }
else else
@ -284,7 +284,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
} }
else else
{ {
plug->load(); plug->load(con);
} }
} }
} }
@ -299,7 +299,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
for(size_t i = 0; i < plug_mgr->size();i++) for(size_t i = 0; i < plug_mgr->size();i++)
{ {
Plugin * plug = (plug_mgr->operator[](i)); Plugin * plug = (plug_mgr->operator[](i));
plug->reload(); plug->reload(con);
} }
} }
else else
@ -311,7 +311,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
} }
else else
{ {
plug->reload(); plug->reload(con);
} }
} }
} }
@ -326,7 +326,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
for(size_t i = 0; i < plug_mgr->size();i++) for(size_t i = 0; i < plug_mgr->size();i++)
{ {
Plugin * plug = (plug_mgr->operator[](i)); Plugin * plug = (plug_mgr->operator[](i));
plug->unload(); plug->unload(con);
} }
} }
else else
@ -338,7 +338,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
} }
else else
{ {
plug->unload(); plug->unload(con);
} }
} }
} }

@ -26,9 +26,11 @@ distribution.
#include "Core.h" #include "Core.h"
#include "MemAccess.h" #include "MemAccess.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "RemoteServer.h"
#include "Console.h" #include "Console.h"
#include "DataDefs.h" #include "DataDefs.h"
#include "MiscUtils.h"
using namespace DFHack; using namespace DFHack;
@ -146,6 +148,7 @@ Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _f
plugin_status = 0; plugin_status = 0;
plugin_onupdate = 0; plugin_onupdate = 0;
plugin_onstatechange = 0; plugin_onstatechange = 0;
plugin_rpcconnect = 0;
state = PS_UNLOADED; state = PS_UNLOADED;
access = new RefLock(); access = new RefLock();
} }
@ -154,12 +157,12 @@ Plugin::~Plugin()
{ {
if(state == PS_LOADED) if(state == PS_LOADED)
{ {
unload(); unload(Core::getInstance().getConsole());
} }
delete access; delete access;
} }
bool Plugin::load() bool Plugin::load(color_ostream &con)
{ {
RefAutolock lock(access); RefAutolock lock(access);
if(state == PS_BROKEN) if(state == PS_BROKEN)
@ -170,8 +173,6 @@ bool Plugin::load()
{ {
return true; return true;
} }
Core & c = Core::getInstance();
Console & con = c.getConsole();
DFLibrary * plug = OpenPlugin(filename.c_str()); DFLibrary * plug = OpenPlugin(filename.c_str());
if(!plug) if(!plug)
{ {
@ -208,6 +209,7 @@ bool Plugin::load()
plugin_onupdate = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_onupdate"); plugin_onupdate = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_onupdate");
plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown"); plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown");
plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange"); plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange");
plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect");
this->name = *plug_name; this->name = *plug_name;
plugin_lib = plug; plugin_lib = plug;
if(plugin_init(con,commands) == CR_OK) if(plugin_init(con,commands) == CR_OK)
@ -225,10 +227,8 @@ bool Plugin::load()
} }
} }
bool Plugin::unload() bool Plugin::unload(color_ostream &con)
{ {
Core & c = Core::getInstance();
Console & con = c.getConsole();
// get the mutex // get the mutex
access->lock(); access->lock();
// if we are actually loaded // if we are actually loaded
@ -266,18 +266,18 @@ bool Plugin::unload()
return false; return false;
} }
bool Plugin::reload() bool Plugin::reload(color_ostream &out)
{ {
if(state != PS_LOADED) if(state != PS_LOADED)
return false; return false;
if(!unload()) if(!unload(out))
return false; return false;
if(!load()) if(!load(out))
return false; return false;
return true; return true;
} }
command_result Plugin::invoke(color_ostream &out, std::string & command, std::vector <std::string> & parameters, bool interactive_) command_result Plugin::invoke(color_ostream &out, const std::string & command, std::vector <std::string> & parameters)
{ {
Core & c = Core::getInstance(); Core & c = Core::getInstance();
command_result cr = CR_NOT_IMPLEMENTED; command_result cr = CR_NOT_IMPLEMENTED;
@ -290,7 +290,7 @@ command_result Plugin::invoke(color_ostream &out, std::string & command, std::ve
if(cmd.name == command) if(cmd.name == command)
{ {
// running interactive things from some other source than the console would break it // running interactive things from some other source than the console would break it
if(!(interactive_ && out.is_console()) && cmd.interactive) if(!out.is_console() && cmd.interactive)
cr = CR_WOULD_BREAK; cr = CR_WOULD_BREAK;
else if (cmd.guard) else if (cmd.guard)
{ {
@ -325,7 +325,7 @@ command_result Plugin::invoke(color_ostream &out, std::string & command, std::ve
return cr; return cr;
} }
bool Plugin::can_invoke_hotkey( std::string & command, df::viewscreen *top ) bool Plugin::can_invoke_hotkey(const std::string & command, df::viewscreen *top )
{ {
Core & c = Core::getInstance(); Core & c = Core::getInstance();
bool cr = false; bool cr = false;
@ -375,6 +375,42 @@ command_result Plugin::on_state_change(color_ostream &out, state_change_event ev
return cr; return cr;
} }
RPCService *Plugin::rpc_connect(color_ostream &out)
{
RPCService *rv = NULL;
access->lock_add();
if(state == PS_LOADED && plugin_rpcconnect)
{
rv = plugin_rpcconnect(out);
}
if (rv)
{
// Retain the access reference
assert(!rv->holder);
services.push_back(rv);
rv->holder = this;
return rv;
}
else
{
access->lock_sub();
return NULL;
}
}
void Plugin::detach_connection(RPCService *svc)
{
int idx = linear_index(services, svc);
assert(svc->holder == this && idx >= 0);
vector_erase_at(services, idx);
access->lock_sub();
}
Plugin::plugin_state Plugin::getState() const Plugin::plugin_state Plugin::getState() const
{ {
return state; return state;
@ -399,7 +435,7 @@ PluginManager::PluginManager(Core * core)
Plugin * p = new Plugin(core, path + filez[i], filez[i], this); Plugin * p = new Plugin(core, path + filez[i], filez[i], this);
all_plugins.push_back(p); all_plugins.push_back(p);
// make all plugins load by default (until a proper design emerges). // make all plugins load by default (until a proper design emerges).
p->load(); p->load(core->getConsole());
} }
} }
} }
@ -435,13 +471,13 @@ Plugin *PluginManager::getPluginByCommand(const std::string &command)
} }
// FIXME: handle name collisions... // FIXME: handle name collisions...
command_result PluginManager::InvokeCommand(color_ostream &out, std::string & command, std::vector <std::string> & parameters, bool interactive) command_result PluginManager::InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters)
{ {
Plugin *plugin = getPluginByCommand(command); Plugin *plugin = getPluginByCommand(command);
return plugin ? plugin->invoke(out, command, parameters, interactive) : CR_NOT_IMPLEMENTED; return plugin ? plugin->invoke(out, command, parameters) : CR_NOT_IMPLEMENTED;
} }
bool PluginManager::CanInvokeHotkey(std::string &command, df::viewscreen *top) bool PluginManager::CanInvokeHotkey(const std::string &command, df::viewscreen *top)
{ {
Plugin *plugin = getPluginByCommand(command); Plugin *plugin = getPluginByCommand(command);
return plugin ? plugin->can_invoke_hotkey(command, top) : false; return plugin ? plugin->can_invoke_hotkey(command, top) : false;

@ -75,7 +75,7 @@ command_result CoreService::BindMethod(color_ostream &stream,
const dfproto::CoreBindRequest *in, const dfproto::CoreBindRequest *in,
dfproto::CoreBindReply *out) dfproto::CoreBindReply *out)
{ {
ServerFunctionBase *fn = connection()->findFunction(in->plugin(), in->method()); ServerFunctionBase *fn = connection()->findFunction(stream, in->plugin(), in->method());
if (!fn) if (!fn)
{ {
@ -104,7 +104,7 @@ command_result CoreService::RunCommand(color_ostream &stream,
for (int i = 0; i < in->arguments_size(); i++) for (int i = 0; i < in->arguments_size(); i++)
args.push_back(in->arguments(i)); args.push_back(in->arguments(i));
return Core::getInstance().plug_mgr->InvokeCommand(stream, cmd, args, false); return Core::getInstance().plug_mgr->InvokeCommand(stream, cmd, args);
} }
RPCService::RPCService() RPCService::RPCService()
@ -115,6 +115,9 @@ RPCService::RPCService()
RPCService::~RPCService() RPCService::~RPCService()
{ {
if (holder)
holder->detach_connection(this);
for (size_t i = 0; i < functions.size(); i++) for (size_t i = 0; i < functions.size(); i++)
delete functions[i]; delete functions[i];
} }
@ -163,12 +166,37 @@ ServerConnection::~ServerConnection()
delete core_service; delete core_service;
} }
ServerFunctionBase *ServerConnection::findFunction(const std::string &plugin, const std::string &name) ServerFunctionBase *ServerConnection::findFunction(color_ostream &out, const std::string &plugin, const std::string &name)
{ {
RPCService *svc;
if (plugin.empty()) if (plugin.empty())
return core_service->getFunction(name); svc = core_service;
else else
return NULL; // todo: add plugin api support {
svc = plugin_services[plugin];
if (!svc)
{
Plugin *plug = Core::getInstance().plug_mgr->getPluginByName(plugin);
if (!plug)
{
out.printerr("No such plugin: %s\n", plugin.c_str());
return NULL;
}
svc = plug->rpc_connect(out);
if (!svc)
{
out.printerr("Plugin %s doesn't export any RPC methods.\n", plugin.c_str());
return NULL;
}
plugin_services[plugin] = svc;
}
}
return svc->getFunction(name);
} }
void ServerConnection::connection_ostream::flush_proxy() void ServerConnection::connection_ostream::flush_proxy()

@ -198,6 +198,7 @@ namespace DFHack
std::map<std::string,void*> misc_data_map; std::map<std::string,void*> misc_data_map;
friend class CoreService; friend class CoreService;
friend class ServerConnection;
ServerMain *server; ServerMain *server;
}; };

@ -48,6 +48,7 @@ namespace DFHack
class Core; class Core;
class PluginManager; class PluginManager;
class virtual_identity; class virtual_identity;
class RPCService;
enum state_change_event enum state_change_event
{ {
@ -106,17 +107,23 @@ namespace DFHack
PS_BROKEN PS_BROKEN
}; };
friend class PluginManager; friend class PluginManager;
friend class RPCService;
Plugin(DFHack::Core* core, const std::string& filepath, const std::string& filename, PluginManager * pm); Plugin(DFHack::Core* core, const std::string& filepath, const std::string& filename, PluginManager * pm);
~Plugin(); ~Plugin();
command_result on_update(color_ostream &out); command_result on_update(color_ostream &out);
command_result on_state_change(color_ostream &out, state_change_event event); command_result on_state_change(color_ostream &out, state_change_event event);
void detach_connection(RPCService *svc);
public: public:
bool load(); bool load(color_ostream &out);
bool unload(); bool unload(color_ostream &out);
bool reload(); bool reload(color_ostream &out);
command_result invoke(color_ostream &out, std::string & command, std::vector <std::string> & parameters, bool interactive );
bool can_invoke_hotkey( std::string & command, df::viewscreen *top ); 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; plugin_state getState () const;
RPCService *rpc_connect(color_ostream &out);
const PluginCommand& operator[] (std::size_t index) const const PluginCommand& operator[] (std::size_t index) const
{ {
return commands[index]; return commands[index];
@ -132,6 +139,7 @@ namespace DFHack
private: private:
RefLock * access; RefLock * access;
std::vector <PluginCommand> commands; std::vector <PluginCommand> commands;
std::vector <RPCService*> services;
std::string filename; std::string filename;
std::string name; std::string name;
DFLibrary * plugin_lib; DFLibrary * plugin_lib;
@ -142,6 +150,7 @@ namespace DFHack
command_result (*plugin_shutdown)(color_ostream &); command_result (*plugin_shutdown)(color_ostream &);
command_result (*plugin_onupdate)(color_ostream &); command_result (*plugin_onupdate)(color_ostream &);
command_result (*plugin_onstatechange)(color_ostream &, state_change_event); command_result (*plugin_onstatechange)(color_ostream &, state_change_event);
RPCService* (*plugin_rpcconnect)(color_ostream &);
}; };
class DFHACK_EXPORT PluginManager class DFHACK_EXPORT PluginManager
{ {
@ -158,8 +167,8 @@ namespace DFHack
public: public:
Plugin *getPluginByName (const std::string & name); Plugin *getPluginByName (const std::string & name);
Plugin *getPluginByCommand (const std::string &command); Plugin *getPluginByCommand (const std::string &command);
command_result InvokeCommand(color_ostream &out, std::string & command, std::vector <std::string> & parameters, bool interactive = true ); command_result InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters);
bool CanInvokeHotkey(std::string &command, df::viewscreen *top); bool CanInvokeHotkey(const std::string &command, df::viewscreen *top);
Plugin* operator[] (std::size_t index) Plugin* operator[] (std::size_t index)
{ {
if(index >= all_plugins.size()) if(index >= all_plugins.size())

@ -37,11 +37,12 @@ namespace DFHack
enum command_result enum command_result
{ {
CR_WOULD_BREAK = -2, CR_WOULD_BREAK = -2, // Attempt to call interactive command without console
CR_NOT_IMPLEMENTED = -1, CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded
CR_OK = 0, CR_OK = 0, // Success
CR_FAILURE = 1, CR_FAILURE = 1, // Failure
CR_WRONG_USAGE = 2 CR_WRONG_USAGE = 2, // Wrong arguments or ui state
CR_NOT_FOUND = 3 // Target object not found (for RPC mainly)
}; };
enum DFHackReplyCode : int16_t { enum DFHackReplyCode : int16_t {

@ -132,6 +132,7 @@ namespace DFHack
class DFHACK_EXPORT RPCService { class DFHACK_EXPORT RPCService {
friend class ServerConnection; friend class ServerConnection;
friend class Plugin;
std::vector<ServerFunctionBase*> functions; std::vector<ServerFunctionBase*> functions;
std::map<std::string, ServerFunctionBase*> lookup; std::map<std::string, ServerFunctionBase*> lookup;
@ -225,7 +226,7 @@ namespace DFHack
ServerConnection(CActiveSocket *socket); ServerConnection(CActiveSocket *socket);
~ServerConnection(); ~ServerConnection();
ServerFunctionBase *findFunction(const std::string &plugin, const std::string &name); ServerFunctionBase *findFunction(color_ostream &out, const std::string &plugin, const std::string &name);
}; };
class DFHACK_EXPORT ServerMain { class DFHACK_EXPORT ServerMain {

@ -37,6 +37,7 @@ message CoreErrorNotification {
CR_OK = 0; CR_OK = 0;
CR_FAILURE = 1; CR_FAILURE = 1;
CR_WRONG_USAGE = 2; CR_WRONG_USAGE = 2;
CR_NOT_FOUND = 3;
}; };
required ErrorCode code = 1; required ErrorCode code = 1;

@ -45,6 +45,21 @@ if (BUILD_DWARFEXPORT)
add_subdirectory (dwarfexport) add_subdirectory (dwarfexport)
endif() endif()
# Protobuf
FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto)
STRING(REPLACE ".proto" ".pb.cc" PROJECT_PROTO_SRCS "${PROJECT_PROTOS}")
STRING(REPLACE ".proto" ".pb.h" PROJECT_PROTO_HDRS "${PROJECT_PROTOS}")
ADD_CUSTOM_COMMAND(
OUTPUT ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS}
COMMAND protoc-bin -I=${dfhack_SOURCE_DIR}/proto/ -I=${CMAKE_CURRENT_SOURCE_DIR}/proto/
--cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/proto/
${PROJECT_PROTOS}
DEPENDS protoc-bin ${PROJECT_PROTOS}
)
# Plugins
OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON) OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON)
if (BUILD_SUPPORTED) if (BUILD_SUPPORTED)
DFHACK_PLUGIN(reveal reveal.cpp) DFHACK_PLUGIN(reveal reveal.cpp)
@ -70,7 +85,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(seedwatch seedwatch.cpp) DFHACK_PLUGIN(seedwatch seedwatch.cpp)
DFHACK_PLUGIN(initflags initflags.cpp) DFHACK_PLUGIN(initflags initflags.cpp)
DFHACK_PLUGIN(stockpiles stockpiles.cpp) DFHACK_PLUGIN(stockpiles stockpiles.cpp)
DFHACK_PLUGIN(rename rename.cpp) DFHACK_PLUGIN(rename rename.cpp PROTOBUFS rename)
DFHACK_PLUGIN(jobutils jobutils.cpp) DFHACK_PLUGIN(jobutils jobutils.cpp)
DFHACK_PLUGIN(workflow workflow.cpp) DFHACK_PLUGIN(workflow workflow.cpp)
DFHACK_PLUGIN(showmood showmood.cpp) DFHACK_PLUGIN(showmood showmood.cpp)

@ -8,6 +8,7 @@ ENDIF()
include_directories("${dfhack_SOURCE_DIR}/library/include") include_directories("${dfhack_SOURCE_DIR}/library/include")
include_directories("${dfhack_SOURCE_DIR}/library/proto") include_directories("${dfhack_SOURCE_DIR}/library/proto")
include_directories("${dfhack_SOURCE_DIR}/library/depends/xgetopt") include_directories("${dfhack_SOURCE_DIR}/library/depends/xgetopt")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/proto")
MACRO(CAR var) MACRO(CAR var)
SET(${var} ${ARGV1}) SET(${var} ${ARGV1})
@ -58,21 +59,28 @@ ENDMACRO()
MACRO(DFHACK_PLUGIN) MACRO(DFHACK_PLUGIN)
PARSE_ARGUMENTS(PLUGIN PARSE_ARGUMENTS(PLUGIN
"LINK_LIBRARIES;DEPENDS" "LINK_LIBRARIES;DEPENDS;PROTOBUFS"
"SOME_OPT" "SOME_OPT"
${ARGN} ${ARGN}
) )
CAR(PLUGIN_NAME ${PLUGIN_DEFAULT_ARGS}) CAR(PLUGIN_NAME ${PLUGIN_DEFAULT_ARGS})
CDR(PLUGIN_SOURCES ${PLUGIN_DEFAULT_ARGS}) CDR(PLUGIN_SOURCES ${PLUGIN_DEFAULT_ARGS})
FOREACH(pbuf ${PLUGIN_PROTOBUFS})
SET(PLUGIN_SOURCES ${PLUGIN_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/proto/${pbuf}.pb.cc)
ENDFOREACH()
ADD_LIBRARY(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES}) ADD_LIBRARY(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES})
IDE_FOLDER(${PLUGIN_NAME} "Plugins") IDE_FOLDER(${PLUGIN_NAME} "Plugins")
TARGET_LINK_LIBRARIES(${PLUGIN_NAME} dfhack ${PLUGIN_LINK_LIBRARIES}) TARGET_LINK_LIBRARIES(${PLUGIN_NAME} dfhack ${PLUGIN_LINK_LIBRARIES})
IF(UNIX) IF(UNIX)
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES SUFFIX .plug.so PREFIX "") SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES SUFFIX .plug.so PREFIX "")
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES COMPILE_FLAGS "-include Export.h")
ELSE() ELSE()
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES SUFFIX .plug.dll) SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES SUFFIX .plug.dll)
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"")
ENDIF() ENDIF()
install(TARGETS ${PLUGIN_NAME} install(TARGETS ${PLUGIN_NAME}
LIBRARY DESTINATION ${DFHACK_PLUGIN_DESTINATION} LIBRARY DESTINATION ${DFHACK_PLUGIN_DESTINATION}
RUNTIME DESTINATION ${DFHACK_PLUGIN_DESTINATION}) RUNTIME DESTINATION ${DFHACK_PLUGIN_DESTINATION})

@ -0,0 +1,3 @@
*.pb.cc
*.pb.cc.rule
*.pb.h

@ -0,0 +1,17 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
message RenameSquadRq {
required int32 squad_id = 1;
optional string nickname = 2;
optional string alias = 3;
}
message RenameUnitRq {
required int32 unit_id = 1;
optional string nickname = 2;
optional string profession = 3;
}

@ -17,6 +17,9 @@
#include "df/assumed_identity.h" #include "df/assumed_identity.h"
#include "df/language_name.h" #include "df/language_name.h"
#include "RemoteServer.h"
#include "rename.pb.h"
#include <stdlib.h> #include <stdlib.h>
using std::vector; using std::vector;
@ -24,6 +27,7 @@ using std::string;
using std::endl; using std::endl;
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
using namespace dfproto;
using df::global::ui; using df::global::ui;
using df::global::world; using df::global::world;
@ -79,6 +83,85 @@ static df::squad *getSquadByIndex(unsigned idx)
return df::squad::find(entity->squads[idx]); return df::squad::find(entity->squads[idx]);
} }
void setUnitNickname(df::unit *unit, const std::string &nick)
{
// There are >=3 copies of the name, and the one
// in the unit is not the authoritative one.
// This is the reason why military units often
// lose nicknames set from Dwarf Therapist.
set_nickname(&unit->name, nick);
if (unit->status.current_soul)
set_nickname(&unit->status.current_soul->name, nick);
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
if (figure)
{
set_nickname(&figure->name, nick);
// v0.34.01: added the vampire's assumed identity
if (figure->info && figure->info->reputation)
{
auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity);
if (identity)
{
auto id_hfig = df::historical_figure::find(identity->histfig_id);
if (id_hfig)
{
// Even DF doesn't do this bit, because it's apparently
// only used for demons masquerading as gods, so you
// can't ever change their nickname in-game.
set_nickname(&id_hfig->name, nick);
}
else
set_nickname(&identity->name, nick);
}
}
}
}
static command_result RenameSquad(color_ostream &stream, const RenameSquadRq *in)
{
CoreSuspender suspend;
df::squad *squad = df::squad::find(in->squad_id());
if (!squad)
return CR_NOT_FOUND;
if (in->has_nickname())
set_nickname(&squad->name, in->nickname());
if (in->has_alias())
squad->alias = in->alias();
return CR_OK;
}
static command_result RenameUnit(color_ostream &stream, const RenameUnitRq *in)
{
CoreSuspender suspend;
df::unit *unit = df::unit::find(in->unit_id());
if (!unit)
return CR_NOT_FOUND;
if (in->has_nickname())
setUnitNickname(unit, in->nickname());
if (in->has_profession())
unit->custom_profession = in->profession();
return CR_OK;
}
DFhackCExport RPCService *plugin_rpcconnect(color_ostream &)
{
RPCService *svc = new RPCService();
svc->addFunction("RenameSquad", RenameSquad);
svc->addFunction("RenameUnit", RenameUnit);
return svc;
}
static command_result rename(color_ostream &out, vector <string> &parameters) static command_result rename(color_ostream &out, vector <string> &parameters)
{ {
CoreSuspender suspend; CoreSuspender suspend;
@ -124,29 +207,7 @@ static command_result rename(color_ostream &out, vector <string> &parameters)
if (!unit) if (!unit)
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
// There are 3 copies of the name, and the one setUnitNickname(unit, parameters[1]);
// in the unit is not the authoritative one.
// This is the reason why military units often
// lose nicknames set from Dwarf Therapist.
set_nickname(&unit->name, parameters[1]);
if (unit->status.current_soul)
set_nickname(&unit->status.current_soul->name, parameters[1]);
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
if (figure)
{
set_nickname(&figure->name, parameters[1]);
// v0.34.01: added the vampire's assumed identity
if (figure->info && figure->info->reputation)
{
auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity);
if (identity)
set_nickname(&identity->name, parameters[1]);
}
}
} }
else if (cmd == "unit-profession") else if (cmd == "unit-profession")
{ {