From 14d6a62e1d212808623f1a71b3c559ac4a8dada3 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 17 Mar 2012 12:09:30 +0400 Subject: [PATCH 1/6] Move CoreService to a separate file. --- library/CMakeLists.txt | 2 + library/RemoteServer.cpp | 73 +----------------- library/RemoteTools.cpp | 133 +++++++++++++++++++++++++++++++++ library/include/Core.h | 2 +- library/include/RemoteServer.h | 28 ++----- library/include/RemoteTools.h | 48 ++++++++++++ plugins/Plugins.cmake | 9 ++- 7 files changed, 199 insertions(+), 96 deletions(-) create mode 100644 library/RemoteTools.cpp create mode 100644 library/include/RemoteTools.h diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index f062be875..0db0042e6 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -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 diff --git a/library/RemoteServer.cpp b/library/RemoteServer.cpp index e7ab0cbad..ae8b0024c 100644 --- a/library/RemoteServer.cpp +++ b/library/RemoteServer.cpp @@ -46,6 +46,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #include "RemoteServer.h" +#include "RemoteTools.h" + #include "PassiveSocket.h" #include "PluginManager.h" #include "MiscUtils.h" @@ -65,77 +67,6 @@ 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(); -} - -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 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() { owner = NULL; diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp new file mode 100644 index 000000000..a3e3c9e4d --- /dev/null +++ b/library/RemoteTools.cpp @@ -0,0 +1,133 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2011 Petr Mrázek + +A thread-safe logging console with a line editor for windows. + +Based on linenoise win32 port, +copyright 2010, Jon Griffiths . +All rights reserved. +Based on linenoise, copyright 2010, Salvatore Sanfilippo . +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 +#include +#include +#include +#include +#include +#include +#include + +#include "RemoteTools.h" +#include "PluginManager.h" +#include "MiscUtils.h" + +#include +#include +#include + +#include + +using namespace DFHack; + +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(); +} + +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 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; +} diff --git a/library/include/Core.h b/library/include/Core.h index f4c2c2ae7..10b95a47d 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -142,7 +142,7 @@ namespace DFHack Core(); - class Private; + struct Private; Private *d; bool Init(); diff --git a/library/include/RemoteServer.h b/library/include/RemoteServer.h index 274d2bcda..7e45ba560 100644 --- a/library/include/RemoteServer.h +++ b/library/include/RemoteServer.h @@ -34,7 +34,10 @@ class CSimpleSocket; namespace DFHack { - class DFHACK_EXPORT ServerConnection; + class Plugin; + class CoreService; + class ServerConnection; + class DFHACK_EXPORT RPCService; class DFHACK_EXPORT ServerFunctionBase : public RPCFunctionBase { @@ -130,8 +133,6 @@ namespace DFHack function_type fptr; }; - class Plugin; - class DFHACK_EXPORT RPCService { friend class ServerConnection; friend class Plugin; @@ -190,24 +191,7 @@ namespace DFHack } }; - 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 +221,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; diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h new file mode 100644 index 000000000..9c5a9b069 --- /dev/null +++ b/library/include/RemoteTools.h @@ -0,0 +1,48 @@ +/* +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" + +namespace DFHack +{ + 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); + }; +} diff --git a/plugins/Plugins.cmake b/plugins/Plugins.cmake index 78892967d..a8252e706 100644 --- a/plugins/Plugins.cmake +++ b/plugins/Plugins.cmake @@ -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} From 58eb199036db3662ec2f7a2d6d301e7f432d5639 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 17 Mar 2012 12:52:22 +0400 Subject: [PATCH 2/6] Add many new template functions for enums & bitfields. An incompatible change: ENUM_KEY_STR returns std::string now. The old behavior is available via enum_item_key_str function. --- library/DataDefs.cpp | 93 ++++++++++++------ library/MiscUtils.cpp | 16 +++ library/RemoteTools.cpp | 7 ++ library/include/DataDefs.h | 177 +++++++++++++++++++++++++++++++--- library/include/MiscUtils.h | 1 + library/include/RemoteTools.h | 20 +++- library/modules/Job.cpp | 12 +-- library/modules/Materials.cpp | 10 +- plugins/devel/dumpmats.cpp | 6 +- plugins/feature.cpp | 10 +- plugins/jobutils.cpp | 4 +- plugins/probe.cpp | 34 ++++--- plugins/showmood.cpp | 18 ++-- plugins/workflow.cpp | 10 +- 14 files changed, 326 insertions(+), 92 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 2396e03fd..30a4da271 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.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<> 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 *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 *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; } diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index dff8af0a0..8247cd002 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -36,6 +36,8 @@ distribution. #include #include +#include + std::string stl_sprintf(const char *fmt, ...) { va_list lst; va_start(lst, fmt); @@ -82,6 +84,20 @@ bool split_string(std::vector *out, return out->size() > 1; } +std::string join_strings(const std::string &separator, const std::vector &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(),' '); diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index a3e3c9e4d..aa6324356 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -61,6 +61,13 @@ using dfproto::CoreTextNotification; using dfproto::CoreTextFragment; using google::protobuf::MessageLite; +void DFHack::strVectorToRepeatedField(RepeatedPtrField *pf, + const std::vector &vec) +{ + for (size_t i = 0; i < vec.size(); ++i) + *pf->Add() = vec[i]; +} + CoreService::CoreService() { suspend_depth = 0; diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index b4623f791..46662efca 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -25,6 +25,7 @@ distribution. #pragma once #include +#include #include #include @@ -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 &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 &items); + namespace DFHack { - // Enums + /* + * Enum trait tools. + */ + + /** + * Return the next item in the enum, wrapping to the first one at the end. + */ template inline typename df::enum_traits::enum_type next_enum_item(T v) { typedef df::enum_traits traits; @@ -223,55 +244,185 @@ 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 inline bool is_valid_enum_item(T v) { return df::enum_traits::is_valid(v); } + /** + * Return the enum item key string pointer, or NULL if none. + */ template inline const char *enum_item_raw_key(T val) { typedef df::enum_traits 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 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 + 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 + inline std::string enum_item_key(T val) { + typedef typename df::enum_traits::base_type base_type; + return format_key(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 inline bool find_enum_item(T *var, const std::string &name) { typedef df::enum_traits 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 - inline int findBitfieldField(const std::string &name) { + inline bool find_bitfield_field(unsigned *idx, const std::string &name, const T* = NULL) { typedef df::bitfield_traits traits; - return findBitfieldField_(name, traits::bit_count, traits::bits); + return findBitfieldField(&idx, name, traits::bit_count, traits::bits); } - DFHACK_EXPORT std::string bitfieldToString(const void *p, int size, const bitfield_item_info *items); + /** + * Find a bitfield item by key and set its value. Returns success code. + */ + template + inline bool set_bitfield_field(T *bitfield, const std::string &name, int value) + { + typedef df::bitfield_traits 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 - inline std::string bitfieldToString(const T &val) { + inline bool get_bitfield_field(int *value, const T &bitfield, const std::string &name) + { typedef df::bitfield_traits traits; - return bitfieldToString(&val.whole, 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 void bitfieldToString(std::vector *pvec, const void *p, + unsigned size, const bitfield_item_info *items); + + /** + * Represent bitfield bits as strings in a vector. + */ + template + inline void bitfield_to_string(std::vector *pvec, const T &val) { + typedef df::bitfield_traits traits; + bitfieldToString(pvec, &val.whole, traits::bit_count, traits::bits); } -} + /** + * Represent bitfield bits as a string, using sep as join separator. + */ + template + inline std::string bitfield_to_string(const T &val, const std::string &sep = " ") { + std::vector tmp; + bitfield_to_string(&tmp, val); + return join_strings(sep, tmp); + } + + /* + * BitArray tools + */ + + /** + * Find a flag array item by key string. Returns success code. + */ + template + inline bool find_bitfield_field(unsigned *idx, const std::string &name, const BitArray*) { + 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 + inline bool set_bitfield_field(BitArray *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 + inline bool get_bitfield_field(int *value, const BitArray &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 *pvec, const void *p, + int bytes, int base, int size, const char *const *items); + + /** + * Represent flag array bits as strings in a vector. + */ + template + inline void bitfield_to_string(std::vector *pvec, const BitArray &val) { + typedef df::enum_traits 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); + } + +} #define ENUM_ATTR(enum,attr,val) (df::enum_traits::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(val)) +#define ENUM_KEY_STR(enum,val) (DFHack::enum_item_key(val)) #define ENUM_FIRST_ITEM(enum) (df::enum_traits::first_item) #define ENUM_LAST_ITEM(enum) (df::enum_traits::last_item) @@ -280,6 +431,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" diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 039b75b20..e5ecb25f4 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -253,6 +253,7 @@ Link *linked_list_insert_after(Link *pos, Link *link) DFHACK_EXPORT bool split_string(std::vector *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 &items); DFHACK_EXPORT std::string toUpper(const std::string &str); DFHACK_EXPORT std::string toLower(const std::string &str); diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h index 9c5a9b069..00d5e5281 100644 --- a/library/include/RemoteTools.h +++ b/library/include/RemoteTools.h @@ -27,8 +27,26 @@ distribution. #include "Export.h" #include "RemoteServer.h" -namespace DFHack +#include "DataDefs.h" + +namespace DFHack { + using google::protobuf::RepeatedPtrField; + + DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField *pf, + const std::vector &vec); + + /** + * Represent bitfield bits as a repeated string field. + */ + template + inline void bitfield_to_string(RepeatedPtrField *pf, const T &val) { + std::vector tmp; + bitfield_to_string(&tmp, val); + strVectorToRepeatedField(pf, tmp); + } + + class CoreService : public RPCService { int suspend_depth; public: diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index d1ca7923c..2c9a5d1d0 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -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) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 68aadc3d6..8f5739c7e 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -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(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(items[i]); - if (id < 0) + if (!set_bitfield_field(cat, items[i], 1)) return false; - - cat->whole |= (1 << id); } return true; diff --git a/plugins/devel/dumpmats.cpp b/plugins/devel/dumpmats.cpp index 6ee3d693f..ba888e7cf 100644 --- a/plugins/devel/dumpmats.cpp +++ b/plugins/devel/dumpmats.cpp @@ -245,13 +245,13 @@ command_result df_dumpmats (color_ostream &out, vector ¶meters) 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()) diff --git a/plugins/feature.cpp b/plugins/feature.cpp index cadd91b69..834284e0f 100644 --- a/plugins/feature.cpp +++ b/plugins/feature.cpp @@ -38,7 +38,9 @@ static command_result feature(color_ostream &out, vector ¶meters) 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 ¶meters) 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 ¶meters) 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; diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index ca234423a..603346c33 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -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 & 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; } diff --git a/plugins/probe.cpp b/plugins/probe.cpp index bff612680..cc3cbb0f6 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -175,15 +175,15 @@ command_result df_probe (color_ostream &out, vector & 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()); + tileDirection(tiletype).getStr()); out.print("\n"); out.print("temperature1: %d U\n",mc.temperature1At(cursor)); @@ -300,34 +300,38 @@ command_result df_bprobe (color_ostream &out, vector & 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) diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index d47bd8a80..d49477b3f 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -235,16 +235,16 @@ command_result df_showmood (color_ostream &out, vector & 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 & 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; } } diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index b48ce85db..237068b08 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -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; } From 15ccfbb08693a3c89c01a522cd3d2af59c13b051 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 17 Mar 2012 15:36:42 +0400 Subject: [PATCH 3/6] Add a ListMaterials remote call for bulk download of basic material info. --- library/CMakeLists.txt | 3 +- library/RemoteClient.cpp | 22 +++-- library/RemoteServer.cpp | 29 ++++-- library/RemoteTools.cpp | 161 ++++++++++++++++++++++++++++++++- library/include/DataDefs.h | 17 +++- library/include/RemoteClient.h | 6 +- library/include/RemoteTools.h | 30 ++++++ library/proto/Basic.proto | 54 +++++++++++ library/proto/BasicApi.proto | 17 ++++ 9 files changed, 310 insertions(+), 29 deletions(-) create mode 100644 library/proto/Basic.proto create mode 100644 library/proto/BasicApi.proto diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 0db0042e6..63107456a 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -218,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) diff --git a/library/RemoteClient.cpp b/library/RemoteClient.cpp index 3a497ba8c..a66421aa0 100644 --- a/library/RemoteClient.cpp +++ b/library/RemoteClient.cpp @@ -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,14 +324,11 @@ 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 data(new uint8_t[fullsz]); RPCMessageHeader *hdr = (RPCMessageHeader*)data.get(); @@ -361,7 +358,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 +394,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); diff --git a/library/RemoteServer.cpp b/library/RemoteServer.cpp index ae8b0024c..bae62bb75 100644 --- a/library/RemoteServer.cpp +++ b/library/RemoteServer.cpp @@ -67,6 +67,11 @@ using dfproto::CoreTextNotification; using dfproto::CoreTextFragment; using google::protobuf::MessageLite; +bool readFullBuffer(CSimpleSocket *socket, void *buf, int size); +bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, + const ::google::protobuf::MessageLite *msg, bool size_ready); + + RPCService::RPCService() { owner = NULL; @@ -183,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"); @@ -243,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; @@ -278,6 +283,8 @@ void ServerConnection::threadFn(void *arg) } else { + buf.reset(); + reply = fn->out(); res = fn->execute(me->stream); } @@ -287,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; @@ -304,9 +318,6 @@ void ServerConnection::threadFn(void *arg) } else { - if (reply) - out_size = reply->ByteSize(); - header.id = RPC_REPLY_FAIL; header.size = res; diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index aa6324356..42bc84630 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -49,6 +49,18 @@ POSSIBILITY OF SUCH DAMAGE. #include "PluginManager.h" #include "MiscUtils.h" +#include "modules/Materials.h" + +#include "DataDefs.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 "BasicApi.pb.h" + #include #include #include @@ -56,9 +68,9 @@ POSSIBILITY OF SUCH DAMAGE. #include using namespace DFHack; +using namespace df::enums; +using namespace dfproto; -using dfproto::CoreTextNotification; -using dfproto::CoreTextFragment; using google::protobuf::MessageLite; void DFHack::strVectorToRepeatedField(RepeatedPtrField *pf, @@ -68,6 +80,149 @@ void DFHack::strVectorToRepeatedField(RepeatedPtrField *pf, *pf->Add() = vec[i]; } +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); + + 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_string(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_creature_id(mat.figure->race); + } + else + info->set_creature_id(mat.index); + break; + + case MaterialInfo::Plant: + info->set_plant_id(mat.index); + break; + } +} + +static void listMaterial(ListMaterialsRes *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 ListMaterialsRq *in, ListMaterialsRes *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; +} + CoreService::CoreService() { suspend_depth = 0; @@ -78,6 +233,8 @@ CoreService::CoreService() { // Add others here: addMethod("CoreSuspend", &CoreService::CoreSuspend); addMethod("CoreResume", &CoreService::CoreResume); + + addFunction("ListMaterials", ListMaterials); } CoreService::~CoreService() diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 46662efca..99694d949 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -373,7 +373,7 @@ namespace DFHack { * Find a flag array item by key string. Returns success code. */ template - inline bool find_bitfield_field(unsigned *idx, const std::string &name, const BitArray*) { + inline bool find_flagarray_field(unsigned *idx, const std::string &name, const BitArray*) { T tmp; if (!find_enum_item(&tmp, name) || tmp < 0) return false; *idx = unsigned(tmp); @@ -384,7 +384,7 @@ namespace DFHack { * Find a flag array item by key and set its value. Returns success code. */ template - inline bool set_bitfield_field(BitArray *bitfield, const std::string &name, int value) + inline bool set_flagarray_field(BitArray *bitfield, const std::string &name, int value) { T tmp; if (!find_enum_item(&tmp, name) || tmp < 0) return false; @@ -396,7 +396,7 @@ namespace DFHack { * Find a flag array item by key and retrieve its value. Returns success code. */ template - inline bool get_bitfield_field(int *value, const BitArray &bitfield, const std::string &name) + inline bool get_flagarray_field(int *value, const BitArray &bitfield, const std::string &name) { T tmp; if (!find_enum_item(&tmp, name) || tmp < 0) return false; @@ -411,13 +411,22 @@ namespace DFHack { * Represent flag array bits as strings in a vector. */ template - inline void bitfield_to_string(std::vector *pvec, const BitArray &val) { + inline void flagarray_to_string(std::vector *pvec, const BitArray &val) { typedef df::enum_traits 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 + inline std::string bitfield_to_string(const BitArray &val, const std::string &sep = " ") { + std::vector tmp; + flagarray_to_string(&tmp, val); + return join_strings(sep, tmp); + } } #define ENUM_ATTR(enum,attr,val) (df::enum_traits::attrs(val).attr) diff --git a/library/include/RemoteClient.h b/library/include/RemoteClient.h index 7858223f1..9eb8fb427 100644 --- a/library/include/RemoteClient.h +++ b/library/include/RemoteClient.h @@ -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; diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h index 00d5e5281..44a414467 100644 --- a/library/include/RemoteTools.h +++ b/library/include/RemoteTools.h @@ -29,8 +29,17 @@ distribution. #include "DataDefs.h" +#include "Basic.pb.h" + +namespace df +{ + struct material; +} + namespace DFHack { + class MaterialInfo; + using google::protobuf::RepeatedPtrField; DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField *pf, @@ -46,6 +55,27 @@ namespace DFHack strVectorToRepeatedField(pf, tmp); } + /** + * Represent flagarray bits as a repeated string field. + */ + template + inline void flagarray_to_string(RepeatedPtrField *pf, const BitArray &val) { + std::vector tmp; + flagarray_to_string(&tmp, val); + strVectorToRepeatedField(pf, tmp); + } + + ///// + + 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); + + ///// class CoreService : public RPCService { int suspend_depth; diff --git a/library/proto/Basic.proto b/library/proto/Basic.proto new file mode 100644 index 000000000..92a993b70 --- /dev/null +++ b/library/proto/Basic.proto @@ -0,0 +1,54 @@ +package dfproto; + +option optimize_for = LITE_RUNTIME; + +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 string flags = 4; + + 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 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 string inorganic_flags = 15; +}; + +message BasicMaterialInfoMask { + enum StateType { + Solid = 0; + Liquid = 1; + Gas = 2; + Powder = 3; + Paste = 4; + Pressed = 5; + }; + repeated StateType states = 1; + + optional bool flags = 2 [default = false]; + optional bool reaction = 3 [default = false]; + optional int32 temperature = 4; +}; + diff --git a/library/proto/BasicApi.proto b/library/proto/BasicApi.proto new file mode 100644 index 000000000..b5a569862 --- /dev/null +++ b/library/proto/BasicApi.proto @@ -0,0 +1,17 @@ +package dfproto; + +option optimize_for = LITE_RUNTIME; + +import "Basic.proto"; + +message ListMaterialsRq { + 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 ListMaterialsRes { + repeated BasicMaterialInfo value = 1; +}; From 368b92f81f5257eaf5bf4f11f41507a52f3d29c1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 17 Mar 2012 17:41:56 +0400 Subject: [PATCH 4/6] Add a RPC call to retrieve unit info. For performance reasons, material flag arrays are changed to use ints, with enum item name mappings retrievable via a different RPC call. --- library/RemoteServer.cpp | 2 +- library/RemoteTools.cpp | 166 +++++++++++++++++++++++++- library/include/BitArray.h | 2 +- library/include/RemoteTools.h | 47 ++++++++ library/include/modules/Translation.h | 3 +- library/include/modules/Units.h | 2 + library/modules/Translation.cpp | 34 +++--- library/modules/Units.cpp | 29 +++++ library/proto/Basic.proto | 60 +++++++++- library/proto/BasicApi.proto | 23 ++++ 10 files changed, 340 insertions(+), 28 deletions(-) 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 From 3d80a1ee35be9dcff1e53f74e5be3581c0d42743 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 17 Mar 2012 17:52:23 +0400 Subject: [PATCH 5/6] Use a more obscure serialize method to avoid recalculating byte size. --- library/RemoteClient.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/RemoteClient.cpp b/library/RemoteClient.cpp index a66421aa0..a1ac2ec92 100644 --- a/library/RemoteClient.cpp +++ b/library/RemoteClient.cpp @@ -335,8 +335,9 @@ bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg 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); } From aa63493bb81160e924382e617ac48431f1fba0c7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 17 Mar 2012 20:04:15 +0400 Subject: [PATCH 6/6] Add a ListSquads request. --- library/RemoteServer.cpp | 3 ++- library/RemoteTools.cpp | 45 ++++++++++++++++++++++++++----- library/include/RemoteServer.h | 48 +++++++++++++++++++++------------- library/proto/Basic.proto | 24 ++++++++++++----- library/proto/BasicApi.proto | 17 +++++++----- plugins/proto/rename.proto | 4 +-- plugins/rename.cpp | 4 +-- 7 files changed, 103 insertions(+), 42 deletions(-) diff --git a/library/RemoteServer.cpp b/library/RemoteServer.cpp index 8c486aff9..e12b8b043 100644 --- a/library/RemoteServer.cpp +++ b/library/RemoteServer.cpp @@ -331,7 +331,8 @@ void ServerConnection::threadFn(void *arg) // Cleanup if (fn) { - fn->reset(out_size > 128*1024 || in_size > 32*1024); + fn->reset((fn->flags & SF_CALLED_ONCE) || + (out_size > 128*1024 || in_size > 32*1024)); } } diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index ae955012c..1fdd26c74 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -54,6 +54,7 @@ POSSIBILITY OF SUCH DAMAGE. #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" @@ -63,6 +64,9 @@ POSSIBILITY OF SUCH DAMAGE. #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" @@ -268,7 +272,7 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit, } static command_result ListEnums(color_ostream &stream, - const EmptyMessage *, ListEnumsRes *out) + const EmptyMessage *, ListEnumsOut *out) { #define ENUM(name) describe_enum(out->mutable_##name()); #define BITFIELD(name) describe_bitfield(out->mutable_##name()); @@ -283,7 +287,7 @@ static command_result ListEnums(color_ostream &stream, #undef BITFIELD } -static void listMaterial(ListMaterialsRes *out, int type, int index, const BasicMaterialInfoMask *mask) +static void listMaterial(ListMaterialsOut *out, int type, int index, const BasicMaterialInfoMask *mask) { MaterialInfo info(type, index); if (info.isValid()) @@ -291,7 +295,7 @@ static void listMaterial(ListMaterialsRes *out, int type, int index, const Basic } static command_result ListMaterials(color_ostream &stream, - const ListMaterialsRq *in, ListMaterialsRes *out) + const ListMaterialsIn *in, ListMaterialsOut *out) { CoreSuspender suspend; @@ -344,7 +348,7 @@ static command_result ListMaterials(color_ostream &stream, } static command_result ListUnits(color_ostream &stream, - const ListUnitsRq *in, ListUnitsRes *out) + const ListUnitsIn *in, ListUnitsOut *out) { CoreSuspender suspend; @@ -379,6 +383,34 @@ static command_result ListUnits(color_ostream &stream, 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; @@ -390,9 +422,10 @@ CoreService::CoreService() { addMethod("CoreSuspend", &CoreService::CoreSuspend); addMethod("CoreResume", &CoreService::CoreResume); - addFunction("ListEnums", ListEnums); - addFunction("ListMaterials", ListMaterials); + addFunction("ListEnums", ListEnums, SF_CALLED_ONCE); + addFunction("ListMaterials", ListMaterials, SF_CALLED_ONCE); addFunction("ListUnits", ListUnits); + addFunction("ListSquads", ListSquads); } CoreService::~CoreService() diff --git a/library/include/RemoteServer.h b/library/include/RemoteServer.h index 7e45ba560..d7b216f7f 100644 --- a/library/include/RemoteServer.h +++ b/library/include/RemoteServer.h @@ -40,9 +40,16 @@ namespace DFHack 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; @@ -51,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; @@ -67,8 +75,8 @@ namespace DFHack In *in() { return static_cast(RPCFunctionBase::in()); } Out *out() { return static_cast(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()); } @@ -84,8 +92,8 @@ namespace DFHack In *in() { return static_cast(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()); } @@ -102,8 +110,8 @@ namespace DFHack In *in() { return static_cast(RPCFunctionBase::in()); } Out *out() { return static_cast(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) { @@ -121,8 +129,8 @@ namespace DFHack In *in() { return static_cast(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) { @@ -154,19 +162,21 @@ namespace DFHack template 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(this, name, fptr)); + functions.push_back(new ServerFunction(this, name, flags, fptr)); } template 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(this, name, fptr)); + functions.push_back(new VoidServerFunction(this, name, flags, fptr)); } protected: @@ -175,19 +185,21 @@ namespace DFHack template 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(this, name, fptr)); + functions.push_back(new ServerMethod(this, name, flags, fptr)); } template 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(this, name, fptr)); + functions.push_back(new VoidServerMethod(this, name, flags, fptr)); } }; diff --git a/library/proto/Basic.proto b/library/proto/Basic.proto index 1a9a8f1b4..6e58345ff 100644 --- a/library/proto/Basic.proto +++ b/library/proto/Basic.proto @@ -52,10 +52,10 @@ message BasicMaterialInfoMask { Pressed = 5; }; repeated StateType states = 1; + optional int32 temperature = 4 [default = 10015]; optional bool flags = 2 [default = false]; optional bool reaction = 3 [default = false]; - optional int32 temperature = 4 [default = 10015]; }; message NameInfo { @@ -68,6 +68,12 @@ message NameInfo { 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; @@ -86,12 +92,7 @@ message BasicUnitInfo { repeated int32 labors = 11; - message Skill { - required int32 id = 1; - required int32 level = 2; - required int32 experience = 3; - }; - repeated Skill skills = 12; + repeated SkillInfo skills = 12; required int32 pos_x = 13; required int32 pos_y = 14; @@ -102,3 +103,12 @@ 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; +}; diff --git a/library/proto/BasicApi.proto b/library/proto/BasicApi.proto index ed1276fef..33f2a5764 100644 --- a/library/proto/BasicApi.proto +++ b/library/proto/BasicApi.proto @@ -4,7 +4,7 @@ option optimize_for = LITE_RUNTIME; import "Basic.proto"; -message ListEnumsRes { +message ListEnumsOut { repeated EnumItemName material_flags = 1; repeated EnumItemName inorganic_flags = 2; @@ -16,7 +16,7 @@ message ListEnumsRes { repeated EnumItemName job_skill = 7; }; -message ListMaterialsRq { +message ListMaterialsIn { optional BasicMaterialInfoMask mask = 1; repeated BasicMaterialId id_list = 2; optional bool builtin = 3; @@ -24,17 +24,22 @@ message ListMaterialsRq { optional bool creatures = 5; optional bool plants = 6; }; -message ListMaterialsRes { +message ListMaterialsOut { repeated BasicMaterialInfo value = 1; }; -message ListUnitsRq { +message ListUnitsIn { optional BasicUnitInfoMask mask = 1; repeated int32 id_list = 2; optional int32 race = 3; optional int32 civ_id = 4; }; -message ListUnitsRes { +message ListUnitsOut { repeated BasicUnitInfo value = 1; -}; \ No newline at end of file +}; + +message ListSquadsIn {} +message ListSquadsOut { + repeated BasicSquadInfo value = 1; +} diff --git a/plugins/proto/rename.proto b/plugins/proto/rename.proto index a139ceaa0..a6c3b055a 100644 --- a/plugins/proto/rename.proto +++ b/plugins/proto/rename.proto @@ -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; diff --git a/plugins/rename.cpp b/plugins/rename.cpp index ae6edc652..9544901fc 100644 --- a/plugins/rename.cpp +++ b/plugins/rename.cpp @@ -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;