diff --git a/library/RemoteServer.cpp b/library/RemoteServer.cpp index bae62bb75..8c486aff9 100644 --- a/library/RemoteServer.cpp +++ b/library/RemoteServer.cpp @@ -331,7 +331,7 @@ void ServerConnection::threadFn(void *arg) // Cleanup if (fn) { - fn->reset(out_size > 32768 || in_size > 32768); + fn->reset(out_size > 128*1024 || in_size > 32*1024); } } diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index 42bc84630..ae955012c 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -50,8 +50,13 @@ POSSIBILITY OF SUCH DAMAGE. #include "MiscUtils.h" #include "modules/Materials.h" +#include "modules/Translation.h" +#include "modules/Units.h" #include "DataDefs.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" @@ -80,15 +85,47 @@ void DFHack::strVectorToRepeatedField(RepeatedPtrField *pf, *pf->Add() = vec[i]; } +void DFHack::describeEnum(RepeatedPtrField *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 *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_string(info->mutable_flags(), mat->flags); + flagarray_to_ints(info->mutable_flags(), mat->flags); - info->set_name_prefix(mat->prefix); + if (!mat->prefix.empty()) + info->set_name_prefix(mat->prefix); if (!mask || mask->states_size() == 0) { @@ -143,14 +180,14 @@ void DFHack::describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat, case MaterialInfo::Inorganic: info->set_token(mat.inorganic->id); if (mask && mask->flags()) - flagarray_to_string(info->mutable_inorganic_flags(), mat.inorganic->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_hfig_id(mat.index); + info->set_histfig_id(mat.index); info->set_creature_id(mat.figure->race); } else @@ -163,6 +200,89 @@ void DFHack::describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat, } } +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 *, ListEnumsRes *out) +{ +#define ENUM(name) describe_enum(out->mutable_##name()); +#define BITFIELD(name) describe_bitfield(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(ListMaterialsRes *out, int type, int index, const BasicMaterialInfoMask *mask) { MaterialInfo info(type, index); @@ -223,6 +343,42 @@ static command_result ListMaterials(color_ostream &stream, return out->value_size() ? CR_OK : CR_NOT_FOUND; } +static command_result ListUnits(color_ostream &stream, + const ListUnitsRq *in, ListUnitsRes *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; +} + CoreService::CoreService() { suspend_depth = 0; @@ -234,7 +390,9 @@ CoreService::CoreService() { addMethod("CoreSuspend", &CoreService::CoreSuspend); addMethod("CoreResume", &CoreService::CoreResume); + addFunction("ListEnums", ListEnums); addFunction("ListMaterials", ListMaterials); + addFunction("ListUnits", ListUnits); } CoreService::~CoreService() diff --git a/library/include/BitArray.h b/library/include/BitArray.h index 6759b9247..ff17550a7 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -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) diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h index 44a414467..b8ccb67db 100644 --- a/library/include/RemoteTools.h +++ b/library/include/RemoteTools.h @@ -34,12 +34,15 @@ distribution. 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 *pf, @@ -65,6 +68,40 @@ namespace DFHack strVectorToRepeatedField(pf, tmp); } + /** + * Represent flagarray bits as a repeated int field. + */ + template + void flagarray_to_ints(RepeatedField *pf, const BitArray &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 *pf, int base, + int size, const char* const *names); + + template + void describe_enum(RepeatedPtrField *pf) + { + typedef df::enum_traits 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 *pf, + int size, const bitfield_item_info *items); + + template + void describe_bitfield(RepeatedPtrField *pf) + { + typedef df::bitfield_traits traits; + describeBitfield(pf, traits::bit_count, traits::bits); + } + ///// using dfproto::BasicMaterialInfo; @@ -75,6 +112,16 @@ namespace DFHack 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 { diff --git a/library/include/modules/Translation.h b/library/include/modules/Translation.h index c88f35b89..19a3ed9a6 100644 --- a/library/include/modules/Translation.h +++ b/library/include/modules/Translation.h @@ -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 diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 3d491b86e..6df8b2c19 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -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 diff --git a/library/modules/Translation.cpp b/library/modules/Translation.cpp index 7ab0c950b..7b5fa654c 100644 --- a/library/modules/Translation.cpp +++ b/library/modules/Translation.cpp @@ -91,29 +91,31 @@ 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 (!name->first_name.empty()) - addNameWord(out, name->first_name); + if (!onlyLastPart) { + if (!name->first_name.empty()) + addNameWord(out, name->first_name); - if (!name->nickname.empty()) - { - word = "`" + name->nickname + "'"; - switch (d_init ? d_init->nickname_dwarf : d_init_nickname::CENTRALIZE) + if (!name->nickname.empty()) { - case d_init_nickname::REPLACE_ALL: - out = word; - return out; - case d_init_nickname::REPLACE_FIRST: - out = ""; - break; - case d_init_nickname::CENTRALIZE: - break; + word = "`" + name->nickname + "'"; + switch (d_init ? d_init->nickname_dwarf : d_init_nickname::CENTRALIZE) + { + case d_init_nickname::REPLACE_ALL: + out = word; + return out; + case d_init_nickname::REPLACE_FIRST: + out = ""; + break; + case d_init_nickname::CENTRALIZE: + break; + } + addNameWord(out, word); } - addNameWord(out, word); } if (!inEnglish) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index ae3f9064c..9e97f8e74 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -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; +} diff --git a/library/proto/Basic.proto b/library/proto/Basic.proto index 92a993b70..1a9a8f1b4 100644 --- a/library/proto/Basic.proto +++ b/library/proto/Basic.proto @@ -2,6 +2,12 @@ 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; @@ -10,14 +16,14 @@ message BasicMaterialId { message BasicMaterialInfo { required int32 type = 1; required sint32 index = 2; - required string token = 3; - repeated string flags = 4; + + 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 hfig_id = 8 [default = -1]; + optional int32 histfig_id = 8 [default = -1]; optional string name_prefix = 9 [default = ""]; @@ -33,7 +39,7 @@ message BasicMaterialInfo { repeated string reaction_class = 13; repeated Product reaction_product = 14; - repeated string inorganic_flags = 15; + repeated int32 inorganic_flags = 15; }; message BasicMaterialInfoMask { @@ -49,6 +55,50 @@ message BasicMaterialInfoMask { optional bool flags = 2 [default = false]; optional bool reaction = 3 [default = false]; - optional int32 temperature = 4; + optional int32 temperature = 4 [default = 10015]; +}; + +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 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; + + message Skill { + required int32 id = 1; + required int32 level = 2; + required int32 experience = 3; + }; + repeated Skill 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]; +}; diff --git a/library/proto/BasicApi.proto b/library/proto/BasicApi.proto index b5a569862..ed1276fef 100644 --- a/library/proto/BasicApi.proto +++ b/library/proto/BasicApi.proto @@ -4,6 +4,18 @@ option optimize_for = LITE_RUNTIME; import "Basic.proto"; +message ListEnumsRes { + 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 ListMaterialsRq { optional BasicMaterialInfoMask mask = 1; repeated BasicMaterialId id_list = 2; @@ -15,3 +27,14 @@ message ListMaterialsRq { message ListMaterialsRes { repeated BasicMaterialInfo value = 1; }; + +message ListUnitsRq { + optional BasicUnitInfoMask mask = 1; + repeated int32 id_list = 2; + + optional int32 race = 3; + optional int32 civ_id = 4; +}; +message ListUnitsRes { + repeated BasicUnitInfo value = 1; +}; \ No newline at end of file