develop
Petr Mrázek 2012-03-18 13:10:47 +01:00
commit 19a317deeb
9 changed files with 356 additions and 43 deletions

@ -198,6 +198,14 @@ void ServerConnection::connection_ostream::flush_proxy()
void ServerConnection::threadFn(void *arg)
{
ServerConnection *me = (ServerConnection*)arg;
me->threadFn();
delete me;
}
void ServerConnection::threadFn()
{
color_ostream_proxy out(Core::getInstance().getConsole());
/* Handshake */
@ -205,10 +213,9 @@ void ServerConnection::threadFn(void *arg)
{
RPCHandshakeHeader header;
if (!readFullBuffer(me->socket, &header, sizeof(header)))
if (!readFullBuffer(socket, &header, sizeof(header)))
{
out << "In RPC server: could not read handshake header." << endl;
delete me;
return;
}
@ -216,17 +223,15 @@ void ServerConnection::threadFn(void *arg)
header.version != 1)
{
out << "In RPC server: invalid handshake header." << endl;
delete me;
return;
}
memcpy(header.magic, RPCHandshakeHeader::RESPONSE_MAGIC, sizeof(header.magic));
header.version = 1;
if (me->socket->Send((uint8*)&header, sizeof(header)) != sizeof(header))
if (socket->Send((uint8*)&header, sizeof(header)) != sizeof(header))
{
out << "In RPC server: could not send handshake response." << endl;
delete me;
return;
}
}
@ -235,11 +240,11 @@ void ServerConnection::threadFn(void *arg)
std::cerr << "Client connection established." << endl;
while (!me->in_error) {
while (!in_error) {
// Read the message
RPCMessageHeader header;
if (!readFullBuffer(me->socket, &header, sizeof(header)))
if (!readFullBuffer(socket, &header, sizeof(header)))
{
out.printerr("In RPC server: I/O error in receive header.\n");
break;
@ -256,7 +261,7 @@ void ServerConnection::threadFn(void *arg)
std::auto_ptr<uint8_t> buf(new uint8_t[header.size]);
if (!readFullBuffer(me->socket, buf.get(), header.size))
if (!readFullBuffer(socket, buf.get(), header.size))
{
out.printerr("In RPC server: I/O error in receive %d bytes of data.\n", header.size);
break;
@ -267,31 +272,40 @@ void ServerConnection::threadFn(void *arg)
// Find and call the function
int in_size = header.size;
ServerFunctionBase *fn = vector_get(me->functions, header.id);
ServerFunctionBase *fn = vector_get(functions, header.id);
MessageLite *reply = NULL;
command_result res = CR_FAILURE;
if (!fn)
{
me->stream.printerr("RPC call of invalid id %d\n", header.id);
stream.printerr("RPC call of invalid id %d\n", header.id);
}
else
{
if (!fn->in()->ParseFromArray(buf.get(), header.size))
{
me->stream.printerr("In call to %s: could not decode input args.\n", fn->name);
stream.printerr("In call to %s: could not decode input args.\n", fn->name);
}
else
{
buf.reset();
reply = fn->out();
res = fn->execute(me->stream);
if (fn->flags & SF_DONT_SUSPEND)
{
res = fn->execute(stream);
}
else
{
CoreSuspender suspend;
res = fn->execute(stream);
}
}
}
// Flush all text output
if (me->in_error)
if (in_error)
break;
//out.print("Answer %d:%d\n", res, reply);
@ -301,16 +315,16 @@ void ServerConnection::threadFn(void *arg)
if (out_size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{
me->stream.printerr("In call to %s: reply too large: %d.\n",
stream.printerr("In call to %s: reply too large: %d.\n",
(fn ? fn->name : "UNKNOWN"), out_size);
res = CR_LINK_FAILURE;
}
me->stream.flush();
stream.flush();
if (res == CR_OK && reply)
{
if (!sendRemoteMessage(me->socket, RPC_REPLY_RESULT, reply, true))
if (!sendRemoteMessage(socket, RPC_REPLY_RESULT, reply, true))
{
out.printerr("In RPC server: I/O error in send result.\n");
break;
@ -321,7 +335,7 @@ void ServerConnection::threadFn(void *arg)
header.id = RPC_REPLY_FAIL;
header.size = res;
if (me->socket->Send((uint8_t*)&header, sizeof(header)) != sizeof(header))
if (socket->Send((uint8_t*)&header, sizeof(header)) != sizeof(header))
{
out.printerr("In RPC server: I/O error in send failure code.\n");
break;
@ -337,8 +351,6 @@ void ServerConnection::threadFn(void *arg)
}
std::cerr << "Shutting down client connection." << endl;
delete me;
}
ServerMain::ServerMain()

@ -48,13 +48,18 @@ POSSIBILITY OF SUCH DAMAGE.
#include "RemoteTools.h"
#include "PluginManager.h"
#include "MiscUtils.h"
#include "VersionInfo.h"
#include "modules/Materials.h"
#include "modules/Translation.h"
#include "modules/Units.h"
#include "modules/World.h"
#include "DataDefs.h"
#include "df/ui.h"
#include "df/ui_advmode.h"
#include "df/world.h"
#include "df/world_data.h"
#include "df/unit.h"
#include "df/unit_soul.h"
#include "df/unit_skill.h"
@ -63,10 +68,12 @@ POSSIBILITY OF SUCH DAMAGE.
#include "df/inorganic_raw.h"
#include "df/creature_raw.h"
#include "df/plant_raw.h"
#include "df/nemesis_record.h"
#include "df/historical_figure.h"
#include "df/historical_entity.h"
#include "df/squad.h"
#include "df/squad_position.h"
#include "df/death_info.h"
#include "BasicApi.pb.h"
@ -94,10 +101,12 @@ void DFHack::describeEnum(RepeatedPtrField<EnumItemName> *pf, int base,
{
for (int i = 0; i < size; i++)
{
const char *key = names[i];
if (!key)
continue;
auto item = pf->Add();
item->set_value(base+i);
const char *key = names[i];
if (key)
item->set_name(key);
}
}
@ -107,11 +116,16 @@ void DFHack::describeBitfield(RepeatedPtrField<EnumItemName> *pf,
{
for (int i = 0; i < size; i++)
{
const char *key = items[i].name;
if (!key && items[i].size <= 1)
continue;
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);
@ -217,11 +231,22 @@ void DFHack::describeName(NameInfo *info, df::language_name *name)
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::describeNameTriple(NameTriple *info, const std::string &name,
const std::string &plural, const std::string &adj)
{
info->set_normal(name);
if (!plural.empty() && plural != name)
info->set_plural(plural);
if (!adj.empty() && adj != name)
info->set_adjective(adj);
}
void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
const BasicUnitInfoMask *mask)
{
@ -249,6 +274,27 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
if (unit->hist_figure_id >= 0)
info->set_histfig_id(unit->hist_figure_id);
if (unit->counters.death_id >= 0)
{
info->set_death_id(unit->counters.death_id);
if (auto death = df::death_info::find(unit->counters.death_id))
info->set_death_flags(death->flags.whole);
}
if (mask && mask->profession())
{
if (unit->profession >= 0)
info->set_profession(unit->profession);
if (!unit->custom_profession.empty())
info->set_custom_profession(unit->custom_profession);
if (unit->military.squad_index >= 0)
{
info->set_squad_id(unit->military.squad_index);
info->set_squad_position(unit->military.squad_position);
}
}
if (mask && mask->labors())
{
for (int i = 0; i < sizeof(unit->status.labors)/sizeof(bool); i++)
@ -269,6 +315,106 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
item->set_experience(skill->experience);
}
}
if (unit->curse.add_tags1.whole ||
unit->curse.add_tags2.whole ||
unit->curse.rem_tags1.whole ||
unit->curse.rem_tags2.whole ||
unit->curse.name_visible)
{
auto curse = info->mutable_curse();
curse->set_add_tags1(unit->curse.add_tags1.whole);
curse->set_rem_tags1(unit->curse.rem_tags1.whole);
curse->set_add_tags2(unit->curse.add_tags2.whole);
curse->set_rem_tags2(unit->curse.rem_tags2.whole);
if (unit->curse.name_visible)
describeNameTriple(curse->mutable_name(), unit->curse.name,
unit->curse.name_plural, unit->curse.name_adjective);
}
for (size_t i = 0; i < unit->burrows.size(); i++)
info->add_burrows(unit->burrows[i]);
}
static command_result GetVersion(color_ostream &stream,
const EmptyMessage *, StringMessage *out)
{
out->set_value(DFHACK_VERSION);
return CR_OK;
}
static command_result GetDFVersion(color_ostream &stream,
const EmptyMessage *, StringMessage *out)
{
out->set_value(Core::getInstance().vinfo->getVersion());
return CR_OK;
}
static command_result GetWorldInfo(color_ostream &stream,
const EmptyMessage *, GetWorldInfoOut *out)
{
using df::global::ui;
using df::global::ui_advmode;
using df::global::world;
if (!ui || !world || !Core::getInstance().isWorldLoaded())
return CR_NOT_FOUND;
t_gamemodes mode;
if (!Core::getInstance().getWorld()->ReadGameMode(mode))
mode.g_type = GAMETYPE_DWARF_MAIN;
out->set_save_dir(world->cur_savegame.save_dir);
if (world->world_data->name.has_name)
describeName(out->mutable_world_name(), &world->world_data->name);
switch (mode.g_type)
{
case GAMETYPE_DWARF_MAIN:
case GAMETYPE_DWARF_RECLAIM:
out->set_mode(GetWorldInfoOut::MODE_DWARF);
out->set_civ_id(ui->civ_id);
out->set_site_id(ui->site_id);
out->set_group_id(ui->group_id);
out->set_race_id(ui->race_id);
break;
case GAMETYPE_ADVENTURE_MAIN:
out->set_mode(GetWorldInfoOut::MODE_ADVENTURE);
if (auto unit = vector_get(world->units.other[0], 0))
out->set_player_unit_id(unit->id);
if (!ui_advmode)
break;
if (auto nemesis = vector_get(world->nemesis.all, ui_advmode->player_id))
{
if (nemesis->figure)
out->set_player_histfig_id(nemesis->figure->id);
for (size_t i = 0; i < nemesis->companions.size(); i++)
{
auto unm = df::nemesis_record::find(nemesis->companions[i]);
if (!unm || !unm->figure)
continue;
out->add_companion_histfig_ids(unm->figure->id);
}
}
break;
case GAMETYPE_VIEW_LEGENDS:
out->set_mode(GetWorldInfoOut::MODE_LEGENDS);
break;
default:
return CR_NOT_FOUND;
}
return CR_OK;
}
static command_result ListEnums(color_ostream &stream,
@ -276,13 +422,24 @@ static command_result ListEnums(color_ostream &stream,
{
#define ENUM(name) describe_enum<df::name>(out->mutable_##name());
#define BITFIELD(name) describe_bitfield<df::name>(out->mutable_##name());
ENUM(material_flags);
ENUM(inorganic_flags);
BITFIELD(unit_flags1);
BITFIELD(unit_flags2);
BITFIELD(unit_flags3);
ENUM(unit_labor);
ENUM(job_skill);
BITFIELD(cie_add_tag_mask1);
BITFIELD(cie_add_tag_mask2);
describe_bitfield<df::death_info::T_flags>(out->mutable_death_info_flags());
ENUM(profession);
#undef ENUM
#undef BITFIELD
}
@ -297,8 +454,6 @@ static void listMaterial(ListMaterialsOut *out, int type, int index, const Basic
static command_result ListMaterials(color_ostream &stream,
const ListMaterialsIn *in, ListMaterialsOut *out)
{
CoreSuspender suspend;
auto mask = in->has_mask() ? &in->mask() : NULL;
for (int i = 0; i < in->id_list_size(); i++)
@ -350,8 +505,6 @@ static command_result ListMaterials(color_ostream &stream,
static command_result ListUnits(color_ostream &stream,
const ListUnitsIn *in, ListUnitsOut *out)
{
CoreSuspender suspend;
auto mask = in->has_mask() ? &in->mask() : NULL;
if (in->id_list_size() > 0)
@ -363,7 +516,8 @@ static command_result ListUnits(color_ostream &stream,
describeUnit(out->add_value(), unit, mask);
}
}
else
if (in->scan_all())
{
auto &vec = df::unit::get_vector();
@ -373,7 +527,13 @@ static command_result ListUnits(color_ostream &stream,
if (in->has_race() && unit->race != in->race())
continue;
if (in->civ_id() && unit->civ_id != in->civ_id())
if (in->has_civ_id() && unit->civ_id != in->civ_id())
continue;
if (in->has_dead() && Units::isDead(unit) != in->dead())
continue;
if (in->has_alive() && Units::isAlive(unit) != in->alive())
continue;
if (in->has_sane() && Units::isSane(unit) != in->sane())
continue;
describeUnit(out->add_value(), unit, mask);
@ -415,14 +575,21 @@ 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);
addMethod("BindMethod", &CoreService::BindMethod, SF_DONT_SUSPEND);
addMethod("RunCommand", &CoreService::RunCommand, SF_DONT_SUSPEND);
// Add others here:
addMethod("CoreSuspend", &CoreService::CoreSuspend);
addMethod("CoreResume", &CoreService::CoreResume);
addMethod("CoreSuspend", &CoreService::CoreSuspend, SF_DONT_SUSPEND);
addMethod("CoreResume", &CoreService::CoreResume, SF_DONT_SUSPEND);
// Functions:
addFunction("GetVersion", GetVersion, SF_DONT_SUSPEND);
addFunction("GetDFVersion", GetDFVersion, SF_DONT_SUSPEND);
addFunction("GetWorldInfo", GetWorldInfo);
addFunction("ListEnums", ListEnums, SF_CALLED_ONCE | SF_DONT_SUSPEND);
addFunction("ListEnums", ListEnums, SF_CALLED_ONCE);
addFunction("ListMaterials", ListMaterials, SF_CALLED_ONCE);
addFunction("ListUnits", ListUnits);
addFunction("ListSquads", ListSquads);

@ -43,7 +43,10 @@ namespace DFHack
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
SF_CALLED_ONCE = 1,
// Don't automatically suspend the core around the call.
// The function is supposed to manage locking itself.
SF_DONT_SUSPEND = 2
};
class DFHACK_EXPORT ServerFunctionBase : public RPCFunctionBase {
@ -225,6 +228,7 @@ namespace DFHack
tthread::thread *thread;
static void threadFn(void *);
void threadFn();
public:
ServerConnection(CActiveSocket *socket);

@ -116,6 +116,11 @@ namespace DFHack
DFHACK_EXPORT void describeName(NameInfo *info, df::language_name *name);
using dfproto::NameTriple;
DFHACK_EXPORT void describeNameTriple(NameTriple *info, const std::string &name,
const std::string &plural, const std::string &adj);
using dfproto::BasicUnitInfo;
using dfproto::BasicUnitInfoMask;

@ -194,6 +194,10 @@ 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);
DFHACK_EXPORT bool isDead(df::unit *unit);
DFHACK_EXPORT bool isAlive(df::unit *unit);
DFHACK_EXPORT bool isSane(df::unit *unit);
}
}
#endif

@ -54,6 +54,7 @@ using namespace std;
#include "df/assumed_identity.h"
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::ui;
@ -551,3 +552,39 @@ df::language_name *Units::GetVisibleName(df::unit *unit)
return &unit->name;
}
bool DFHack::Units::isDead(df::unit *unit)
{
return unit->flags1.bits.dead;
}
bool DFHack::Units::isAlive(df::unit *unit)
{
return !unit->flags1.bits.dead &&
!unit->flags3.bits.ghostly &&
!unit->curse.add_tags1.bits.NOT_LIVING;
}
bool DFHack::Units::isSane(df::unit *unit)
{
if (unit->flags1.bits.dead ||
unit->flags3.bits.ghostly ||
unit->curse.add_tags1.bits.OPPOSED_TO_LIFE ||
unit->curse.add_tags1.bits.CRAZED)
return false;
if (unit->flags1.bits.has_mood)
{
switch (unit->mood)
{
case mood_type::Melancholy:
case mood_type::Raving:
case mood_type::Berserk:
return false;
default:
break;
}
}
return true;
}

@ -5,6 +5,8 @@ option optimize_for = LITE_RUNTIME;
message EnumItemName {
required int32 value = 1;
optional string name = 2;
// For bitfield members
optional int32 bit_size = 3 [default = 1];
};
@ -16,10 +18,15 @@ message BasicMaterialId {
message BasicMaterialInfo {
required int32 type = 1;
required sint32 index = 2;
// The raw token
required string token = 3;
repeated int32 flags = 4; // of material_flags
// IF mask.flags:
// List of material_flags indices
repeated int32 flags = 4;
// Material type/index expanded:
optional int32 subtype = 5 [default = -1];
optional int32 creature_id = 6 [default = -1];
optional int32 plant_id = 7 [default = -1];
@ -27,10 +34,13 @@ message BasicMaterialInfo {
optional string name_prefix = 9 [default = ""];
// IF mask.states: in listed order;
// ELSE: one state matching mask.temperature
repeated fixed32 state_color = 10;
repeated string state_name = 11;
repeated string state_adj = 12;
// IF mask.reaction:
message Product {
required string id = 1;
required int32 type = 2;
@ -39,6 +49,7 @@ message BasicMaterialInfo {
repeated string reaction_class = 13;
repeated Product reaction_product = 14;
// IF mask.flags:
repeated int32 inorganic_flags = 15;
};
@ -68,6 +79,21 @@ message NameInfo {
optional string english_name = 5;
};
message NameTriple {
required string normal = 1;
optional string plural = 2;
optional string adjective = 3;
};
message UnitCurseInfo {
required fixed32 add_tags1 = 1;
required fixed32 rem_tags1 = 2;
required fixed32 add_tags2 = 3;
required fixed32 rem_tags2 = 4;
optional NameTriple name = 5;
};
message SkillInfo {
required int32 id = 1;
required int32 level = 2;
@ -77,6 +103,10 @@ message SkillInfo {
message BasicUnitInfo {
required int32 unit_id = 1;
required int32 pos_x = 13;
required int32 pos_y = 14;
required int32 pos_z = 15;
optional NameInfo name = 2;
required fixed32 flags1 = 3;
@ -90,25 +120,41 @@ message BasicUnitInfo {
optional int32 civ_id = 9 [default = -1];
optional int32 histfig_id = 10 [default = -1];
optional int32 death_id = 17 [default = -1];
optional uint32 death_flags = 18;
// IF mask.profession:
optional int32 squad_id = 19 [default = -1];
optional int32 squad_position = 20 [default = -1];
optional int32 profession = 22 [default = -1];
optional string custom_profession = 23;
// IF mask.labors:
repeated int32 labors = 11;
// IF mask.skills:
repeated SkillInfo skills = 12;
required int32 pos_x = 13;
required int32 pos_y = 14;
required int32 pos_z = 15;
optional UnitCurseInfo curse = 16;
repeated int32 burrows = 21;
};
message BasicUnitInfoMask {
optional bool labors = 1 [default = false];
optional bool skills = 2 [default = false];
optional bool profession = 3 [default = false];
};
message BasicSquadInfo {
required int32 squad_id = 1;
optional NameInfo name = 2;
// A special field completely overriding the name:
optional string alias = 3;
// Member histfig ids:
repeated sint32 members = 4;
};

@ -4,6 +4,29 @@ option optimize_for = LITE_RUNTIME;
import "Basic.proto";
message GetWorldInfoOut {
enum Mode {
MODE_DWARF = 1;
MODE_ADVENTURE = 2;
MODE_LEGENDS = 3;
};
required Mode mode = 1;
required string save_dir = 2;
optional NameInfo world_name = 3;
// Dwarf mode
optional int32 civ_id = 4;
optional int32 site_id = 5;
optional int32 group_id = 6;
optional int32 race_id = 7;
// Adventure mode
optional int32 player_unit_id = 8;
optional int32 player_histfig_id = 9;
repeated int32 companion_histfig_ids = 10;
};
message ListEnumsOut {
repeated EnumItemName material_flags = 1;
repeated EnumItemName inorganic_flags = 2;
@ -14,11 +37,22 @@ message ListEnumsOut {
repeated EnumItemName unit_labor = 6;
repeated EnumItemName job_skill = 7;
repeated EnumItemName cie_add_tag_mask1 = 8;
repeated EnumItemName cie_add_tag_mask2 = 9;
repeated EnumItemName death_info_flags = 10;
repeated EnumItemName profession = 11;
};
message ListMaterialsIn {
optional BasicMaterialInfoMask mask = 1;
// Specific materials:
repeated BasicMaterialId id_list = 2;
// Complete list by type:
optional bool builtin = 3;
optional bool inorganic = 4;
optional bool creatures = 5;
@ -30,10 +64,18 @@ message ListMaterialsOut {
message ListUnitsIn {
optional BasicUnitInfoMask mask = 1;
// Specific units:
repeated int32 id_list = 2;
// All units matching:
optional bool scan_all = 5;
optional int32 race = 3;
optional int32 civ_id = 4;
optional bool dead = 6; // i.e. passive corpse
optional bool alive = 7; // i.e. not dead or undead
optional bool sane = 8; // not dead, ghost, zombie, or insane
};
message ListUnitsOut {
repeated BasicUnitInfo value = 1;

@ -124,8 +124,6 @@ void setUnitNickname(df::unit *unit, const std::string &nick)
static command_result RenameSquad(color_ostream &stream, const RenameSquadIn *in)
{
CoreSuspender suspend;
df::squad *squad = df::squad::find(in->squad_id());
if (!squad)
return CR_NOT_FOUND;
@ -140,8 +138,6 @@ static command_result RenameSquad(color_ostream &stream, const RenameSquadIn *in
static command_result RenameUnit(color_ostream &stream, const RenameUnitIn *in)
{
CoreSuspender suspend;
df::unit *unit = df::unit::find(in->unit_id());
if (!unit)
return CR_NOT_FOUND;