A number of interface tweaks in RemoteClient.

- Associate a default output stream with the whole connection.
  If not explicitly specified in the constructor, uses stdout.
- Add methods that use this default stream to RemoteFunction.
- Add easily usable wrappers for CoreSuspend and CoreResume.
develop
Alexander Gavrilov 2012-03-16 14:11:46 +04:00
parent aa7f13266b
commit 976fa18d72
6 changed files with 136 additions and 29 deletions

@ -187,7 +187,7 @@ void fHKthread(void * iodata)
args.erase(args.begin());
command_result cr = plug_mgr->InvokeCommand(out, first, args);
if(cr == CR_WOULD_BREAK)
if(cr == CR_NEEDS_CONSOLE)
{
out.printerr("It isn't possible to run an interactive command outside the console.\n");
}

@ -291,7 +291,7 @@ command_result Plugin::invoke(color_ostream &out, const std::string & command, s
{
// running interactive things from some other source than the console would break it
if(!out.is_console() && cmd.interactive)
cr = CR_WOULD_BREAK;
cr = CR_NEEDS_CONSOLE;
else if (cmd.guard)
{
// Execute hotkey commands in a way where they can

@ -87,16 +87,29 @@ void color_ostream_proxy::decode(CoreTextNotification *data)
}
}
RemoteClient::RemoteClient()
RemoteClient::RemoteClient(color_ostream *default_output)
: p_default_output(default_output)
{
active = false;
socket = new CActiveSocket();
suspend_ready = false;
if (!p_default_output)
{
delete_output = true;
p_default_output = new color_ostream_wrapper(std::cout);
}
else
delete_output = false;
}
RemoteClient::~RemoteClient()
{
disconnect();
delete socket;
if (delete_output)
delete p_default_output;
}
bool DFHack::readFullBuffer(CSimpleSocket *socket, void *buf, int size)
@ -138,13 +151,13 @@ bool RemoteClient::connect(int port)
if (!socket->Initialize())
{
std::cerr << "Socket init failed." << endl;
default_output().printerr("Socket init failed.\n");
return false;
}
if (!socket->Open((const uint8 *)"localhost", port))
{
std::cerr << "Could not connect to localhost:" << port << endl;
default_output().printerr("Could not connect to localhost: %d\n", port);
return false;
}
@ -156,14 +169,14 @@ bool RemoteClient::connect(int port)
if (socket->Send((uint8*)&header, sizeof(header)) != sizeof(header))
{
std::cerr << "Could not send header." << endl;
default_output().printerr("Could not send handshake header.\n");
socket->Close();
return active = false;
}
if (!readFullBuffer(socket, &header, sizeof(header)))
{
std::cerr << "Could not read header." << endl;
default_output().printerr("Could not read handshake header.\n");
socket->Close();
return active = false;
}
@ -171,7 +184,7 @@ bool RemoteClient::connect(int port)
if (memcmp(header.magic, RPCHandshakeHeader::RESPONSE_MAGIC, sizeof(header.magic)) ||
header.version != 1)
{
std::cerr << "Invalid handshake response." << endl;
default_output().printerr("Invalid handshake response.\n");
socket->Close();
return active = false;
}
@ -195,7 +208,7 @@ void RemoteClient::disconnect()
header.id = RPC_REQUEST_QUIT;
header.size = 0;
if (socket->Send((uint8_t*)&header, sizeof(header)) != sizeof(header))
std::cerr << "Could not send the disconnect message." << endl;
default_output().printerr("Could not send the disconnect message.\n");
}
socket->Close();
@ -222,7 +235,6 @@ bool RemoteClient::bind(color_ostream &out, RemoteFunctionBase *function,
if (bind_call(out) != CR_OK)
return false;
function->p_client = this;
function->id = bind_call.out()->assigned_id();
return true;
@ -246,6 +258,35 @@ command_result RemoteClient::run_command(color_ostream &out, const std::string &
return runcmd_call(out);
}
int RemoteClient::suspend_game()
{
if (!active)
return -1;
if (!suspend_ready) {
suspend_ready = true;
suspend_call.bind(this, "CoreSuspend");
resume_call.bind(this, "CoreResume");
}
if (suspend_call(default_output()) == CR_OK)
return suspend_call.out()->value();
else
return -1;
}
int RemoteClient::resume_game()
{
if (!suspend_ready)
return -1;
if (resume_call(default_output()) == CR_OK)
return resume_call.out()->value();
else
return -1;
}
void RPCFunctionBase::reset(bool free)
{
if (free)
@ -266,11 +307,11 @@ void RPCFunctionBase::reset(bool free)
bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client,
const std::string &name, const std::string &proto)
{
if (p_client == client)
return true;
if (p_client)
if (isValid())
{
if (p_client == client && this->name == name && this->proto == proto)
return true;
out.printerr("Function already bound to %s::%s\n",
this->proto.c_str(), this->name.c_str());
return false;
@ -278,6 +319,7 @@ bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client,
this->name = name;
this->proto = proto;
this->p_client = client;
return client->bind(out, this, name, proto);
}
@ -305,9 +347,10 @@ bool DFHack::sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageL
command_result RemoteFunctionBase::execute(color_ostream &out,
const message_type *input, message_type *output)
{
if (!p_client)
if (!isValid())
{
out.printerr("Calling an unbound RPC function.\n");
out.printerr("Calling an unbound RPC function %s::%s.\n",
this->proto.c_str(), this->name.c_str());
return CR_NOT_IMPLEMENTED;
}
@ -315,14 +358,14 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
{
out.printerr("In call to %s::%s: invalid socket.\n",
this->proto.c_str(), this->name.c_str());
return CR_FAILURE;
return CR_LINK_FAILURE;
}
if (!sendRemoteMessage(p_client->socket, id, input))
{
out.printerr("In call to %s::%s: I/O error in send.\n",
this->proto.c_str(), this->name.c_str());
return CR_FAILURE;
return CR_LINK_FAILURE;
}
color_ostream_proxy text_decoder(out);
@ -337,7 +380,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
{
out.printerr("In call to %s::%s: I/O error in receive header.\n",
this->proto.c_str(), this->name.c_str());
return CR_FAILURE;
return CR_LINK_FAILURE;
}
//out.print("Received %d:%d\n", header.id, header.size);
@ -349,7 +392,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
{
out.printerr("In call to %s::%s: invalid received size %d.\n",
this->proto.c_str(), this->name.c_str(), header.size);
return CR_FAILURE;
return CR_LINK_FAILURE;
}
std::auto_ptr<uint8_t> buf(new uint8_t[header.size]);
@ -358,7 +401,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
{
out.printerr("In call to %s::%s: I/O error in receive %d bytes of data.\n",
this->proto.c_str(), this->name.c_str(), header.size);
return CR_FAILURE;
return CR_LINK_FAILURE;
}
switch (header.id) {
@ -367,7 +410,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
{
out.printerr("In call to %s::%s: error parsing received result.\n",
this->proto.c_str(), this->name.c_str());
return CR_FAILURE;
return CR_LINK_FAILURE;
}
return CR_OK;

@ -68,7 +68,7 @@ int main (int argc, char *argv[])
}
// Connect to DFHack
RemoteClient client;
RemoteClient client(&out);
if (!client.connect())
return 2;
@ -77,7 +77,7 @@ int main (int argc, char *argv[])
for (int i = 2; i < argc; i++)
args.push_back(argv[i]);
command_result rv = client.run_command(out, argv[1], args);
command_result rv = client.run_command(argv[1], args);
if (rv != CR_OK) {
if (rv == CR_NOT_IMPLEMENTED)

@ -41,7 +41,8 @@ namespace DFHack
enum command_result
{
CR_WOULD_BREAK = -2, // Attempt to call interactive command without console
CR_LINK_FAILURE = -3, // RPC call failed due to I/O or protocol error
CR_NEEDS_CONSOLE = -2, // Attempt to call interactive command without console
CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded
CR_OK = 0, // Success
CR_FAILURE = 1, // Failure
@ -146,10 +147,13 @@ namespace DFHack
class DFHACK_EXPORT RemoteFunctionBase : public RPCFunctionBase {
public:
bool bind(RemoteClient *client, const std::string &name,
const std::string &proto = std::string());
bool bind(color_ostream &out,
RemoteClient *client, const std::string &name,
const std::string &proto = std::string());
bool isValid() { return (p_client != NULL); }
bool isValid() { return (id >= 0); }
protected:
friend class RemoteClient;
@ -158,6 +162,7 @@ namespace DFHack
: RPCFunctionBase(in, out), p_client(NULL), id(-1)
{}
inline color_ostream &default_ostream();
command_result execute(color_ostream &out, const message_type *input, message_type *output);
std::string name, proto;
@ -175,9 +180,17 @@ namespace DFHack
RemoteFunction() : RemoteFunctionBase(&In::default_instance(), &Out::default_instance()) {}
command_result operator() () {
return p_client ? RemoteFunctionBase::execute(default_ostream(), in(), out())
: CR_NOT_IMPLEMENTED;
}
command_result operator() (color_ostream &stream) {
return RemoteFunctionBase::execute(stream, in(), out());
}
command_result operator() (const In *input, Out *output) {
return p_client ? RemoteFunctionBase::execute(default_ostream(), input, output)
: CR_NOT_IMPLEMENTED;
}
command_result operator() (color_ostream &stream, const In *input, Out *output) {
return RemoteFunctionBase::execute(stream, input, output);
}
@ -191,9 +204,17 @@ namespace DFHack
RemoteFunction() : RemoteFunctionBase(&In::default_instance(), &EmptyMessage::default_instance()) {}
command_result operator() () {
return p_client ? RemoteFunctionBase::execute(default_ostream(), in(), out())
: CR_NOT_IMPLEMENTED;
}
command_result operator() (color_ostream &stream) {
return RemoteFunctionBase::execute(stream, in(), out());
}
command_result operator() (const In *input) {
return p_client ? RemoteFunctionBase::execute(default_ostream(), input, out())
: CR_NOT_IMPLEMENTED;
}
command_result operator() (color_ostream &stream, const In *input) {
return RemoteFunctionBase::execute(stream, input, out());
}
@ -211,22 +232,56 @@ namespace DFHack
const std::string &name, const std::string &proto);
public:
RemoteClient();
RemoteClient(color_ostream *default_output = NULL);
~RemoteClient();
static int GetDefaultPort();
color_ostream &default_output() { return *p_default_output; };
bool connect(int port = -1);
void disconnect();
command_result run_command(const std::string &cmd, const std::vector<std::string> &args) {
return run_command(default_output(), cmd, args);
}
command_result run_command(color_ostream &out, const std::string &cmd,
const std::vector<std::string> &args);
// For executing multiple calls in rapid succession.
// Best used via RemoteSuspender.
int suspend_game();
int resume_game();
private:
bool active;
CActiveSocket * socket;
bool active, delete_output;
CActiveSocket *socket;
color_ostream *p_default_output;
RemoteFunction<dfproto::CoreBindRequest,dfproto::CoreBindReply> bind_call;
RemoteFunction<dfproto::CoreRunCommandRequest> runcmd_call;
bool suspend_ready;
RemoteFunction<EmptyMessage, IntMessage> suspend_call, resume_call;
};
inline color_ostream &RemoteFunctionBase::default_ostream() {
return p_client->default_output();
}
inline bool RemoteFunctionBase::bind(RemoteClient *client, const std::string &name,
const std::string &proto) {
return bind(client->default_output(), client, name, proto);
}
class RemoteSuspender {
RemoteClient *client;
public:
RemoteSuspender(RemoteClient *client) : client(client) {
if (!client || client->suspend_game() <= 0) client = NULL;
}
~RemoteSuspender() {
if (client) client->resume_game();
}
};
}

@ -32,6 +32,7 @@ message CoreTextNotification {
message CoreErrorNotification {
enum ErrorCode {
CR_LINK_FAILURE = -3;
CR_WOULD_BREAK = -2;
CR_NOT_IMPLEMENTED = -1;
CR_OK = 0;
@ -49,10 +50,18 @@ message IntMessage {
required int32 value = 1;
}
message IntListMessage {
repeated int32 value = 1;
}
message StringMessage {
required string value = 1;
}
message StringListMessage {
repeated string value = 1;
}
message CoreBindRequest {
required string method = 1;
required string input_msg = 2;