Go client for DFHack RPC server. Communication happens with DFHack's binary TCP protocol (handshake, fixed frames, bind/call/quit)
client/the RPC client (Dial,Bind,Call,Close). Text delivered viaOnTextis automatically converted from DFHack's CP437 codepage to UTF-8, so non-ASCII names and glyphs render correctly.gen/proto/generated Go bindings for DFHack's core protobuf messages (CoreProtocol.proto,Basic.proto,BasicApi.proto), regenerated from upstream DFHack.protofiles viamake proto(seescripts/proto-entrypoint.sh).cmd/a minimal CLI example that runs a single DFHack console command.
go get github.com/salimnassim/dfhackConnect and run a DFHack console command
(CoreRunCommandRequest/RunCommandID are always available, no Bind needed):
package main
import (
"context"
"fmt"
"log/slog"
"github.com/salimnassim/dfhack/client"
pb "github.com/salimnassim/dfhack/gen/proto"
)
func main() {
ctx := context.Background()
c, err := client.Dial(ctx, client.DefaultAddr)
if err != nil {
slog.Error("dial failed", "error", err)
return
}
defer c.Close()
// Streamed text output (e.g. command output) arrives via OnText.
c.OnText = func(n *pb.CoreTextNotification) {
for _, fragment := range n.GetFragments() {
fmt.Print(fragment.GetText())
}
}
cmd := &pb.CoreRunCommandRequest{Command: new("ls")}
if err := c.Call(ctx, client.RunCommandID, cmd, &pb.EmptyMessage{}); err != nil {
slog.Error("command failed", "error", err)
}
}A runnable version of this is in cmd/command.go:
go run ./cmd -addr 127.0.0.1:5000 -command lsEvery Call and Bind takes a context.Context; cancelling it or passing its
deadline aborts the in-flight request. After a transport or framing error
(including a cancelled request) the connection's stream position is unknown, so
all later calls on that Client fail with the same error — Dial a new one.
A server-side failure (*client.RPCError) does not break the connection.
Dwarf Fortress encodes in-game text (item names, creature names, etc.) using
the DOS CP437 codepage rather than UTF-8. The client transparently decodes
CoreTextFragment text from CP437 to UTF-8 before it reaches OnText, so
accented and special characters (e.g. é) print correctly instead of showing
up as � or ?.
$ go run cmd/command.go --command instruments
shigós (handheld, WOODCRAFT/assemble)
make shigós keyboard (GLASSMAKER: sand, sand bag)
make shigós body (WOODCRAFT: wood)
make shigós bellows (LEATHERWORK: leather)
zasgim (handheld, STONECRAFT/assemble)
make zasgim yoke (BONECARVE: bone)
make zasgim sound-chest (STONECRAFT: stone)
make zasgim strings (WEAVING: silk thread)
make zasgim bow (BONECARVE: bone)
eststek (handheld, STONECRAFT: stone)
gethust (handheld, POTTERY/assemble)
make gethust blowpipe (WOODCRAFT: wood)
make gethust wind chest (POTTERY: clay)
make gethust pipe (POTTERY: clay)
ubur (handheld, GLASSMAKER: sand, sand bag)
kikës (building, METALCRAFT/assemble)
forge kikës triangles (METALCRAFT: metal bars)
make kikës stand (STONECRAFT: stone)
thortith (building, BONECARVE/assemble)
make thortith bowls (BONECARVE: bone)
forge thortith stand (METALCRAFT: metal bars)
make thortith stick (WOODCRAFT: wood)
unib (building, POTTERY: clay)
dakas (building, CARPENTRY: wood)
Plugin-provided methods must be bound to an ID before use with Client.Bind, then invoked with Client.Call using that ID:
id, err := c.Bind(ctx, "SomeMethod", "someplugin", &pb.SomeMethodIn{}, &pb.SomeMethodOut{})
if err != nil {
// handle error
}
out := &pb.SomeMethodOut{}
if err := c.Call(ctx, id, &pb.SomeMethodIn{ /* ... */ }, out); err != nil {
// handle error
}Generated code under gen/proto/ is produced from upstream DFHack .proto files:
make proto DFHACK_VERSION=<tag>See Makefile, buf.gen.yaml, and scripts/proto-entrypoint.sh for details.