# 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 exchanged over a TCP socket. Both the core and plugins can define remotely-accessible methods, or RPC methods. The RPC methods currently available are not comprehensive, but can be extended with plugins.

## 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 takes precedence over this setting and may be more useful for overriding the port temporarily.

## 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.

### Examples

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 (Bay12 forums thread)

### Client libraries

Some external libraries are available for interacting with the remote interface from other (non-C++) languages, including:

- RemoteClientDF-Net for C#

- dfhackrpc for Go

- dfhack-remote for JavaScript

- dfhack-client-qt for C++ with Qt

- dfhack-client-python for Python (adapted from “Blendwarf”)

- dfhack-client-java for Java

- dfhack-remote for Rust

## Protocol description

This is a low-level description of the RPC protocol, which may be useful when developing custom clients.

A WireShark dissector for this protocol is available in the df_misc repo.

### 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

Type | Name | Value
--- | --- | ---
char[8] | magic | `DFHack?\n`
int32_t | version | 1

#### handshake reply

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`.

Type | Name
--- | ---
int16_t | id
int16_t | (padding - unused)
int32_t | size

#### request

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

Type | Description
--- | ---
header | `header(RPC_REPLY_TEXT, size)`
buffer | Protobuf-encoded payload of type `dfproto.CoreTextNotification`; length of `size` bytes

#### result

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

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

Note: the server closes the connection after receiving this message.

Type | Description
--- | ---
header | `header(RPC_REQUEST_QUIT, 0)`