Hex resource lookup

POST /roads/region/<id>/resources returns allowlisted harvestable nodes on a list of small-hex tiles in one region. It is a point lookup, not a region dump: send the hexes your planner already has (typically 1–16 384), get back sparse ResourceNodes. Schema: roads_cache.proto.

HTTP semantics

Protobuf on both sides. Send Content-Type: application/x-protobuf and Accept: application/x-protobuf. There is no JSON variant.

StatusWhenBody
200 OK Region ready; query accepted ResourcesResponse protobuf. Empty tiles simply contribute no nodes.
202 Accepted Region exists but roads grid still seeding Empty. Poll /roads/health until state=READY (3), then retry.
400 Bad Request Body is not a ResourceQuery, or tiles.length > 16384 Plain text (invalid ResourceQuery protobuf or too many tiles (N > 16384)). The cap is on the request list before de-duplication.
404 Not Found Unknown region id Plain text: unknown region N
503 Service Unavailable Roads cache disabled on host Plain text: roads cache not enabled

Protobuf messages

From roads_cache.proto (package roads_cache):

message Hex {
  int32 x = 1;
  int32 z = 2;
}

message ResourceQuery {
  repeated Hex tiles = 1;          // max 16384
}

message ResourceNode {
  int32 x = 1;
  int32 z = 2;
  int32 resource_id = 3;           // resource_desc.id
}

message ResourcesResponse {
  repeated ResourceNode nodes = 1; // sparse; omitted hexes had no match
}

What is indexed

The relay joins resource_state to overworld (dimension = 1) location_state at ingest and keeps only an allowlist of resource_ids vendored from BitCraft Public/resource.json tags:

Included tagsNot included
Tree, Sapling, Wood Logs, Stump, Ore Vein, Rock, Rock Boulder, Rock Outcrop, Clay, Sand (215 ids) Fiber plants, flowers, grain, fish, salt, mushrooms, berries, hexite (use GET /deposits), obstacles, footprints of a node on neighboring hexes

Position is the entity’s location_state tile only. resource_id matches resource_desc.id (and resource.json id). Resolve names/tiers from that catalog on the client; this API does not return names.

Example (Python)

Generate stubs from /proto/roads_cache.proto (protoc --python_out=.), then:

import requests
from roads_cache_pb2 import ResourceQuery, ResourcesResponse

REGION = 14
# World small-hex, same space as location_state / overlay.
# Region 14 origin is (23040, 15360); these two tiles sit on that origin.
query = ResourceQuery()
query.tiles.add(x=23040, z=15360)
query.tiles.add(x=23041, z=15360)

r = requests.post(
    f"https://relay.bitcraftsync.app/roads/region/{REGION}/resources",
    data=query.SerializeToString(),
    headers={
        "Content-Type": "application/x-protobuf",
        "Accept": "application/x-protobuf",
    },
    timeout=30,
)
r.raise_for_status()
if r.status_code == 202:
    raise RuntimeError("region still seeding; retry after /roads/health READY")

resp = ResourcesResponse()
resp.ParseFromString(r.content)
for n in resp.nodes:
    print(n.x, n.z, n.resource_id)

With a pre-encoded body file:

# query.pb is a serialized roads_cache.ResourceQuery
curl -sS -H 'Content-Type: application/x-protobuf' \
  -H 'Accept: application/x-protobuf' \
  --data-binary @query.pb \
  https://relay.bitcraftsync.app/roads/region/14/resources \
  -o resources.pb -w '%{http_code}\n'
# 200 → decode resources.pb as ResourcesResponse
# 202 → empty body, region not ready
# 400 → too many tiles or invalid protobuf

Client workflow

  1. Wait until GET /roads/health reports your region READY (same gate as the map snapshot).
  2. Collect the odd-r small-hex tiles on the planned path (world x, z). Chunk requests at 16 384.
  3. POST one ResourceQuery per chunk to /roads/region/<id>/resources.
  4. Join resource_id against your local resource.json / resource_desc catalog for names and tags.
Your planner │ planned tiles (world small-hex x, z) — max 16384 per POST ▼ POST /roads/region/14/resources → ResourceQuery { tiles: [{x,z}, …] } ← ResourcesResponse { nodes: [{x, z, resource_id}, …] } sparse: empty hexes omitted; several nodes may share a hex