Skip to content

Commit 15ccfbb

Browse files
committed
Add a ListMaterials remote call for bulk download of basic material info.
1 parent 58eb199 commit 15ccfbb

9 files changed

Lines changed: 310 additions & 29 deletions

File tree

library/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,7 @@ ENDIF()
218218
ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES})
219219
ADD_DEPENDENCIES(dfhack generate_headers)
220220

221-
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp
222-
proto/CoreProtocol.pb.cc)
221+
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ${PROJECT_PROTO_SRCS})
223222
ADD_DEPENDENCIES(dfhack-client dfhack)
224223

225224
ADD_EXECUTABLE(dfhack-run dfhack-run.cpp)

library/RemoteClient.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ RemoteClient::~RemoteClient()
112112
delete p_default_output;
113113
}
114114

115-
bool DFHack::readFullBuffer(CSimpleSocket *socket, void *buf, int size)
115+
bool readFullBuffer(CSimpleSocket *socket, void *buf, int size)
116116
{
117117
if (!socket->IsSocketValid())
118118
return false;
@@ -324,14 +324,11 @@ bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client,
324324
return client->bind(out, this, name, proto);
325325
}
326326

327-
bool DFHack::sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg, int *psz)
327+
bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg, bool size_ready)
328328
{
329-
int size = msg->ByteSize();
329+
int size = size_ready ? msg->GetCachedSize() : msg->ByteSize();
330330
int fullsz = size + sizeof(RPCMessageHeader);
331331

332-
if (psz)
333-
*psz = size;
334-
335332
std::auto_ptr<uint8_t> data(new uint8_t[fullsz]);
336333
RPCMessageHeader *hdr = (RPCMessageHeader*)data.get();
337334

@@ -361,7 +358,16 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
361358
return CR_LINK_FAILURE;
362359
}
363360

364-
if (!sendRemoteMessage(p_client->socket, id, input))
361+
int send_size = input->ByteSize();
362+
363+
if (send_size > RPCMessageHeader::MAX_MESSAGE_SIZE)
364+
{
365+
out.printerr("In call to %s::%s: message too large: %d.\n",
366+
this->proto.c_str(), this->name.c_str(), send_size);
367+
return CR_LINK_FAILURE;
368+
}
369+
370+
if (!sendRemoteMessage(p_client->socket, id, input, true))
365371
{
366372
out.printerr("In call to %s::%s: I/O error in send.\n",
367373
this->proto.c_str(), this->name.c_str());
@@ -388,7 +394,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
388394
if (header.id == RPC_REPLY_FAIL)
389395
return header.size == CR_OK ? CR_FAILURE : command_result(header.size);
390396

391-
if (header.size < 0 || header.size > 2*1048576)
397+
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)
392398
{
393399
out.printerr("In call to %s::%s: invalid received size %d.\n",
394400
this->proto.c_str(), this->name.c_str(), header.size);

library/RemoteServer.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ using dfproto::CoreTextNotification;
6767
using dfproto::CoreTextFragment;
6868
using google::protobuf::MessageLite;
6969

70+
bool readFullBuffer(CSimpleSocket *socket, void *buf, int size);
71+
bool sendRemoteMessage(CSimpleSocket *socket, int16_t id,
72+
const ::google::protobuf::MessageLite *msg, bool size_ready);
73+
74+
7075
RPCService::RPCService()
7176
{
7277
owner = NULL;
@@ -183,7 +188,7 @@ void ServerConnection::connection_ostream::flush_proxy()
183188

184189
buffer.clear();
185190

186-
if (!sendRemoteMessage(owner->socket, RPC_REPLY_TEXT, &msg))
191+
if (!sendRemoteMessage(owner->socket, RPC_REPLY_TEXT, &msg, false))
187192
{
188193
owner->in_error = true;
189194
Core::printerr("Error writing text into client socket.\n");
@@ -243,7 +248,7 @@ void ServerConnection::threadFn(void *arg)
243248
if (header.id == RPC_REQUEST_QUIT)
244249
break;
245250

246-
if (header.size < 0 || header.size > 2*1048576)
251+
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)
247252
{
248253
out.printerr("In RPC server: invalid received size %d.\n", header.size);
249254
break;
@@ -278,6 +283,8 @@ void ServerConnection::threadFn(void *arg)
278283
}
279284
else
280285
{
286+
buf.reset();
287+
281288
reply = fn->out();
282289
res = fn->execute(me->stream);
283290
}
@@ -287,26 +294,30 @@ void ServerConnection::threadFn(void *arg)
287294
if (me->in_error)
288295
break;
289296

290-
me->stream.flush();
291-
292297
//out.print("Answer %d:%d\n", res, reply);
293298

294299
// Send reply
295-
int out_size = 0;
300+
int out_size = (reply ? reply->ByteSize() : 0);
301+
302+
if (out_size > RPCMessageHeader::MAX_MESSAGE_SIZE)
303+
{
304+
me->stream.printerr("In call to %s: reply too large: %d.\n",
305+
(fn ? fn->name : "UNKNOWN"), out_size);
306+
res = CR_LINK_FAILURE;
307+
}
308+
309+
me->stream.flush();
296310

297311
if (res == CR_OK && reply)
298312
{
299-
if (!sendRemoteMessage(me->socket, RPC_REPLY_RESULT, reply, &out_size))
313+
if (!sendRemoteMessage(me->socket, RPC_REPLY_RESULT, reply, true))
300314
{
301315
out.printerr("In RPC server: I/O error in send result.\n");
302316
break;
303317
}
304318
}
305319
else
306320
{
307-
if (reply)
308-
out_size = reply->ByteSize();
309-
310321
header.id = RPC_REPLY_FAIL;
311322
header.size = res;
312323

library/RemoteTools.cpp

Lines changed: 159 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,28 @@ POSSIBILITY OF SUCH DAMAGE.
4949
#include "PluginManager.h"
5050
#include "MiscUtils.h"
5151

52+
#include "modules/Materials.h"
53+
54+
#include "DataDefs.h"
55+
#include "df/material.h"
56+
#include "df/matter_state.h"
57+
#include "df/inorganic_raw.h"
58+
#include "df/creature_raw.h"
59+
#include "df/plant_raw.h"
60+
#include "df/historical_figure.h"
61+
62+
#include "BasicApi.pb.h"
63+
5264
#include <cstdio>
5365
#include <cstdlib>
5466
#include <sstream>
5567

5668
#include <memory>
5769

5870
using namespace DFHack;
71+
using namespace df::enums;
72+
using namespace dfproto;
5973

60-
using dfproto::CoreTextNotification;
61-
using dfproto::CoreTextFragment;
6274
using google::protobuf::MessageLite;
6375

6476
void DFHack::strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
@@ -68,6 +80,149 @@ void DFHack::strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
6880
*pf->Add() = vec[i];
6981
}
7082

83+
void DFHack::describeMaterial(BasicMaterialInfo *info, df::material *mat,
84+
const BasicMaterialInfoMask *mask)
85+
{
86+
info->set_token(mat->id);
87+
88+
if (mask && mask->flags())
89+
flagarray_to_string(info->mutable_flags(), mat->flags);
90+
91+
info->set_name_prefix(mat->prefix);
92+
93+
if (!mask || mask->states_size() == 0)
94+
{
95+
df::matter_state state = matter_state::Solid;
96+
int temp = (mask && mask->has_temperature()) ? mask->temperature() : 10015;
97+
98+
if (temp >= mat->heat.melting_point)
99+
state = matter_state::Liquid;
100+
if (temp >= mat->heat.boiling_point)
101+
state = matter_state::Gas;
102+
103+
info->add_state_color(mat->state_color[state]);
104+
info->add_state_name(mat->state_name[state]);
105+
info->add_state_adj(mat->state_adj[state]);
106+
}
107+
else
108+
{
109+
for (int i = 0; i < mask->states_size(); i++)
110+
{
111+
info->add_state_color(mat->state_color[i]);
112+
info->add_state_name(mat->state_name[i]);
113+
info->add_state_adj(mat->state_adj[i]);
114+
}
115+
}
116+
117+
if (mask && mask->reaction())
118+
{
119+
for (size_t i = 0; i < mat->reaction_class.size(); i++)
120+
info->add_reaction_class(*mat->reaction_class[i]);
121+
122+
for (size_t i = 0; i < mat->reaction_product.id.size(); i++)
123+
{
124+
auto ptr = info->add_reaction_product();
125+
ptr->set_id(*mat->reaction_product.id[i]);
126+
ptr->set_type(mat->reaction_product.material.mat_type[i]);
127+
ptr->set_index(mat->reaction_product.material.mat_index[i]);
128+
}
129+
}
130+
}
131+
132+
void DFHack::describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat,
133+
const BasicMaterialInfoMask *mask)
134+
{
135+
assert(mat.isValid());
136+
137+
info->set_type(mat.type);
138+
info->set_index(mat.index);
139+
140+
describeMaterial(info, mat.material, mask);
141+
142+
switch (mat.mode) {
143+
case MaterialInfo::Inorganic:
144+
info->set_token(mat.inorganic->id);
145+
if (mask && mask->flags())
146+
flagarray_to_string(info->mutable_inorganic_flags(), mat.inorganic->flags);
147+
break;
148+
149+
case MaterialInfo::Creature:
150+
info->set_subtype(mat.subtype);
151+
if (mat.figure)
152+
{
153+
info->set_hfig_id(mat.index);
154+
info->set_creature_id(mat.figure->race);
155+
}
156+
else
157+
info->set_creature_id(mat.index);
158+
break;
159+
160+
case MaterialInfo::Plant:
161+
info->set_plant_id(mat.index);
162+
break;
163+
}
164+
}
165+
166+
static void listMaterial(ListMaterialsRes *out, int type, int index, const BasicMaterialInfoMask *mask)
167+
{
168+
MaterialInfo info(type, index);
169+
if (info.isValid())
170+
describeMaterial(out->add_value(), info, mask);
171+
}
172+
173+
static command_result ListMaterials(color_ostream &stream,
174+
const ListMaterialsRq *in, ListMaterialsRes *out)
175+
{
176+
CoreSuspender suspend;
177+
178+
auto mask = in->has_mask() ? &in->mask() : NULL;
179+
180+
for (int i = 0; i < in->id_list_size(); i++)
181+
{
182+
auto &elt = in->id_list(i);
183+
listMaterial(out, elt.type(), elt.index(), mask);
184+
}
185+
186+
if (in->builtin())
187+
{
188+
for (int i = 0; i < MaterialInfo::NUM_BUILTIN; i++)
189+
listMaterial(out, i, -1, mask);
190+
}
191+
192+
if (in->inorganic())
193+
{
194+
auto &vec = df::inorganic_raw::get_vector();
195+
for (size_t i = 0; i < vec.size(); i++)
196+
listMaterial(out, 0, i, mask);
197+
}
198+
199+
if (in->creatures())
200+
{
201+
auto &vec = df::creature_raw::get_vector();
202+
for (size_t i = 0; i < vec.size(); i++)
203+
{
204+
auto praw = vec[i];
205+
206+
for (size_t j = 0; j < praw->material.size(); j++)
207+
listMaterial(out, MaterialInfo::CREATURE_BASE+j, i, mask);
208+
}
209+
}
210+
211+
if (in->plants())
212+
{
213+
auto &vec = df::plant_raw::get_vector();
214+
for (size_t i = 0; i < vec.size(); i++)
215+
{
216+
auto praw = vec[i];
217+
218+
for (size_t j = 0; j < praw->material.size(); j++)
219+
listMaterial(out, MaterialInfo::PLANT_BASE+j, i, mask);
220+
}
221+
}
222+
223+
return out->value_size() ? CR_OK : CR_NOT_FOUND;
224+
}
225+
71226
CoreService::CoreService() {
72227
suspend_depth = 0;
73228

@@ -78,6 +233,8 @@ CoreService::CoreService() {
78233
// Add others here:
79234
addMethod("CoreSuspend", &CoreService::CoreSuspend);
80235
addMethod("CoreResume", &CoreService::CoreResume);
236+
237+
addFunction("ListMaterials", ListMaterials);
81238
}
82239

83240
CoreService::~CoreService()

library/include/DataDefs.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ namespace DFHack {
373373
* Find a flag array item by key string. Returns success code.
374374
*/
375375
template<class T>
376-
inline bool find_bitfield_field(unsigned *idx, const std::string &name, const BitArray<T>*) {
376+
inline bool find_flagarray_field(unsigned *idx, const std::string &name, const BitArray<T>*) {
377377
T tmp;
378378
if (!find_enum_item(&tmp, name) || tmp < 0) return false;
379379
*idx = unsigned(tmp);
@@ -384,7 +384,7 @@ namespace DFHack {
384384
* Find a flag array item by key and set its value. Returns success code.
385385
*/
386386
template<class T>
387-
inline bool set_bitfield_field(BitArray<T> *bitfield, const std::string &name, int value)
387+
inline bool set_flagarray_field(BitArray<T> *bitfield, const std::string &name, int value)
388388
{
389389
T tmp;
390390
if (!find_enum_item(&tmp, name) || tmp < 0) return false;
@@ -396,7 +396,7 @@ namespace DFHack {
396396
* Find a flag array item by key and retrieve its value. Returns success code.
397397
*/
398398
template<class T>
399-
inline bool get_bitfield_field(int *value, const BitArray<T> &bitfield, const std::string &name)
399+
inline bool get_flagarray_field(int *value, const BitArray<T> &bitfield, const std::string &name)
400400
{
401401
T tmp;
402402
if (!find_enum_item(&tmp, name) || tmp < 0) return false;
@@ -411,13 +411,22 @@ namespace DFHack {
411411
* Represent flag array bits as strings in a vector.
412412
*/
413413
template<class T>
414-
inline void bitfield_to_string(std::vector<std::string> *pvec, const BitArray<T> &val) {
414+
inline void flagarray_to_string(std::vector<std::string> *pvec, const BitArray<T> &val) {
415415
typedef df::enum_traits<T> traits;
416416
int size = traits::last_item_value-traits::first_item_value+1;
417417
flagarrayToString(pvec, val.bits, val.size,
418418
(int)traits::first_item_value, size, traits::key_table);
419419
}
420420

421+
/**
422+
* Represent flag array bits as a string, using sep as join separator.
423+
*/
424+
template<class T>
425+
inline std::string bitfield_to_string(const BitArray<T> &val, const std::string &sep = " ") {
426+
std::vector<std::string> tmp;
427+
flagarray_to_string<T>(&tmp, val);
428+
return join_strings(sep, tmp);
429+
}
421430
}
422431

423432
#define ENUM_ATTR(enum,attr,val) (df::enum_traits<df::enum>::attrs(val).attr)

0 commit comments

Comments
 (0)