2020-07-18 23:13:43 -06:00
|
|
|
.. _remote:
|
|
|
|
|
|
|
|
=======================
|
|
|
|
DFHack Remote Interface
|
|
|
|
=======================
|
|
|
|
|
|
|
|
DFHack provides a remote access interface that external tools can connect to and
|
|
|
|
use to interact with DF. This is implemented with `Google protobuf`_ messages
|
2020-07-21 19:39:37 -06:00
|
|
|
exchanged over a TCP socket. Both the core and plugins can define
|
|
|
|
remotely-accessible methods, or **RPC methods**. The RPC methods currently
|
2020-07-18 23:13:43 -06:00
|
|
|
available are not comprehensive, but can be extended with plugins.
|
|
|
|
|
|
|
|
.. _Google protobuf: https://developers.google.com/protocol-buffers
|
|
|
|
|
2020-07-27 22:21:23 -06:00
|
|
|
.. contents:: Contents
|
2020-07-18 23:13:43 -06:00
|
|
|
:local:
|
|
|
|
|
2020-07-21 19:39:37 -06:00
|
|
|
|
2020-07-21 21:14:59 -06:00
|
|
|
.. _remote-server-config:
|
|
|
|
|
2020-07-21 19:39:37 -06:00
|
|
|
Server configuration
|
|
|
|
====================
|
|
|
|
|
|
|
|
DFHack attempts to start a TCP server to listen for remote connections on
|
|
|
|
startup. If this fails (due to the port being in use, for example), an error
|
|
|
|
message will be logged to stderr.log.
|
|
|
|
|
|
|
|
The server can be configured by setting options in ``dfhack-config/remote-server.json``:
|
|
|
|
|
|
|
|
- ``allow_remote`` (default: ``false``): if true, allows connections from hosts
|
|
|
|
other than the local machine. This is insecure and may allow arbitrary code
|
|
|
|
execution on your machine, so it is disabled by default.
|
|
|
|
- ``port`` (default: ``5000``): the port that the remote server listens on.
|
|
|
|
Overriding this will allow the server to work if you have multiple instances
|
|
|
|
of DF running, or if you have something else running on port 5000. Note that
|
|
|
|
the ``DFHACK_PORT`` `environment variable <env-vars>` takes precedence over
|
|
|
|
this setting and may be more useful for overriding the port temporarily.
|
|
|
|
|
|
|
|
|
2020-07-21 23:34:51 -06:00
|
|
|
Developing with the remote API
|
|
|
|
==============================
|
|
|
|
|
|
|
|
At a high level, the core and plugins define RPC methods, and external clients
|
|
|
|
can call these methods. Each method is assigned an ID internally, which clients
|
|
|
|
use to call it. These method IDs can be obtained using the special ``BindMethod``
|
|
|
|
method, which has an ID of 0.
|
|
|
|
|
2020-07-18 23:13:43 -06:00
|
|
|
Examples
|
2020-07-21 23:34:51 -06:00
|
|
|
--------
|
2020-07-18 23:13:43 -06:00
|
|
|
|
|
|
|
The `dfhack-run` command uses the RPC interface to invoke DFHack commands
|
|
|
|
(or Lua functions) externally.
|
|
|
|
|
|
|
|
Plugins that implement RPC methods include:
|
|
|
|
|
|
|
|
- `rename`
|
|
|
|
- `remotefortressreader`
|
|
|
|
- `isoworldremote`
|
|
|
|
|
|
|
|
Plugins that use the RPC API include:
|
|
|
|
|
|
|
|
- `stonesense`
|
|
|
|
|
|
|
|
Third-party tools that use the RPC API include:
|
|
|
|
|
|
|
|
- `Armok Vision <https://github.com/RosaryMala/armok-vision>`_ (:forums:`Bay12 forums thread <146473>`)
|
|
|
|
|
|
|
|
Client libraries
|
2020-07-21 23:34:51 -06:00
|
|
|
----------------
|
2020-07-18 23:13:43 -06:00
|
|
|
|
|
|
|
Some external libraries are available for interacting with the remote interface
|
|
|
|
from other (non-C++) languages, including:
|
|
|
|
|
|
|
|
- `RemoteClientDF-Net <https://github.com/RosaryMala/RemoteClientDF-Net>`_ for C#
|
|
|
|
- `dfhackrpc <https://github.com/BenLubar/dfhackrpc>`_ for Go
|
|
|
|
- `dfhack-remote <https://github.com/alexchandel/dfhack-remote>`_ for JavaScript
|
2020-07-21 23:34:51 -06:00
|
|
|
|
|
|
|
|
|
|
|
Protocol description
|
|
|
|
====================
|
|
|
|
|
|
|
|
This is a low-level description of the RPC protocol, which may be useful when
|
|
|
|
developing custom clients.
|
|
|
|
|
2020-07-22 00:04:02 -06:00
|
|
|
A WireShark dissector for this protocol is available in the
|
|
|
|
`df_misc repo <https://github.com/DFHack/df_misc/blob/master/wireshark_dfhack_rpc.lua>`_.
|
|
|
|
|
|
|
|
|
2020-07-21 23:34:51 -06:00
|
|
|
Built-in messages
|
|
|
|
-----------------
|
|
|
|
These messages have hardcoded IDs; all others must be obtained through ``BindMethod``.
|
|
|
|
|
|
|
|
=== ============ =============================== =======================
|
|
|
|
ID Method Input Output
|
|
|
|
=== ============ =============================== =======================
|
|
|
|
0 BindMethod dfproto.CoreBindRequest dfproto.CoreBindReply
|
|
|
|
1 RunCommand dfproto.CoreRunCommandRequest dfproto.EmptyMessage
|
|
|
|
=== ============ =============================== =======================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Conversation flow
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
* Client → Server: `handshake request`_
|
|
|
|
* Server → Client: `handshake reply`_
|
|
|
|
* Repeated 0 or more times:
|
|
|
|
* Client → Server: `request`_
|
|
|
|
* Server → Client: `text`_ (0 or more times)
|
|
|
|
* Server → Client: `result`_ or `failure`_
|
|
|
|
* Client → Server: `quit`_
|
|
|
|
|
|
|
|
Raw message types
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
* All numbers are little-endian
|
|
|
|
* All strings are ASCII
|
|
|
|
* A payload size of greater than 64MiB is an error
|
|
|
|
* See ``RemoteClient.h`` for definitions of constants starting with ``RPC``
|
|
|
|
|
|
|
|
handshake request
|
|
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
.. csv-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
|
|
|
|
Type, Name, Value
|
|
|
|
char[8], magic, ``DFHack?\n``
|
|
|
|
int32_t, version, 1
|
|
|
|
|
|
|
|
handshake reply
|
|
|
|
~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
.. csv-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
|
|
|
|
Type, Name, Value
|
|
|
|
char[8], magic, ``DFHack!\n``
|
|
|
|
int32_t, version, 1
|
|
|
|
|
|
|
|
header
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
**Note:** the two fields of this message are sometimes repurposed. Uses of this
|
|
|
|
message are represented as ``header(x, y)``, where ``x`` corresponds to the ``id``
|
|
|
|
field and ``y`` corresponds to ``size``.
|
|
|
|
|
|
|
|
.. csv-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
|
|
|
|
Type, Name
|
|
|
|
int16_t, id
|
|
|
|
int16_t, (padding - unused)
|
|
|
|
int32_t, size
|
|
|
|
|
|
|
|
request
|
|
|
|
~~~~~~~
|
|
|
|
|
|
|
|
.. list-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
:widths: 25 75
|
|
|
|
|
|
|
|
* - Type
|
|
|
|
- Description
|
|
|
|
* - `header`_
|
|
|
|
- ``header(id, size)``
|
|
|
|
* - buffer
|
|
|
|
- Protobuf-encoded payload of the input message type of the method specified by ``id``; length of ``size`` bytes
|
|
|
|
|
|
|
|
text
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
.. list-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
:widths: 25 75
|
|
|
|
|
|
|
|
* - Type
|
|
|
|
- Description
|
|
|
|
* - `header`_
|
|
|
|
- ``header(RPC_REPLY_TEXT, size)``
|
|
|
|
* - buffer
|
|
|
|
- Protobuf-encoded payload of type ``dfproto.CoreTextNotification``; length of ``size`` bytes
|
|
|
|
|
|
|
|
result
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
.. list-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
:widths: 25 75
|
|
|
|
|
|
|
|
* - Type
|
|
|
|
- Description
|
|
|
|
* - `header`_
|
|
|
|
- ``header(RPC_REPLY_RESULT, size)``
|
|
|
|
* - buffer
|
|
|
|
- Protobuf-encoded payload of the output message type of the oldest incomplete method call; when received,
|
|
|
|
that method call is considered completed. Length of ``size`` bytes.
|
|
|
|
|
|
|
|
failure
|
|
|
|
~~~~~~~
|
|
|
|
|
|
|
|
.. list-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
:widths: 25 75
|
|
|
|
|
|
|
|
* - Type
|
|
|
|
- Description
|
|
|
|
* - `header`_
|
|
|
|
- ``header(RPC_REPLY_FAIL, command_result)``
|
|
|
|
* - command_result
|
|
|
|
- return code of the command (a constant starting with ``CR_``; see ``RemoteClient.h``)
|
|
|
|
|
|
|
|
quit
|
|
|
|
~~~~
|
|
|
|
|
2020-07-22 00:04:02 -06:00
|
|
|
**Note:** the server closes the connection after receiving this message.
|
2020-07-21 23:34:51 -06:00
|
|
|
|
|
|
|
.. list-table::
|
|
|
|
:align: left
|
|
|
|
:header-rows: 1
|
|
|
|
:widths: 25 75
|
|
|
|
|
|
|
|
* - Type
|
|
|
|
- Description
|
|
|
|
* - `header`_
|
|
|
|
- ``header(RPC_REQUEST_QUIT, 0)``
|