Add a ListMaterials remote call for bulk download of basic material info.

develop
Alexander Gavrilov 2012-03-17 15:36:42 +04:00
parent 58eb199036
commit 15ccfbb086
9 changed files with 310 additions and 29 deletions

@ -218,8 +218,7 @@ ENDIF()
ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES}) ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES})
ADD_DEPENDENCIES(dfhack generate_headers) ADD_DEPENDENCIES(dfhack generate_headers)
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ${PROJECT_PROTO_SRCS})
proto/CoreProtocol.pb.cc)
ADD_DEPENDENCIES(dfhack-client dfhack) ADD_DEPENDENCIES(dfhack-client dfhack)
ADD_EXECUTABLE(dfhack-run dfhack-run.cpp) ADD_EXECUTABLE(dfhack-run dfhack-run.cpp)

@ -112,7 +112,7 @@ RemoteClient::~RemoteClient()
delete p_default_output; delete p_default_output;
} }
bool DFHack::readFullBuffer(CSimpleSocket *socket, void *buf, int size) bool readFullBuffer(CSimpleSocket *socket, void *buf, int size)
{ {
if (!socket->IsSocketValid()) if (!socket->IsSocketValid())
return false; return false;
@ -324,14 +324,11 @@ bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client,
return client->bind(out, this, name, proto); 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); int fullsz = size + sizeof(RPCMessageHeader);
if (psz)
*psz = size;
std::auto_ptr<uint8_t> data(new uint8_t[fullsz]); std::auto_ptr<uint8_t> data(new uint8_t[fullsz]);
RPCMessageHeader *hdr = (RPCMessageHeader*)data.get(); RPCMessageHeader *hdr = (RPCMessageHeader*)data.get();
@ -361,7 +358,16 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
return CR_LINK_FAILURE; 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", out.printerr("In call to %s::%s: I/O error in send.\n",
this->proto.c_str(), this->name.c_str()); 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) if (header.id == RPC_REPLY_FAIL)
return header.size == CR_OK ? CR_FAILURE : command_result(header.size); 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", out.printerr("In call to %s::%s: invalid received size %d.\n",
this->proto.c_str(), this->name.c_str(), header.size); this->proto.c_str(), this->name.c_str(), header.size);

@ -67,6 +67,11 @@ using dfproto::CoreTextNotification;
using dfproto::CoreTextFragment; using dfproto::CoreTextFragment;
using google::protobuf::MessageLite; 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() RPCService::RPCService()
{ {
owner = NULL; owner = NULL;
@ -183,7 +188,7 @@ void ServerConnection::connection_ostream::flush_proxy()
buffer.clear(); buffer.clear();
if (!sendRemoteMessage(owner->socket, RPC_REPLY_TEXT, &msg)) if (!sendRemoteMessage(owner->socket, RPC_REPLY_TEXT, &msg, false))
{ {
owner->in_error = true; owner->in_error = true;
Core::printerr("Error writing text into client socket.\n"); Core::printerr("Error writing text into client socket.\n");
@ -243,7 +248,7 @@ void ServerConnection::threadFn(void *arg)
if (header.id == RPC_REQUEST_QUIT) if (header.id == RPC_REQUEST_QUIT)
break; 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); out.printerr("In RPC server: invalid received size %d.\n", header.size);
break; break;
@ -278,6 +283,8 @@ void ServerConnection::threadFn(void *arg)
} }
else else
{ {
buf.reset();
reply = fn->out(); reply = fn->out();
res = fn->execute(me->stream); res = fn->execute(me->stream);
} }
@ -287,16 +294,23 @@ void ServerConnection::threadFn(void *arg)
if (me->in_error) if (me->in_error)
break; break;
me->stream.flush();
//out.print("Answer %d:%d\n", res, reply); //out.print("Answer %d:%d\n", res, reply);
// Send 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 (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"); out.printerr("In RPC server: I/O error in send result.\n");
break; break;
@ -304,9 +318,6 @@ void ServerConnection::threadFn(void *arg)
} }
else else
{ {
if (reply)
out_size = reply->ByteSize();
header.id = RPC_REPLY_FAIL; header.id = RPC_REPLY_FAIL;
header.size = res; header.size = res;

@ -49,6 +49,18 @@ POSSIBILITY OF SUCH DAMAGE.
#include "PluginManager.h" #include "PluginManager.h"
#include "MiscUtils.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 <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
@ -56,9 +68,9 @@ POSSIBILITY OF SUCH DAMAGE.
#include <memory> #include <memory>
using namespace DFHack; using namespace DFHack;
using namespace df::enums;
using namespace dfproto;
using dfproto::CoreTextNotification;
using dfproto::CoreTextFragment;
using google::protobuf::MessageLite; using google::protobuf::MessageLite;
void DFHack::strVectorToRepeatedField(RepeatedPtrField<std::string> *pf, void DFHack::strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
@ -68,6 +80,149 @@ void DFHack::strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
*pf->Add() = vec[i]; *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() { CoreService::CoreService() {
suspend_depth = 0; suspend_depth = 0;
@ -78,6 +233,8 @@ CoreService::CoreService() {
// Add others here: // Add others here:
addMethod("CoreSuspend", &CoreService::CoreSuspend); addMethod("CoreSuspend", &CoreService::CoreSuspend);
addMethod("CoreResume", &CoreService::CoreResume); addMethod("CoreResume", &CoreService::CoreResume);
addFunction("ListMaterials", ListMaterials);
} }
CoreService::~CoreService() CoreService::~CoreService()

@ -373,7 +373,7 @@ namespace DFHack {
* Find a flag array item by key string. Returns success code. * Find a flag array item by key string. Returns success code.
*/ */
template<class T> template<class T>
inline bool find_bitfield_field(unsigned *idx, const std::string &name, const BitArray<T>*) { inline bool find_flagarray_field(unsigned *idx, const std::string &name, const BitArray<T>*) {
T tmp; T tmp;
if (!find_enum_item(&tmp, name) || tmp < 0) return false; if (!find_enum_item(&tmp, name) || tmp < 0) return false;
*idx = unsigned(tmp); *idx = unsigned(tmp);
@ -384,7 +384,7 @@ namespace DFHack {
* Find a flag array item by key and set its value. Returns success code. * Find a flag array item by key and set its value. Returns success code.
*/ */
template<class T> template<class T>
inline bool set_bitfield_field(BitArray<T> *bitfield, const std::string &name, int value) inline bool set_flagarray_field(BitArray<T> *bitfield, const std::string &name, int value)
{ {
T tmp; T tmp;
if (!find_enum_item(&tmp, name) || tmp < 0) return false; 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. * Find a flag array item by key and retrieve its value. Returns success code.
*/ */
template<class T> template<class T>
inline bool get_bitfield_field(int *value, const BitArray<T> &bitfield, const std::string &name) inline bool get_flagarray_field(int *value, const BitArray<T> &bitfield, const std::string &name)
{ {
T tmp; T tmp;
if (!find_enum_item(&tmp, name) || tmp < 0) return false; 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. * Represent flag array bits as strings in a vector.
*/ */
template<class T> template<class T>
inline void bitfield_to_string(std::vector<std::string> *pvec, const BitArray<T> &val) { inline void flagarray_to_string(std::vector<std::string> *pvec, const BitArray<T> &val) {
typedef df::enum_traits<T> traits; typedef df::enum_traits<T> traits;
int size = traits::last_item_value-traits::first_item_value+1; int size = traits::last_item_value-traits::first_item_value+1;
flagarrayToString(pvec, val.bits, val.size, flagarrayToString(pvec, val.bits, val.size,
(int)traits::first_item_value, size, traits::key_table); (int)traits::first_item_value, size, traits::key_table);
} }
/**
* Represent flag array bits as a string, using sep as join separator.
*/
template<class T>
inline std::string bitfield_to_string(const BitArray<T> &val, const std::string &sep = " ") {
std::vector<std::string> tmp;
flagarray_to_string<T>(&tmp, val);
return join_strings(sep, tmp);
}
} }
#define ENUM_ATTR(enum,attr,val) (df::enum_traits<df::enum>::attrs(val).attr) #define ENUM_ATTR(enum,attr,val) (df::enum_traits<df::enum>::attrs(val).attr)

@ -66,6 +66,8 @@ namespace DFHack
}; };
struct RPCMessageHeader { struct RPCMessageHeader {
static const int MAX_MESSAGE_SIZE = 8*1048756;
int16_t id; int16_t id;
int32_t size; 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 class DFHACK_EXPORT RemoteClient
{ {
friend class RemoteFunctionBase; friend class RemoteFunctionBase;

@ -29,8 +29,17 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
#include "Basic.pb.h"
namespace df
{
struct material;
}
namespace DFHack namespace DFHack
{ {
class MaterialInfo;
using google::protobuf::RepeatedPtrField; using google::protobuf::RepeatedPtrField;
DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField<std::string> *pf, DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
@ -46,6 +55,27 @@ namespace DFHack
strVectorToRepeatedField(pf, tmp); strVectorToRepeatedField(pf, tmp);
} }
/**
* Represent flagarray bits as a repeated string field.
*/
template<class T>
inline void flagarray_to_string(RepeatedPtrField<std::string> *pf, const BitArray<T> &val) {
std::vector<std::string> tmp;
flagarray_to_string<T>(&tmp, val);
strVectorToRepeatedField(pf, tmp);
}
/////
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 { class CoreService : public RPCService {
int suspend_depth; int suspend_depth;

@ -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;
};

@ -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;
};