Petr Mrázek 2012-03-18 04:48:02 +01:00
commit 293eb53677
30 changed files with 1243 additions and 251 deletions

@ -44,6 +44,7 @@ include/VersionInfoFactory.h
include/Virtual.h
include/RemoteClient.h
include/RemoteServer.h
include/RemoteTools.h
)
SET(MAIN_HEADERS_WINDOWS
@ -63,6 +64,7 @@ VersionInfoFactory.cpp
Virtual.cpp
RemoteClient.cpp
RemoteServer.cpp
RemoteTools.cpp
)
SET(MAIN_SOURCES_WINDOWS
@ -216,8 +218,7 @@ ENDIF()
ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES})
ADD_DEPENDENCIES(dfhack generate_headers)
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp
proto/CoreProtocol.pb.cc)
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ${PROJECT_PROTO_SRCS})
ADD_DEPENDENCIES(dfhack-client dfhack)
ADD_EXECUTABLE(dfhack-run dfhack-run.cpp)

@ -164,57 +164,92 @@ void virtual_identity::Init(Core *core)
*/
}
std::string DFHack::bitfieldToString(const void *p, int size, const bitfield_item_info *items)
bool DFHack::findBitfieldField(unsigned *idx, const std::string &name,
unsigned size, const bitfield_item_info *items)
{
std::string res;
const char *data = (const char*)p;
for (unsigned i = 0; i < size; i++) {
if (items[i].name && items[i].name == name)
{
*idx = i;
return true;
}
}
for (int i = 0; i < size; i++) {
unsigned v;
return false;
}
if (items[i].size > 1) {
unsigned pdv = *(unsigned*)&data[i/8];
v = (pdv >> (i%8)) & ((1 << items[i].size)-1);
} else {
v = (data[i/8]>>(i%8)) & 1;
}
void DFHack::setBitfieldField(void *p, unsigned idx, unsigned size, int value)
{
uint8_t *data = ((uint8_t*)p) + (idx/8);
unsigned shift = idx%8;
uint32_t mask = ((1<<size)-1) << shift;
uint32_t vmask = ((value << shift) & mask);
if (v) {
if (!res.empty())
res += ' ';
#define ACCESS(type) *(type*)data = type((*(type*)data & ~mask) | vmask)
if (items[i].name)
res += items[i].name;
else
res += stl_sprintf("UNK_%d", i);
if (!(mask & ~0xFFU)) ACCESS(uint8_t);
else if (!(mask & ~0xFFFFU)) ACCESS(uint16_t);
else ACCESS(uint32_t);
#undef ACCESS
}
int DFHack::getBitfieldField(const void *p, unsigned idx, unsigned size)
{
const uint8_t *data = ((const uint8_t*)p) + (idx/8);
unsigned shift = idx%8;
uint32_t mask = ((1<<size)-1) << shift;
#define ACCESS(type) return int((*(type*)data & mask) >> shift)
if (!(mask & ~0xFFU)) ACCESS(uint8_t);
else if (!(mask & ~0xFFFFU)) ACCESS(uint16_t);
else ACCESS(uint32_t);
#undef ACCESS
}
void DFHack::bitfieldToString(std::vector<std::string> *pvec, const void *p,
unsigned size, const bitfield_item_info *items)
{
for (unsigned i = 0; i < size; i++) {
int value = getBitfieldField(p, i, std::min(1,items[i].size));
if (value) {
std::string name = format_key(items[i].name, i);
if (items[i].size > 1)
res += stl_sprintf("=%u", v);
name += stl_sprintf("=%u", value);
pvec->push_back(name);
}
if (items[i].size > 1)
i += items[i].size-1;
}
return res;
}
int DFHack::findBitfieldField_(const std::string &name, int size, const bitfield_item_info *items)
int DFHack::findEnumItem(const std::string &name, int size, const char *const *items)
{
for (int i = 0; i < size; i++) {
if (items[i].name && items[i].name == name)
if (items[i] && items[i] == name)
return i;
}
return -1;
}
int DFHack::findEnumItem_(const std::string &name, int size, const char *const *items)
void DFHack::flagarrayToString(std::vector<std::string> *pvec, const void *p,
int bytes, int base, int size, const char *const *items)
{
for (int i = 0; i < size; i++) {
if (items[i] && items[i] == name)
return i;
for (unsigned i = 0; i < bytes*8; i++) {
int value = getBitfieldField(p, i, 1);
if (value)
{
int ridx = int(i) - base;
const char *name = (ridx >= 0 && ridx < size) ? items[ridx] : NULL;
pvec->push_back(format_key(name, i));
}
}
return -1;
}

@ -36,6 +36,8 @@ distribution.
#include <ctype.h>
#include <stdarg.h>
#include <sstream>
std::string stl_sprintf(const char *fmt, ...) {
va_list lst;
va_start(lst, fmt);
@ -82,6 +84,20 @@ bool split_string(std::vector<std::string> *out,
return out->size() > 1;
}
std::string join_strings(const std::string &separator, const std::vector<std::string> &items)
{
std::stringstream ss;
for (size_t i = 0; i < items.size(); i++)
{
if (i)
ss << separator;
ss << items[i];
}
return ss.str();
}
std::string toUpper(const std::string &str)
{
std::string rv(str.size(),' ');

@ -112,7 +112,7 @@ RemoteClient::~RemoteClient()
delete p_default_output;
}
bool DFHack::readFullBuffer(CSimpleSocket *socket, void *buf, int size)
bool readFullBuffer(CSimpleSocket *socket, void *buf, int size)
{
if (!socket->IsSocketValid())
return false;
@ -324,22 +324,20 @@ bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client,
return client->bind(out, this, name, proto);
}
bool DFHack::sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg, int *psz)
bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg, bool size_ready)
{
int size = msg->ByteSize();
int size = size_ready ? msg->GetCachedSize() : msg->ByteSize();
int fullsz = size + sizeof(RPCMessageHeader);
if (psz)
*psz = size;
std::auto_ptr<uint8_t> data(new uint8_t[fullsz]);
RPCMessageHeader *hdr = (RPCMessageHeader*)data.get();
hdr->id = id;
hdr->size = size;
if (!msg->SerializeToArray(data.get() + sizeof(RPCMessageHeader), size))
return false;
uint8_t *pstart = data.get() + sizeof(RPCMessageHeader);
uint8_t *pend = msg->SerializeWithCachedSizesToArray(pstart);
assert((pend - pstart) == size);
return (socket->Send(data.get(), fullsz) == fullsz);
}
@ -361,7 +359,16 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
return CR_LINK_FAILURE;
}
if (!sendRemoteMessage(p_client->socket, id, input))
int send_size = input->ByteSize();
if (send_size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{
out.printerr("In call to %s::%s: message too large: %d.\n",
this->proto.c_str(), this->name.c_str(), send_size);
return CR_LINK_FAILURE;
}
if (!sendRemoteMessage(p_client->socket, id, input, true))
{
out.printerr("In call to %s::%s: I/O error in send.\n",
this->proto.c_str(), this->name.c_str());
@ -388,7 +395,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (header.id == RPC_REPLY_FAIL)
return header.size == CR_OK ? CR_FAILURE : command_result(header.size);
if (header.size < 0 || header.size > 2*1048576)
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{
out.printerr("In call to %s::%s: invalid received size %d.\n",
this->proto.c_str(), this->name.c_str(), header.size);

@ -46,6 +46,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <stdint.h>
#include "RemoteServer.h"
#include "RemoteTools.h"
#include "PassiveSocket.h"
#include "PluginManager.h"
#include "MiscUtils.h"
@ -65,76 +67,10 @@ using dfproto::CoreTextNotification;
using dfproto::CoreTextFragment;
using google::protobuf::MessageLite;
CoreService::CoreService() {
suspend_depth = 0;
// These 2 methods must be first, so that they get id 0 and 1
addMethod("BindMethod", &CoreService::BindMethod);
addMethod("RunCommand", &CoreService::RunCommand);
// Add others here:
addMethod("CoreSuspend", &CoreService::CoreSuspend);
addMethod("CoreResume", &CoreService::CoreResume);
}
CoreService::~CoreService()
{
while (suspend_depth-- > 0)
Core::getInstance().Resume();
}
bool readFullBuffer(CSimpleSocket *socket, void *buf, int size);
bool sendRemoteMessage(CSimpleSocket *socket, int16_t id,
const ::google::protobuf::MessageLite *msg, bool size_ready);
command_result CoreService::BindMethod(color_ostream &stream,
const dfproto::CoreBindRequest *in,
dfproto::CoreBindReply *out)
{
ServerFunctionBase *fn = connection()->findFunction(stream, in->plugin(), in->method());
if (!fn)
{
stream.printerr("RPC method not found: %s::%s\n",
in->plugin().c_str(), in->method().c_str());
return CR_FAILURE;
}
if (fn->p_in_template->GetTypeName() != in->input_msg() ||
fn->p_out_template->GetTypeName() != in->output_msg())
{
stream.printerr("Requested wrong signature for RPC method: %s::%s\n",
in->plugin().c_str(), in->method().c_str());
return CR_FAILURE;
}
out->set_assigned_id(fn->getId());
return CR_OK;
}
command_result CoreService::RunCommand(color_ostream &stream,
const dfproto::CoreRunCommandRequest *in)
{
std::string cmd = in->command();
std::vector<std::string> args;
for (int i = 0; i < in->arguments_size(); i++)
args.push_back(in->arguments(i));
return Core::getInstance().plug_mgr->InvokeCommand(stream, cmd, args);
}
command_result CoreService::CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt)
{
Core::getInstance().Suspend();
cnt->set_value(++suspend_depth);
return CR_OK;
}
command_result CoreService::CoreResume(color_ostream &stream, const EmptyMessage*, IntMessage *cnt)
{
if (suspend_depth <= 0)
return CR_WRONG_USAGE;
Core::getInstance().Resume();
cnt->set_value(--suspend_depth);
return CR_OK;
}
RPCService::RPCService()
{
@ -252,7 +188,7 @@ void ServerConnection::connection_ostream::flush_proxy()
buffer.clear();
if (!sendRemoteMessage(owner->socket, RPC_REPLY_TEXT, &msg))
if (!sendRemoteMessage(owner->socket, RPC_REPLY_TEXT, &msg, false))
{
owner->in_error = true;
Core::printerr("Error writing text into client socket.\n");
@ -312,7 +248,7 @@ void ServerConnection::threadFn(void *arg)
if (header.id == RPC_REQUEST_QUIT)
break;
if (header.size < 0 || header.size > 2*1048576)
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{
out.printerr("In RPC server: invalid received size %d.\n", header.size);
break;
@ -347,6 +283,8 @@ void ServerConnection::threadFn(void *arg)
}
else
{
buf.reset();
reply = fn->out();
res = fn->execute(me->stream);
}
@ -356,16 +294,23 @@ void ServerConnection::threadFn(void *arg)
if (me->in_error)
break;
me->stream.flush();
//out.print("Answer %d:%d\n", res, reply);
// Send reply
int out_size = 0;
int out_size = (reply ? reply->ByteSize() : 0);
if (out_size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{
me->stream.printerr("In call to %s: reply too large: %d.\n",
(fn ? fn->name : "UNKNOWN"), out_size);
res = CR_LINK_FAILURE;
}
me->stream.flush();
if (res == CR_OK && reply)
{
if (!sendRemoteMessage(me->socket, RPC_REPLY_RESULT, reply, &out_size))
if (!sendRemoteMessage(me->socket, RPC_REPLY_RESULT, reply, true))
{
out.printerr("In RPC server: I/O error in send result.\n");
break;
@ -373,9 +318,6 @@ void ServerConnection::threadFn(void *arg)
}
else
{
if (reply)
out_size = reply->ByteSize();
header.id = RPC_REPLY_FAIL;
header.size = res;
@ -389,7 +331,8 @@ void ServerConnection::threadFn(void *arg)
// Cleanup
if (fn)
{
fn->reset(out_size > 32768 || in_size > 32768);
fn->reset((fn->flags & SF_CALLED_ONCE) ||
(out_size > 128*1024 || in_size > 32*1024));
}
}

@ -0,0 +1,488 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2011 Petr Mrázek <peterix@gmail.com>
A thread-safe logging console with a line editor for windows.
Based on linenoise win32 port,
copyright 2010, Jon Griffiths <jon_p_griffiths at yahoo dot com>.
All rights reserved.
Based on linenoise, copyright 2010, Salvatore Sanfilippo <antirez at gmail dot com>.
The original linenoise can be found at: http://github.com/antirez/linenoise
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>
#include "RemoteTools.h"
#include "PluginManager.h"
#include "MiscUtils.h"
#include "modules/Materials.h"
#include "modules/Translation.h"
#include "modules/Units.h"
#include "DataDefs.h"
#include "df/ui.h"
#include "df/unit.h"
#include "df/unit_soul.h"
#include "df/unit_skill.h"
#include "df/material.h"
#include "df/matter_state.h"
#include "df/inorganic_raw.h"
#include "df/creature_raw.h"
#include "df/plant_raw.h"
#include "df/historical_figure.h"
#include "df/historical_entity.h"
#include "df/squad.h"
#include "df/squad_position.h"
#include "BasicApi.pb.h"
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <memory>
using namespace DFHack;
using namespace df::enums;
using namespace dfproto;
using google::protobuf::MessageLite;
void DFHack::strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
const std::vector<std::string> &vec)
{
for (size_t i = 0; i < vec.size(); ++i)
*pf->Add() = vec[i];
}
void DFHack::describeEnum(RepeatedPtrField<EnumItemName> *pf, int base,
int size, const char* const *names)
{
for (int i = 0; i < size; i++)
{
auto item = pf->Add();
item->set_value(base+i);
const char *key = names[i];
if (key)
item->set_name(key);
}
}
void DFHack::describeBitfield(RepeatedPtrField<EnumItemName> *pf,
int size, const bitfield_item_info *items)
{
for (int i = 0; i < size; i++)
{
auto item = pf->Add();
item->set_value(i);
const char *key = items[i].name;
if (key)
item->set_name(key);
if (items[i].size > 1)
{
item->set_bit_size(items[i].size);
i += items[i].size-1;
}
}
}
void DFHack::describeMaterial(BasicMaterialInfo *info, df::material *mat,
const BasicMaterialInfoMask *mask)
{
info->set_token(mat->id);
if (mask && mask->flags())
flagarray_to_ints(info->mutable_flags(), mat->flags);
if (!mat->prefix.empty())
info->set_name_prefix(mat->prefix);
if (!mask || mask->states_size() == 0)
{
df::matter_state state = matter_state::Solid;
int temp = (mask && mask->has_temperature()) ? mask->temperature() : 10015;
if (temp >= mat->heat.melting_point)
state = matter_state::Liquid;
if (temp >= mat->heat.boiling_point)
state = matter_state::Gas;
info->add_state_color(mat->state_color[state]);
info->add_state_name(mat->state_name[state]);
info->add_state_adj(mat->state_adj[state]);
}
else
{
for (int i = 0; i < mask->states_size(); i++)
{
info->add_state_color(mat->state_color[i]);
info->add_state_name(mat->state_name[i]);
info->add_state_adj(mat->state_adj[i]);
}
}
if (mask && mask->reaction())
{
for (size_t i = 0; i < mat->reaction_class.size(); i++)
info->add_reaction_class(*mat->reaction_class[i]);
for (size_t i = 0; i < mat->reaction_product.id.size(); i++)
{
auto ptr = info->add_reaction_product();
ptr->set_id(*mat->reaction_product.id[i]);
ptr->set_type(mat->reaction_product.material.mat_type[i]);
ptr->set_index(mat->reaction_product.material.mat_index[i]);
}
}
}
void DFHack::describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat,
const BasicMaterialInfoMask *mask)
{
assert(mat.isValid());
info->set_type(mat.type);
info->set_index(mat.index);
describeMaterial(info, mat.material, mask);
switch (mat.mode) {
case MaterialInfo::Inorganic:
info->set_token(mat.inorganic->id);
if (mask && mask->flags())
flagarray_to_ints(info->mutable_inorganic_flags(), mat.inorganic->flags);
break;
case MaterialInfo::Creature:
info->set_subtype(mat.subtype);
if (mat.figure)
{
info->set_histfig_id(mat.index);
info->set_creature_id(mat.figure->race);
}
else
info->set_creature_id(mat.index);
break;
case MaterialInfo::Plant:
info->set_plant_id(mat.index);
break;
}
}
void DFHack::describeName(NameInfo *info, df::language_name *name)
{
if (!name->first_name.empty())
info->set_first_name(name->first_name);
if (!name->nickname.empty())
info->set_nickname(name->nickname);
if (name->language >= 0)
info->set_language_id(name->language);
std::string lname = Translation::TranslateName(name, false, true);
if (!lname.empty())
info->set_last_name(lname);
lname = Translation::TranslateName(name, true, true);
if (!lname.empty())
info->set_english_name(lname);
}
void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
const BasicUnitInfoMask *mask)
{
info->set_unit_id(unit->id);
info->set_pos_x(unit->pos.x);
info->set_pos_y(unit->pos.y);
info->set_pos_z(unit->pos.z);
auto name = Units::GetVisibleName(unit);
if (name->has_name)
describeName(info->mutable_name(), name);
info->set_flags1(unit->flags1.whole);
info->set_flags2(unit->flags2.whole);
info->set_flags3(unit->flags3.whole);
info->set_race(unit->race);
info->set_caste(unit->caste);
if (unit->sex >= 0)
info->set_gender(unit->sex);
if (unit->civ_id >= 0)
info->set_civ_id(unit->civ_id);
if (unit->hist_figure_id >= 0)
info->set_histfig_id(unit->hist_figure_id);
if (mask && mask->labors())
{
for (int i = 0; i < sizeof(unit->status.labors)/sizeof(bool); i++)
if (unit->status.labors[i])
info->add_labors(i);
}
if (mask && mask->skills() && unit->status.current_soul)
{
auto &vec = unit->status.current_soul->skills;
for (size_t i = 0; i < vec.size(); i++)
{
auto skill = vec[i];
auto item = info->add_skills();
item->set_id(skill->id);
item->set_level(skill->rating);
item->set_experience(skill->experience);
}
}
}
static command_result ListEnums(color_ostream &stream,
const EmptyMessage *, ListEnumsOut *out)
{
#define ENUM(name) describe_enum<df::name>(out->mutable_##name());
#define BITFIELD(name) describe_bitfield<df::name>(out->mutable_##name());
ENUM(material_flags);
ENUM(inorganic_flags);
BITFIELD(unit_flags1);
BITFIELD(unit_flags2);
BITFIELD(unit_flags3);
ENUM(unit_labor);
ENUM(job_skill);
#undef ENUM
#undef BITFIELD
}
static void listMaterial(ListMaterialsOut *out, int type, int index, const BasicMaterialInfoMask *mask)
{
MaterialInfo info(type, index);
if (info.isValid())
describeMaterial(out->add_value(), info, mask);
}
static command_result ListMaterials(color_ostream &stream,
const ListMaterialsIn *in, ListMaterialsOut *out)
{
CoreSuspender suspend;
auto mask = in->has_mask() ? &in->mask() : NULL;
for (int i = 0; i < in->id_list_size(); i++)
{
auto &elt = in->id_list(i);
listMaterial(out, elt.type(), elt.index(), mask);
}
if (in->builtin())
{
for (int i = 0; i < MaterialInfo::NUM_BUILTIN; i++)
listMaterial(out, i, -1, mask);
}
if (in->inorganic())
{
auto &vec = df::inorganic_raw::get_vector();
for (size_t i = 0; i < vec.size(); i++)
listMaterial(out, 0, i, mask);
}
if (in->creatures())
{
auto &vec = df::creature_raw::get_vector();
for (size_t i = 0; i < vec.size(); i++)
{
auto praw = vec[i];
for (size_t j = 0; j < praw->material.size(); j++)
listMaterial(out, MaterialInfo::CREATURE_BASE+j, i, mask);
}
}
if (in->plants())
{
auto &vec = df::plant_raw::get_vector();
for (size_t i = 0; i < vec.size(); i++)
{
auto praw = vec[i];
for (size_t j = 0; j < praw->material.size(); j++)
listMaterial(out, MaterialInfo::PLANT_BASE+j, i, mask);
}
}
return out->value_size() ? CR_OK : CR_NOT_FOUND;
}
static command_result ListUnits(color_ostream &stream,
const ListUnitsIn *in, ListUnitsOut *out)
{
CoreSuspender suspend;
auto mask = in->has_mask() ? &in->mask() : NULL;
if (in->id_list_size() > 0)
{
for (int i = 0; i < in->id_list_size(); i++)
{
auto unit = df::unit::find(in->id_list(i));
if (unit)
describeUnit(out->add_value(), unit, mask);
}
}
else
{
auto &vec = df::unit::get_vector();
for (size_t i = 0; i < vec.size(); i++)
{
auto unit = vec[i];
if (in->has_race() && unit->race != in->race())
continue;
if (in->civ_id() && unit->civ_id != in->civ_id())
continue;
describeUnit(out->add_value(), unit, mask);
}
}
return out->value_size() ? CR_OK : CR_NOT_FOUND;
}
static command_result ListSquads(color_ostream &stream,
const ListSquadsIn *in, ListSquadsOut *out)
{
auto entity = df::historical_entity::find(df::global::ui->group_id);
if (!entity)
return CR_NOT_FOUND;
for (size_t i = 0; i < entity->squads.size(); i++)
{
auto squad = df::squad::find(entity->squads[i]);
if (!squad)
continue;
auto item = out->add_value();
item->set_squad_id(squad->id);
if (squad->name.has_name)
describeName(item->mutable_name(), &squad->name);
if (!squad->alias.empty())
item->set_alias(squad->alias);
for (size_t j = 0; j < squad->positions.size(); j++)
item->add_members(squad->positions[j]->occupant);
}
return CR_OK;
}
CoreService::CoreService() {
suspend_depth = 0;
// These 2 methods must be first, so that they get id 0 and 1
addMethod("BindMethod", &CoreService::BindMethod);
addMethod("RunCommand", &CoreService::RunCommand);
// Add others here:
addMethod("CoreSuspend", &CoreService::CoreSuspend);
addMethod("CoreResume", &CoreService::CoreResume);
addFunction("ListEnums", ListEnums, SF_CALLED_ONCE);
addFunction("ListMaterials", ListMaterials, SF_CALLED_ONCE);
addFunction("ListUnits", ListUnits);
addFunction("ListSquads", ListSquads);
}
CoreService::~CoreService()
{
while (suspend_depth-- > 0)
Core::getInstance().Resume();
}
command_result CoreService::BindMethod(color_ostream &stream,
const dfproto::CoreBindRequest *in,
dfproto::CoreBindReply *out)
{
ServerFunctionBase *fn = connection()->findFunction(stream, in->plugin(), in->method());
if (!fn)
{
stream.printerr("RPC method not found: %s::%s\n",
in->plugin().c_str(), in->method().c_str());
return CR_FAILURE;
}
if (fn->p_in_template->GetTypeName() != in->input_msg() ||
fn->p_out_template->GetTypeName() != in->output_msg())
{
stream.printerr("Requested wrong signature for RPC method: %s::%s\n",
in->plugin().c_str(), in->method().c_str());
return CR_FAILURE;
}
out->set_assigned_id(fn->getId());
return CR_OK;
}
command_result CoreService::RunCommand(color_ostream &stream,
const dfproto::CoreRunCommandRequest *in)
{
std::string cmd = in->command();
std::vector<std::string> args;
for (int i = 0; i < in->arguments_size(); i++)
args.push_back(in->arguments(i));
return Core::getInstance().plug_mgr->InvokeCommand(stream, cmd, args);
}
command_result CoreService::CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt)
{
Core::getInstance().Suspend();
cnt->set_value(++suspend_depth);
return CR_OK;
}
command_result CoreService::CoreResume(color_ostream &stream, const EmptyMessage*, IntMessage *cnt)
{
if (suspend_depth <= 0)
return CR_WRONG_USAGE;
Core::getInstance().Resume();
cnt->set_value(--suspend_depth);
return CR_OK;
}

@ -117,7 +117,7 @@ namespace DFHack
bits[byte] ^= bit;
}
}
bool is_set (T index)
bool is_set (T index) const
{
uint32_t byte = index / 8;
if(byte < size)

@ -142,7 +142,7 @@ namespace DFHack
Core();
class Private;
struct Private;
Private *d;
bool Init();

@ -25,6 +25,7 @@ distribution.
#pragma once
#include <string>
#include <sstream>
#include <vector>
#include <map>
@ -36,6 +37,10 @@ distribution.
#undef interface
#endif
/*
* Definitions of DFHack namespace structs used by generated headers.
*/
namespace DFHack
{
class virtual_class {};
@ -153,6 +158,10 @@ inline int linear_index(const DFHack::enum_list_attr<const char*> &lst, const st
return -1;
}
/*
* Definitions of df namespace structs used by generated headers.
*/
namespace df
{
using DFHack::virtual_ptr;
@ -213,8 +222,20 @@ namespace df
namespace enums {}
}
/*
* Templates for access to enum and bitfield traits.
*/
DFHACK_EXPORT std::string join_strings(const std::string &separator, const std::vector<std::string> &items);
namespace DFHack {
// Enums
/*
* Enum trait tools.
*/
/**
* Return the next item in the enum, wrapping to the first one at the end.
*/
template<class T>
inline typename df::enum_traits<T>::enum_type next_enum_item(T v) {
typedef df::enum_traits<T> traits;
@ -223,55 +244,194 @@ namespace DFHack {
return (iv < traits::last_item_value) ? T(iv+1) : traits::first_item;
}
/**
* Check if the value is valid for its enum type.
*/
template<class T>
inline bool is_valid_enum_item(T v) {
return df::enum_traits<T>::is_valid(v);
}
/**
* Return the enum item key string pointer, or NULL if none.
*/
template<class T>
inline const char *enum_item_raw_key(T val) {
typedef df::enum_traits<T> traits;
return traits::is_valid(val) ? traits::key_table[val - traits::first_item_value] : NULL;
}
/**
* Return the enum item key string pointer, or "?" if none.
*/
template<class T>
inline const char *enum_item_key_str(T val) {
return ifnull(enum_item_raw_key(val), "?");
}
DFHACK_EXPORT int findEnumItem_(const std::string &name, int size, const char *const *items);
template<class BaseType>
std::string format_key(const char *keyname, BaseType val) {
if (keyname) return std::string(keyname);
std::stringstream ss; ss << "?" << val << "?"; return ss.str();
}
/**
* Return the enum item key string, or ?123? (using the numeric value) if unknown.
*/
template<class T>
inline std::string enum_item_key(T val) {
typedef typename df::enum_traits<T>::base_type base_type;
return format_key<base_type>(enum_item_raw_key(val), base_type(val));
}
DFHACK_EXPORT int findEnumItem(const std::string &name, int size, const char *const *items);
/**
* Find an enum item by key string. Returns success code.
*/
template<class T>
inline bool find_enum_item(T *var, const std::string &name) {
typedef df::enum_traits<T> traits;
int size = traits::last_item_value-traits::first_item_value+1;
int idx = findEnumItem_(name, size, traits::key_table);
int idx = findEnumItem(name, size, traits::key_table);
if (idx < 0) return false;
*var = T(traits::first_item_value+idx);
return true;
}
DFHACK_EXPORT int findBitfieldField_(const std::string &name, int size, const bitfield_item_info *items);
/*
* Bitfield tools.
*/
DFHACK_EXPORT bool findBitfieldField(unsigned *idx, const std::string &name,
unsigned size, const bitfield_item_info *items);
DFHACK_EXPORT void setBitfieldField(void *p, unsigned idx, unsigned size, int value);
DFHACK_EXPORT int getBitfieldField(const void *p, unsigned idx, unsigned size);
/**
* Find a bitfield item by key string. Returns success code.
*/
template<class T>
inline bool find_bitfield_field(unsigned *idx, const std::string &name, const T* = NULL) {
typedef df::bitfield_traits<T> traits;
return findBitfieldField(&idx, name, traits::bit_count, traits::bits);
}
/**
* Find a bitfield item by key and set its value. Returns success code.
*/
template<class T>
inline bool set_bitfield_field(T *bitfield, const std::string &name, int value)
{
typedef df::bitfield_traits<T> traits;
unsigned idx;
if (!findBitfieldField(&idx, name, traits::bit_count, traits::bits)) return false;
setBitfieldField(&bitfield->whole, idx, traits::bits[idx].size, value);
return true;
}
/**
* Find a bitfield item by key and retrieve its value. Returns success code.
*/
template<class T>
inline int findBitfieldField(const std::string &name) {
inline bool get_bitfield_field(int *value, const T &bitfield, const std::string &name)
{
typedef df::bitfield_traits<T> traits;
return findBitfieldField_(name, traits::bit_count, traits::bits);
unsigned idx;
if (!findBitfieldField(&idx, name, traits::bit_count, traits::bits)) return false;
*value = getBitfieldField(&bitfield.whole, idx, traits::bits[idx].size);
return true;
}
DFHACK_EXPORT std::string bitfieldToString(const void *p, int size, const bitfield_item_info *items);
DFHACK_EXPORT void bitfieldToString(std::vector<std::string> *pvec, const void *p,
unsigned size, const bitfield_item_info *items);
/**
* Represent bitfield bits as strings in a vector.
*/
template<class T>
inline std::string bitfieldToString(const T &val) {
inline void bitfield_to_string(std::vector<std::string> *pvec, const T &val) {
typedef df::bitfield_traits<T> traits;
return bitfieldToString(&val.whole, traits::bit_count, traits::bits);
bitfieldToString(pvec, &val.whole, traits::bit_count, traits::bits);
}
/**
* Represent bitfield bits as a string, using sep as join separator.
*/
template<class T>
inline std::string bitfield_to_string(const T &val, const std::string &sep = " ") {
std::vector<std::string> tmp;
bitfield_to_string<T>(&tmp, val);
return join_strings(sep, tmp);
}
/*
* BitArray tools
*/
/**
* Find a flag array item by key string. Returns success code.
*/
template<class T>
inline bool find_flagarray_field(unsigned *idx, const std::string &name, const BitArray<T>*) {
T tmp;
if (!find_enum_item(&tmp, name) || tmp < 0) return false;
*idx = unsigned(tmp);
return true;
}
/**
* Find a flag array item by key and set its value. Returns success code.
*/
template<class T>
inline bool set_flagarray_field(BitArray<T> *bitfield, const std::string &name, int value)
{
T tmp;
if (!find_enum_item(&tmp, name) || tmp < 0) return false;
bitfield->set(tmp, value!=0);
return true;
}
/**
* Find a flag array item by key and retrieve its value. Returns success code.
*/
template<class T>
inline bool get_flagarray_field(int *value, const BitArray<T> &bitfield, const std::string &name)
{
T tmp;
if (!find_enum_item(&tmp, name) || tmp < 0) return false;
*value = (bitfield->is_set(tmp) ? 1 : 0);
return true;
}
DFHACK_EXPORT void flagarrayToString(std::vector<std::string> *pvec, const void *p,
int bytes, int base, int size, const char *const *items);
/**
* Represent flag array bits as strings in a vector.
*/
template<class T>
inline void flagarray_to_string(std::vector<std::string> *pvec, const BitArray<T> &val) {
typedef df::enum_traits<T> traits;
int size = traits::last_item_value-traits::first_item_value+1;
flagarrayToString(pvec, val.bits, val.size,
(int)traits::first_item_value, size, traits::key_table);
}
}
/**
* Represent flag array bits as a string, using sep as join separator.
*/
template<class T>
inline std::string bitfield_to_string(const BitArray<T> &val, const std::string &sep = " ") {
std::vector<std::string> tmp;
flagarray_to_string<T>(&tmp, val);
return join_strings(sep, tmp);
}
}
#define ENUM_ATTR(enum,attr,val) (df::enum_traits<df::enum>::attrs(val).attr)
#define ENUM_ATTR_STR(enum,attr,val) DFHack::ifnull(ENUM_ATTR(enum,attr,val),"?")
#define ENUM_KEY_STR(enum,val) (DFHack::enum_item_key_str<df::enum>(val))
#define ENUM_KEY_STR(enum,val) (DFHack::enum_item_key<df::enum>(val))
#define ENUM_FIRST_ITEM(enum) (df::enum_traits<df::enum>::first_item)
#define ENUM_LAST_ITEM(enum) (df::enum_traits<df::enum>::last_item)
@ -280,6 +440,10 @@ namespace DFHack {
#define FOR_ENUM_ITEMS(enum,iter) \
for(df::enum iter = ENUM_FIRST_ITEM(enum); iter <= ENUM_LAST_ITEM(enum); iter = df::enum(1+int(iter)))
/*
* Include mandatory generated headers.
*/
// Global object pointers
#include "df/global_objects.h"

@ -253,6 +253,7 @@ Link *linked_list_insert_after(Link *pos, Link *link)
DFHACK_EXPORT bool split_string(std::vector<std::string> *out,
const std::string &str, const std::string &separator,
bool squash_empty = false);
DFHACK_EXPORT std::string join_strings(const std::string &separator, const std::vector<std::string> &items);
DFHACK_EXPORT std::string toUpper(const std::string &str);
DFHACK_EXPORT std::string toLower(const std::string &str);

@ -66,6 +66,8 @@ namespace DFHack
};
struct RPCMessageHeader {
static const int MAX_MESSAGE_SIZE = 8*1048756;
int16_t id;
int32_t size;
};
@ -220,10 +222,6 @@ namespace DFHack
}
};
bool readFullBuffer(CSimpleSocket *socket, void *buf, int size);
bool sendRemoteMessage(CSimpleSocket *socket, int16_t id,
const ::google::protobuf::MessageLite *msg, int *psz = NULL);
class DFHACK_EXPORT RemoteClient
{
friend class RemoteFunctionBase;

@ -34,12 +34,22 @@ class CSimpleSocket;
namespace DFHack
{
class DFHACK_EXPORT ServerConnection;
class Plugin;
class CoreService;
class ServerConnection;
class DFHACK_EXPORT RPCService;
enum ServerFunctionFlags {
// The function is expected to be called only once per client,
// so always delete all cached buffers after processing.
SF_CALLED_ONCE = 1
};
class DFHACK_EXPORT ServerFunctionBase : public RPCFunctionBase {
public:
const char *const name;
const int flags;
virtual command_result execute(color_ostream &stream) = 0;
@ -48,8 +58,9 @@ namespace DFHack
protected:
friend class RPCService;
ServerFunctionBase(const message_type *in, const message_type *out, RPCService *owner, const char *name)
: RPCFunctionBase(in, out), name(name), owner(owner), id(-1)
ServerFunctionBase(const message_type *in, const message_type *out,
RPCService *owner, const char *name, int flags)
: RPCFunctionBase(in, out), name(name), flags(flags), owner(owner), id(-1)
{}
RPCService *owner;
@ -64,8 +75,8 @@ namespace DFHack
In *in() { return static_cast<In*>(RPCFunctionBase::in()); }
Out *out() { return static_cast<Out*>(RPCFunctionBase::out()); }
ServerFunction(RPCService *owner, const char *name, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &Out::default_instance(), owner, name),
ServerFunction(RPCService *owner, const char *name, int flags, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &Out::default_instance(), owner, name, flags),
fptr(fptr) {}
virtual command_result execute(color_ostream &stream) { return fptr(stream, in(), out()); }
@ -81,8 +92,8 @@ namespace DFHack
In *in() { return static_cast<In*>(RPCFunctionBase::in()); }
VoidServerFunction(RPCService *owner, const char *name, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &EmptyMessage::default_instance(), owner, name),
VoidServerFunction(RPCService *owner, const char *name, int flags, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &EmptyMessage::default_instance(), owner, name, flags),
fptr(fptr) {}
virtual command_result execute(color_ostream &stream) { return fptr(stream, in()); }
@ -99,8 +110,8 @@ namespace DFHack
In *in() { return static_cast<In*>(RPCFunctionBase::in()); }
Out *out() { return static_cast<Out*>(RPCFunctionBase::out()); }
ServerMethod(RPCService *owner, const char *name, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &Out::default_instance(), owner, name),
ServerMethod(RPCService *owner, const char *name, int flags, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &Out::default_instance(), owner, name, flags),
fptr(fptr) {}
virtual command_result execute(color_ostream &stream) {
@ -118,8 +129,8 @@ namespace DFHack
In *in() { return static_cast<In*>(RPCFunctionBase::in()); }
VoidServerMethod(RPCService *owner, const char *name, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &EmptyMessage::default_instance(), owner, name),
VoidServerMethod(RPCService *owner, const char *name, int flags, function_type fptr)
: ServerFunctionBase(&In::default_instance(), &EmptyMessage::default_instance(), owner, name, flags),
fptr(fptr) {}
virtual command_result execute(color_ostream &stream) {
@ -130,8 +141,6 @@ namespace DFHack
function_type fptr;
};
class Plugin;
class DFHACK_EXPORT RPCService {
friend class ServerConnection;
friend class Plugin;
@ -153,19 +162,21 @@ namespace DFHack
template<typename In, typename Out>
void addFunction(
const char *name,
command_result (*fptr)(color_ostream &out, const In *input, Out *output)
command_result (*fptr)(color_ostream &out, const In *input, Out *output),
int flags = 0
) {
assert(!owner);
functions.push_back(new ServerFunction<In,Out>(this, name, fptr));
functions.push_back(new ServerFunction<In,Out>(this, name, flags, fptr));
}
template<typename In>
void addFunction(
const char *name,
command_result (*fptr)(color_ostream &out, const In *input)
command_result (*fptr)(color_ostream &out, const In *input),
int flags = 0
) {
assert(!owner);
functions.push_back(new VoidServerFunction<In>(this, name, fptr));
functions.push_back(new VoidServerFunction<In>(this, name, flags, fptr));
}
protected:
@ -174,40 +185,25 @@ namespace DFHack
template<typename Svc, typename In, typename Out>
void addMethod(
const char *name,
command_result (Svc::*fptr)(color_ostream &out, const In *input, Out *output)
command_result (Svc::*fptr)(color_ostream &out, const In *input, Out *output),
int flags = 0
) {
assert(!owner);
functions.push_back(new ServerMethod<Svc,In,Out>(this, name, fptr));
functions.push_back(new ServerMethod<Svc,In,Out>(this, name, flags, fptr));
}
template<typename Svc, typename In>
void addMethod(
const char *name,
command_result (Svc::*fptr)(color_ostream &out, const In *input)
command_result (Svc::*fptr)(color_ostream &out, const In *input),
int flags = 0
) {
assert(!owner);
functions.push_back(new VoidServerMethod<Svc,In>(this, name, fptr));
functions.push_back(new VoidServerMethod<Svc,In>(this, name, flags, fptr));
}
};
class CoreService : public RPCService {
int suspend_depth;
public:
CoreService();
~CoreService();
command_result BindMethod(color_ostream &stream,
const dfproto::CoreBindRequest *in,
dfproto::CoreBindReply *out);
command_result RunCommand(color_ostream &stream,
const dfproto::CoreRunCommandRequest *in);
// For batching
command_result CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
command_result CoreResume(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
};
class DFHACK_EXPORT ServerConnection {
class ServerConnection {
class connection_ostream : public buffered_color_ostream {
ServerConnection *owner;
@ -237,7 +233,7 @@ namespace DFHack
ServerFunctionBase *findFunction(color_ostream &out, const std::string &plugin, const std::string &name);
};
class DFHACK_EXPORT ServerMain {
class ServerMain {
CPassiveSocket *socket;
tthread::thread *thread;

@ -0,0 +1,143 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#pragma once
#include "Pragma.h"
#include "Export.h"
#include "RemoteServer.h"
#include "DataDefs.h"
#include "Basic.pb.h"
namespace df
{
struct material;
struct unit;
struct language_name;
}
namespace DFHack
{
class MaterialInfo;
using google::protobuf::RepeatedField;
using google::protobuf::RepeatedPtrField;
DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
const std::vector<std::string> &vec);
/**
* Represent bitfield bits as a repeated string field.
*/
template<class T>
inline void bitfield_to_string(RepeatedPtrField<std::string> *pf, const T &val) {
std::vector<std::string> tmp;
bitfield_to_string<T>(&tmp, val);
strVectorToRepeatedField(pf, tmp);
}
/**
* Represent flagarray bits as a repeated string field.
*/
template<class T>
inline void flagarray_to_string(RepeatedPtrField<std::string> *pf, const BitArray<T> &val) {
std::vector<std::string> tmp;
flagarray_to_string<T>(&tmp, val);
strVectorToRepeatedField(pf, tmp);
}
/**
* Represent flagarray bits as a repeated int field.
*/
template<class T>
void flagarray_to_ints(RepeatedField<google::protobuf::int32> *pf, const BitArray<T> &val) {
for (int i = 0; i < val.size*8; i++)
if (val.is_set(T(i)))
pf->Add(i);
}
using dfproto::EnumItemName;
DFHACK_EXPORT void describeEnum(RepeatedPtrField<EnumItemName> *pf, int base,
int size, const char* const *names);
template<class T>
void describe_enum(RepeatedPtrField<EnumItemName> *pf)
{
typedef df::enum_traits<T> traits;
int base = traits::first_item;
int size = traits::last_item - base + 1;
describeEnum(pf, base, size, traits::key_table);
}
DFHACK_EXPORT void describeBitfield(RepeatedPtrField<EnumItemName> *pf,
int size, const bitfield_item_info *items);
template<class T>
void describe_bitfield(RepeatedPtrField<EnumItemName> *pf)
{
typedef df::bitfield_traits<T> traits;
describeBitfield(pf, traits::bit_count, traits::bits);
}
/////
using dfproto::BasicMaterialInfo;
using dfproto::BasicMaterialInfoMask;
DFHACK_EXPORT void describeMaterial(BasicMaterialInfo *info, df::material *mat,
const BasicMaterialInfoMask *mask = NULL);
DFHACK_EXPORT void describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat,
const BasicMaterialInfoMask *mask = NULL);
using dfproto::NameInfo;
DFHACK_EXPORT void describeName(NameInfo *info, df::language_name *name);
using dfproto::BasicUnitInfo;
using dfproto::BasicUnitInfoMask;
DFHACK_EXPORT void describeUnit(BasicUnitInfo *info, df::unit *unit,
const BasicUnitInfoMask *mask = NULL);
/////
class CoreService : public RPCService {
int suspend_depth;
public:
CoreService();
~CoreService();
command_result BindMethod(color_ostream &stream,
const dfproto::CoreBindRequest *in,
dfproto::CoreBindReply *out);
command_result RunCommand(color_ostream &stream,
const dfproto::CoreRunCommandRequest *in);
// For batching
command_result CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
command_result CoreResume(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
};
}

@ -51,7 +51,8 @@ DFHACK_EXPORT bool readName(t_name & name, df::language_name * address);
DFHACK_EXPORT bool copyName(df::language_name * address, df::language_name * target);
// translate a name using the loaded dictionaries
DFHACK_EXPORT std::string TranslateName (const df::language_name * name, bool inEnglish = true);
DFHACK_EXPORT std::string TranslateName (const df::language_name * name, bool inEnglish = true,
bool onlyLastPart = false);
}
}
#endif

@ -192,6 +192,8 @@ DFHACK_EXPORT void CopyNameTo(df::unit *creature, df::language_name * target);
DFHACK_EXPORT bool RemoveOwnedItemByIdx(const uint32_t index, int32_t id);
DFHACK_EXPORT bool RemoveOwnedItemByPtr(df::unit * unit, int32_t id);
DFHACK_EXPORT df::language_name *GetVisibleName(df::unit *unit);
}
}
#endif

@ -159,11 +159,11 @@ static void print_job_item_details(color_ostream &out, df::job *job, unsigned id
}
if (item->flags1.whole)
out << " flags1: " << bitfieldToString(item->flags1) << endl;
out << " flags1: " << bitfield_to_string(item->flags1) << endl;
if (item->flags2.whole)
out << " flags2: " << bitfieldToString(item->flags2) << endl;
out << " flags2: " << bitfield_to_string(item->flags2) << endl;
if (item->flags3.whole)
out << " flags3: " << bitfieldToString(item->flags3) << endl;
out << " flags3: " << bitfield_to_string(item->flags3) << endl;
if (!item->reaction_class.empty())
out << " reaction class: " << item->reaction_class << endl;
@ -178,7 +178,7 @@ void DFHack::printJobDetails(color_ostream &out, df::job *job)
out.color(job->flags.bits.suspend ? Console::COLOR_DARKGREY : Console::COLOR_GREY);
out << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type);
if (job->flags.whole)
out << " (" << bitfieldToString(job->flags) << ")";
out << " (" << bitfield_to_string(job->flags) << ")";
out << endl;
out.reset_color();
@ -192,7 +192,7 @@ void DFHack::printJobDetails(color_ostream &out, df::job *job)
{
out << " material: " << mat.toString();
if (job->material_category.whole)
out << " (" << bitfieldToString(job->material_category) << ")";
out << " (" << bitfield_to_string(job->material_category) << ")";
out << endl;
}
@ -201,7 +201,7 @@ void DFHack::printJobDetails(color_ostream &out, df::job *job)
ItemTypeInfo iinfo(itype, job->item_subtype);
out << " item: " << iinfo.toString()
<< " (" << bitfieldToString(job->item_category) << ")" << endl;
<< " (" << bitfield_to_string(job->item_category) << ")" << endl;
}
if (job->hist_figure_id >= 0)

@ -505,11 +505,8 @@ bool DFHack::parseJobMaterialCategory(df::job_material_category *cat, const std:
for (size_t i = 0; i < items.size(); i++)
{
int id = findBitfieldField<df::job_material_category>(items[i]);
if (id < 0)
if (!set_bitfield_field(cat, items[i], 1))
return false;
cat->whole |= (1 << id);
}
return true;
@ -524,11 +521,8 @@ bool DFHack::parseJobMaterialCategory(df::dfhack_material_category *cat, const s
for (size_t i = 0; i < items.size(); i++)
{
int id = findBitfieldField<df::dfhack_material_category>(items[i]);
if (id < 0)
if (!set_bitfield_field(cat, items[i], 1))
return false;
cat->whole |= (1 << id);
}
return true;

@ -91,11 +91,12 @@ void addNameWord (string &out, const string &word)
out.append(upper);
}
string Translation::TranslateName(const df::language_name * name, bool inEnglish)
string Translation::TranslateName(const df::language_name * name, bool inEnglish, bool onlyLastPart)
{
string out;
string word;
if (!onlyLastPart) {
if (!name->first_name.empty())
addNameWord(out, name->first_name);
@ -115,6 +116,7 @@ string Translation::TranslateName(const df::language_name * name, bool inEnglish
}
addNameWord(out, word);
}
}
if (!inEnglish)
{

@ -48,6 +48,10 @@ using namespace std;
#include "df/world.h"
#include "df/ui.h"
#include "df/unit_inventory_item.h"
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/assumed_identity.h"
using namespace DFHack;
using df::global::world;
@ -522,3 +526,28 @@ void Units::CopyNameTo(df::unit * creature, df::language_name * target)
Translation::copyName(&creature->name, target);
}
df::language_name *Units::GetVisibleName(df::unit *unit)
{
df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id);
if (figure)
{
// 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)
return &id_hfig->name;
return &identity->name;
}
}
}
return &unit->name;
}

@ -0,0 +1,114 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
message EnumItemName {
required int32 value = 1;
optional string name = 2;
optional int32 bit_size = 3 [default = 1];
};
message BasicMaterialId {
required int32 type = 1;
required sint32 index = 2;
};
message BasicMaterialInfo {
required int32 type = 1;
required sint32 index = 2;
required string token = 3;
repeated int32 flags = 4; // of material_flags
optional int32 subtype = 5 [default = -1];
optional int32 creature_id = 6 [default = -1];
optional int32 plant_id = 7 [default = -1];
optional int32 histfig_id = 8 [default = -1];
optional string name_prefix = 9 [default = ""];
repeated fixed32 state_color = 10;
repeated string state_name = 11;
repeated string state_adj = 12;
message Product {
required string id = 1;
required int32 type = 2;
required sint32 index = 3;
};
repeated string reaction_class = 13;
repeated Product reaction_product = 14;
repeated int32 inorganic_flags = 15;
};
message BasicMaterialInfoMask {
enum StateType {
Solid = 0;
Liquid = 1;
Gas = 2;
Powder = 3;
Paste = 4;
Pressed = 5;
};
repeated StateType states = 1;
optional int32 temperature = 4 [default = 10015];
optional bool flags = 2 [default = false];
optional bool reaction = 3 [default = false];
};
message NameInfo {
optional string first_name = 1;
optional string nickname = 2;
optional int32 language_id = 3 [default = -1];
optional string last_name = 4;
optional string english_name = 5;
};
message SkillInfo {
required int32 id = 1;
required int32 level = 2;
required int32 experience = 3;
};
message BasicUnitInfo {
required int32 unit_id = 1;
optional NameInfo name = 2;
required fixed32 flags1 = 3;
required fixed32 flags2 = 4;
required fixed32 flags3 = 5;
required int32 race = 6;
required int32 caste = 7;
optional int32 gender = 8 [default = -1];
optional int32 civ_id = 9 [default = -1];
optional int32 histfig_id = 10 [default = -1];
repeated int32 labors = 11;
repeated SkillInfo skills = 12;
required int32 pos_x = 13;
required int32 pos_y = 14;
required int32 pos_z = 15;
};
message BasicUnitInfoMask {
optional bool labors = 1 [default = false];
optional bool skills = 2 [default = false];
};
message BasicSquadInfo {
required int32 squad_id = 1;
optional NameInfo name = 2;
optional string alias = 3;
repeated sint32 members = 4;
};

@ -0,0 +1,45 @@
package dfproto;
option optimize_for = LITE_RUNTIME;
import "Basic.proto";
message ListEnumsOut {
repeated EnumItemName material_flags = 1;
repeated EnumItemName inorganic_flags = 2;
repeated EnumItemName unit_flags1 = 3;
repeated EnumItemName unit_flags2 = 4;
repeated EnumItemName unit_flags3 = 5;
repeated EnumItemName unit_labor = 6;
repeated EnumItemName job_skill = 7;
};
message ListMaterialsIn {
optional BasicMaterialInfoMask mask = 1;
repeated BasicMaterialId id_list = 2;
optional bool builtin = 3;
optional bool inorganic = 4;
optional bool creatures = 5;
optional bool plants = 6;
};
message ListMaterialsOut {
repeated BasicMaterialInfo value = 1;
};
message ListUnitsIn {
optional BasicUnitInfoMask mask = 1;
repeated int32 id_list = 2;
optional int32 race = 3;
optional int32 civ_id = 4;
};
message ListUnitsOut {
repeated BasicUnitInfo value = 1;
};
message ListSquadsIn {}
message ListSquadsOut {
repeated BasicSquadInfo value = 1;
}

@ -72,18 +72,23 @@ MACRO(DFHACK_PLUGIN)
ADD_LIBRARY(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES})
IDE_FOLDER(${PLUGIN_NAME} "Plugins")
LIST(LENGTH PLUGIN_PROTOBUFS NUM_PROTO)
IF(NUM_PROTO)
TARGET_LINK_LIBRARIES(${PLUGIN_NAME} dfhack protobuf-lite ${PLUGIN_LINK_LIBRARIES})
IF(UNIX)
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES COMPILE_FLAGS "-include Export.h")
ELSE()
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"")
ENDIF()
ELSE()
TARGET_LINK_LIBRARIES(${PLUGIN_NAME} dfhack ${PLUGIN_LINK_LIBRARIES})
ENDIF()
IF(UNIX)
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES SUFFIX .plug.so PREFIX "")
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES COMPILE_FLAGS "-include Export.h")
ELSE()
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES SUFFIX .plug.dll)
SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"")
ENDIF()
install(TARGETS ${PLUGIN_NAME}

@ -245,13 +245,13 @@ command_result df_dumpmats (color_ostream &out, vector<string> &parameters)
FOR_ENUM_ITEMS(material_flags, i)
{
if (mat->flags.is_set(i))
out.print("\t[%s]\n", ENUM_KEY_STR(material_flags, i));
out.print("\t[%s]\n", ENUM_KEY_STR(material_flags, i).c_str());
}
if (mat->extract_storage != item_type::BARREL)
out.print("\t[EXTRACT_STORAGE:%s]\n", ENUM_KEY_STR(item_type, mat->extract_storage));
out.print("\t[EXTRACT_STORAGE:%s]\n", ENUM_KEY_STR(item_type, mat->extract_storage).c_str());
if (mat->butcher_special_type != item_type::NONE || mat->butcher_special_subtype != -1)
out.print("\t[BUTCHER_SPECIAL:%s:%s]\n", ENUM_KEY_STR(item_type, mat->butcher_special_type), (mat->butcher_special_subtype == -1) ? "NONE" : "?");
out.print("\t[BUTCHER_SPECIAL:%s:%s]\n", ENUM_KEY_STR(item_type, mat->butcher_special_type).c_str(), (mat->butcher_special_subtype == -1) ? "NONE" : "?");
if (mat->meat_name[0].size() || mat->meat_name[1].size() || mat->meat_name[2].size())
out.print("\t[MEAT_NAME:%s:%s:%s]\n", mat->meat_name[0].c_str(), mat->meat_name[1].c_str(), mat->meat_name[2].c_str());
if (mat->block_name[0].size() || mat->block_name[1].size())

@ -38,7 +38,9 @@ static command_result feature(color_ostream &out, vector <string> &parameters)
df::feature_init *feature_init = world->cur_savegame.map_features[i];
string name;
feature_init->getName(&name);
out.print("Feature #%i (\"%s\", type %s) is %s\n", i, name.c_str(), ENUM_KEY_STR(feature_type, feature_init->getType()), feature_init->flags.is_set(feature_init_flags::Discovered) ? "discovered" : "hidden");
out.print("Feature #%i (\"%s\", type %s) is %s\n",
i, name.c_str(), ENUM_KEY_STR(feature_type, feature_init->getType()).c_str(),
feature_init->flags.is_set(feature_init_flags::Discovered) ? "discovered" : "hidden");
}
}
else if(cmd == "show")
@ -60,7 +62,8 @@ static command_result feature(color_ostream &out, vector <string> &parameters)
feature_init->flags.set(feature_init_flags::Discovered);
string name;
feature_init->getName(&name);
out.print("Feature #%i (\"%s\", type %s) is now discovered\n", i, name.c_str(), ENUM_KEY_STR(feature_type, feature_init->getType()));
out.print("Feature #%i (\"%s\", type %s) is now discovered\n",
i, name.c_str(), ENUM_KEY_STR(feature_type, feature_init->getType()).c_str());
}
else if(cmd == "hide")
{
@ -81,7 +84,8 @@ static command_result feature(color_ostream &out, vector <string> &parameters)
feature_init->flags.clear(feature_init_flags::Discovered);
string name;
feature_init->getName(&name);
out.print("Feature #%i (\"%s\", type %s) is now hidden\n", i, name.c_str(), ENUM_KEY_STR(feature_type, feature_init->getType()));
out.print("Feature #%i (\"%s\", type %s) is now hidden\n",
i, name.c_str(), ENUM_KEY_STR(feature_type, feature_init->getType()).c_str());
}
else return CR_WRONG_USAGE;

@ -146,7 +146,7 @@ static command_result job_material_in_job(color_ostream &out, MaterialInfo &new_
if (new_mat.getCraftClass() != old_class)
{
out.printerr("New material %s does not satisfy requirement: %s\n",
new_mat.toString().c_str(), ENUM_KEY_STR(craft_material_class, old_class));
new_mat.toString().c_str(), ENUM_KEY_STR(craft_material_class, old_class).c_str());
return CR_FAILURE;
}
@ -277,7 +277,7 @@ static command_result job_duplicate(color_ostream &out, vector <string> & parame
job->job_type != job_type::CollectSand &&
job->job_type != job_type::CollectClay))
{
out.printerr("Cannot duplicate job %s\n", ENUM_KEY_STR(job_type,job->job_type));
out.printerr("Cannot duplicate job %s\n", ENUM_KEY_STR(job_type,job->job_type).c_str());
return CR_FAILURE;
}

@ -175,13 +175,13 @@ command_result df_probe (color_ostream &out, vector <string> & parameters)
df::tiletype_special special = tileSpecial(tiletype);
df::tiletype_variant variant = tileVariant(tiletype);
out.print("%-10s: %4d %s\n","Class" ,shape,
ENUM_KEY_STR(tiletype_shape, shape));
ENUM_KEY_STR(tiletype_shape, shape).c_str());
out.print("%-10s: %4d %s\n","Material" ,
material, ENUM_KEY_STR(tiletype_material, material));
material, ENUM_KEY_STR(tiletype_material, material).c_str());
out.print("%-10s: %4d %s\n","Special" ,
special, ENUM_KEY_STR(tiletype_special, special));
special, ENUM_KEY_STR(tiletype_special, special).c_str());
out.print("%-10s: %4d %s\n" ,"Variant" ,
variant, ENUM_KEY_STR(tiletype_variant, variant));
variant, ENUM_KEY_STR(tiletype_variant, variant).c_str());
out.print("%-10s: %s\n" ,"Direction",
tileDirection(tiletype).getStr());
out.print("\n");
@ -300,34 +300,38 @@ command_result df_bprobe (color_ostream &out, vector <string> & parameters)
continue;
string name;
building.origin->getName(&name);
out.print("Building %i - \"%s\" - type %s", building.origin->id, name.c_str(), ENUM_KEY_STR(building_type, building.type));
out.print("Building %i - \"%s\" - type %s",
building.origin->id, name.c_str(),
ENUM_KEY_STR(building_type, building.type).c_str());
switch (building.type)
{
case building_type::Civzone:
out.print(", subtype %s", ENUM_KEY_STR(civzone_type, building.civzone_type));
out.print(", subtype %s", ENUM_KEY_STR(civzone_type, building.civzone_type).c_str());
break;
case building_type::Furnace:
out.print(", subtype %s", ENUM_KEY_STR(furnace_type, building.furnace_type));
out.print(", subtype %s", ENUM_KEY_STR(furnace_type, building.furnace_type).c_str());
if (building.furnace_type == furnace_type::Custom)
out.print(", custom type %i (%s)", building.custom_type, world->raws.buildings.all[building.custom_type]->code.c_str());
out.print(", custom type %i (%s)", building.custom_type,
world->raws.buildings.all[building.custom_type]->code.c_str());
break;
case building_type::Workshop:
out.print(", subtype %s", ENUM_KEY_STR(workshop_type, building.workshop_type));
out.print(", subtype %s", ENUM_KEY_STR(workshop_type, building.workshop_type).c_str());
if (building.workshop_type == workshop_type::Custom)
out.print(", custom type %i (%s)", building.custom_type, world->raws.buildings.all[building.custom_type]->code.c_str());
out.print(", custom type %i (%s)", building.custom_type,
world->raws.buildings.all[building.custom_type]->code.c_str());
break;
case building_type::Construction:
out.print(", subtype %s", ENUM_KEY_STR(construction_type, building.construction_type));
out.print(", subtype %s", ENUM_KEY_STR(construction_type, building.construction_type).c_str());
break;
case building_type::Shop:
out.print(", subtype %s", ENUM_KEY_STR(shop_type, building.shop_type));
out.print(", subtype %s", ENUM_KEY_STR(shop_type, building.shop_type).c_str());
break;
case building_type::SiegeEngine:
out.print(", subtype %s", ENUM_KEY_STR(siegeengine_type, building.siegeengine_type));
out.print(", subtype %s", ENUM_KEY_STR(siegeengine_type, building.siegeengine_type).c_str());
break;
case building_type::Trap:
out.print(", subtype %s", ENUM_KEY_STR(trap_type, building.trap_type));
out.print(", subtype %s", ENUM_KEY_STR(trap_type, building.trap_type).c_str());
break;
default:
if (building.subtype != -1)

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

@ -122,7 +122,7 @@ void setUnitNickname(df::unit *unit, const std::string &nick)
}
}
static command_result RenameSquad(color_ostream &stream, const RenameSquadRq *in)
static command_result RenameSquad(color_ostream &stream, const RenameSquadIn *in)
{
CoreSuspender suspend;
@ -138,7 +138,7 @@ static command_result RenameSquad(color_ostream &stream, const RenameSquadRq *in
return CR_OK;
}
static command_result RenameUnit(color_ostream &stream, const RenameUnitRq *in)
static command_result RenameUnit(color_ostream &stream, const RenameUnitIn *in)
{
CoreSuspender suspend;

@ -235,16 +235,16 @@ command_result df_showmood (color_ostream &out, vector <string> & parameters)
else
out.print("%s unknown body parts (%s:%s:%s)",
mat_name.c_str(),
bitfieldToString(item->flags1).c_str(),
bitfieldToString(item->flags2).c_str(),
bitfieldToString(item->flags3).c_str());
bitfield_to_string(item->flags1).c_str(),
bitfield_to_string(item->flags2).c_str(),
bitfield_to_string(item->flags3).c_str());
}
else
out.print("indeterminate %s item (%s:%s:%s)",
mat_name.c_str(),
bitfieldToString(item->flags1).c_str(),
bitfieldToString(item->flags2).c_str(),
bitfieldToString(item->flags3).c_str());
bitfield_to_string(item->flags1).c_str(),
bitfield_to_string(item->flags2).c_str(),
bitfield_to_string(item->flags3).c_str());
break;
default:
{
@ -252,9 +252,9 @@ command_result df_showmood (color_ostream &out, vector <string> & parameters)
out.print("item %s material %s flags (%s:%s:%s)",
itinfo.toString().c_str(), mat_name.c_str(),
bitfieldToString(item->flags1).c_str(),
bitfieldToString(item->flags2).c_str(),
bitfieldToString(item->flags3).c_str());
bitfield_to_string(item->flags1).c_str(),
bitfield_to_string(item->flags2).c_str(),
bitfield_to_string(item->flags3).c_str());
break;
}
}

@ -502,7 +502,7 @@ static bool recover_job(color_ostream &out, ProtectedJob *pj)
if (!pj->holder)
{
out.printerr("Forgetting job %d (%s): holder building lost.",
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type));
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type).c_str());
forget_job(out, pj);
return true;
}
@ -511,7 +511,7 @@ static bool recover_job(color_ostream &out, ProtectedJob *pj)
if (pj->holder->jobs.size() >= 10)
{
out.printerr("Forgetting job %d (%s): holder building has too many jobs.",
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type));
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type).c_str());
forget_job(out, pj);
return true;
}
@ -534,7 +534,7 @@ static bool recover_job(color_ostream &out, ProtectedJob *pj)
deleteJobStruct(recovered);
out.printerr("Inconsistency: job %d (%s) already in list.",
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type));
pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type).c_str());
return true;
}
@ -1283,7 +1283,7 @@ static void update_jobs_by_constraints(color_ostream &out)
if (ct->material.isValid())
info = ct->material.toString() + " " + info;
else if (ct->mat_mask.whole)
info = bitfieldToString(ct->mat_mask) + " " + info;
info = bitfield_to_string(ct->mat_mask) + " " + info;
if (is_running != ct->is_active)
{
@ -1337,7 +1337,7 @@ static std::string shortJobDescription(df::job *job)
if (mat.isValid())
rv += " [" + mat.toString() + "]";
else if (mat_mask.whole)
rv += " [" + bitfieldToString(mat_mask) + "]";
rv += " [" + bitfield_to_string(mat_mask) + "]";
return rv;
}