Structs Streaming (GRASS)
GRASS (Game Real-time Application Streaming Service) delivers real-time game events over NATS. Instead of polling queries repeatedly, subscribe to GRASS and react to events the moment they happen.
When to Use GRASS
| Situation | Use GRASS | Use Polling |
|---|
| Detect incoming raid | Yes — instant alert | Too slow |
| Wait for player creation after guild signup |
Yes — listen for
address_register | Polling every 10s works too |
| Monitor fleet arriving at your planet | Yes —
fleet_arrive event | Might miss it |
| Track struct health during combat | Yes —
planet_activity with
struct_health | Too slow |
| Check your own resource balance | No | Yes — one-off query |
| Read struct type stats | No | Yes — static data |
Rule of thumb: If you need to react to something, use GRASS. If you need to read something, use a query.
Finding Your GRASS Endpoint
The GRASS WebSocket URL is not hardcoded — it comes from the guild configuration.
- 1. Query the guild list: INLINECODE4
- Follow the guild's
endpoint URL to get its config - Look for INLINECODE6
Example (Orbital Hydro guild):
CODEBLOCK0
The grass_nats_websocket value is your NATS WebSocket endpoint. Not all guilds provide this service — check before relying on it.
A reliable reference endpoint: ws://crew.oh.energy:1443 (Orbital Hydro / Slow Ninja).
Discovery First
Before subscribing to specific subjects, subscribe to the > wildcard to see all traffic flowing through the GRASS server. This reveals the actual subject patterns in use, which may differ from documentation.
CODEBLOCK1
Watch the output for 30-60 seconds. You will see subjects like structs.planet.2-1, consensus, healthcheck, etc. Once you know what subjects carry the events you need, narrow your subscriptions to those specific subjects.
Important: Struct events (attacks, builds, status changes) often arrive on the planet subject rather than the struct subject. If you are not receiving expected struct events, subscribe to the struct's planet subject instead.
Subject Patterns
Subscribe to subjects matching the entities you care about:
| Entity | Wildcard | Specific | Example |
|---|
| Player | INLINECODE13 | INLINECODE14 | INLINECODE15 |
| Planet |
structs.planet.* |
structs.planet.{planet_id} |
structs.planet.2-1 |
| Guild |
structs.guild.* |
structs.guild.{guild_id} |
structs.guild.0-1 |
| Struct |
structs.struct.* |
structs.struct.{struct_id} |
structs.struct.5-1 |
| Fleet |
structs.fleet.* |
structs.fleet.{fleet_id} |
structs.fleet.9-1 |
| Address |
structs.address.register.* |
structs.address.register.{code} | -- |
| Inventory |
structs.inventory.> |
structs.inventory.{denom}.{guild_id}.{player_id}.{address} | Token movements |
| Grid |
structs.grid.* |
structs.grid.{object_id} | Attribute changes (ore, power, load, etc.) |
| Global |
structs.global |
structs.global | Block updates |
| Consensus |
consensus |
consensus | Chain consensus events |
| Healthcheck |
healthcheck |
healthcheck | Node health status |
Use wildcards (*) to discover what events exist. Narrow to specific subjects once you know what you need. Use > to see everything (see "Discovery First" above).
Event Types
Planet Events
| Event | Description | React By |
|---|
| INLINECODE42 | Raid started/completed on planet | Activate defenses, alert |
| INLINECODE43 |
Activity log including
struct_health changes | Track combat damage |
|
fleet_arrive | Fleet arrived at planet | Prepare defense or welcome |
|
fleet_depart | Fleet left planet | Update threat assessment |
Struct Events
Note: Struct events frequently arrive on the planet subject (structs.planet.{id}) rather than the struct subject. Subscribe to both if you need complete coverage.
| Event | Description | React By |
|---|
| INLINECODE48 | Struct was attacked | Counter-attack, repair |
| INLINECODE49 |
Struct status changed (online/offline/destroyed) | Rebuild, reallocate power |
|
struct_defense_add /
struct_defense_remove | Defense assignments changed | Update defense map |
|
struct_defender_clear | All defense relationships cleared | Re-assign defenders |
|
struct_block_build_start | Build operation initiated | Track in job list |
|
struct_block_ore_mine_start | Mine operation initiated | Track in job list |
|
struct_block_ore_refine_start | Refine operation initiated | Track in job list |
Player Events
| Event | Description | React By |
|---|
| INLINECODE56 | Player chain data updated | Update intel |
| INLINECODE57 |
Player metadata changed | Update intel |
Guild Events
| Event | Description | React By |
|---|
| INLINECODE58 | Guild chain data updated | Update guild status |
| INLINECODE59 |
Member joined/left guild | Update relationship map |
Inventory Events
Subject: INLINECODE60
Track token movements — Alpha Matter, guild tokens, ore, etc.
| Category | Description | React By |
|---|
| INLINECODE61 | Tokens sent from this player | Update balance tracking |
| INLINECODE62 |
Tokens received by this player | Update balance tracking |
|
seized | Tokens seized via raid | Trigger counter-raid or refine alert |
|
mined | Ore mined | Start refining immediately |
|
refined | Ore refined into Alpha | Update wealth tracking |
|
minted | Guild tokens minted | Track guild economy |
|
infused | Alpha infused into reactor/generator | Update capacity tracking |
|
forfeited | Tokens lost (penalties, etc.) | Investigate cause |
Grid Events
Subject: INLINECODE69
Track attribute changes on any game object (players, structs, planets).
| Category | Description | React By |
|---|
| INLINECODE70 | Power capacity changed | Check if approaching offline |
| INLINECODE71 |
Connection capacity changed | Update power routing |
|
connectionCount | Connection count changed | Update power routing |
|
fuel | Fuel level changed | Monitor generator/reactor |
|
lastAction | Last action timestamp updated | Track activity |
|
load | Power load changed | Check if approaching offline |
|
nonce | Player nonce incremented | Detect activity (useful for scouting) |
|
ore | Ore balance changed |
Refine immediately if yours; raid target if theirs |
|
player_consensus | Player consensus data updated | Update intel |
|
power | Power level changed | Monitor energy infrastructure |
|
proxyNonce | Proxy nonce changed | Detect proxy activity |
|
structsLoad | Structs load changed | Assess fleet strength changes |
Combat Event Payloads
INLINECODE82 events include detailed shot-by-shot resolution. Example payload (observed on planet subject):
CODEBLOCK2
Key fields in eventAttackShotDetail:
- -
evaded -- true if the shot missed (defense type interaction) - INLINECODE85 -- true if a defender intercepted
- INLINECODE86 -- which struct blocked (if any)
- INLINECODE87 /
counterAttackerStructId -- counter-attack info per shot
INLINECODE89 events track HP changes:
CODEBLOCK3
Noise Filtering
The consensus and healthcheck subjects fire constantly (every few seconds). When using the > wildcard for discovery, filter these out to see actual game events:
CODEBLOCK4
Global Events
| Event | Description | React By |
|---|
| INLINECODE93 | New block produced | Tick game loop, update charge calculations |
Building Event Listener Tools
Agents should build custom tools that connect to GRASS when they need event-driven behavior. Here are patterns to follow.
Minimal Node.js Listener
Install the NATS WebSocket client:
CODEBLOCK5
CODEBLOCK6
Minimal Python Listener
Install the NATS client:
CODEBLOCK7
CODEBLOCK8
Raid Alert Tool (example pattern)
A tool that watches for raids on your planet and outputs an alert:
CODEBLOCK9
Player Creation Watcher (example pattern)
Instead of polling structsd query structs address after guild signup, watch for the address registration event:
CODEBLOCK10
When to Build a Custom Tool
Build a GRASS listener tool when:
- - You need to wait for an event — guild signup completion, fleet arrival, raid detection
- You need continuous monitoring — threat detection during vulnerable ore window, combat tracking
- You want an event-driven game loop — react to block events instead of polling on a timer
- You're managing multiple players — one GRASS connection can monitor all your entities simultaneously
Store custom tools in your workspace (e.g., scripts/ or alongside the relevant skill).
Connection Best Practices
- - Use specific subjects once you know what you need. Wildcards are for discovery.
- Limit to 10-20 subscriptions per connection to avoid overwhelming the client.
- Implement reconnection with exponential backoff — NATS connections can drop.
- Parse JSON defensively — not all messages may match expected schema.
- Close connections when done. Don't leave idle GRASS connections open.
Procedure
Quick Setup
- 1. Get the GRASS endpoint from your guild config (or use
ws://crew.oh.energy:1443) - Record the endpoint in TOOLS.md under Servers
- Choose your language (Node.js or Python)
- Install the NATS client library (
nats.ws for Node, nats-py for Python) - Write a listener script for your specific use case
- Run it in a background terminal
For Ongoing Monitoring
- 1. Subscribe to your planet(s):
structs.planet.{id} — raid alerts, fleet arrivals - Subscribe to your structs:
structs.struct.{id} — attack/status alerts - Subscribe to global:
structs.global — block tick for game loop timing - Log events to memory/ for cross-session awareness
Automation Patterns (Defence Contractor)
The Structs permission system and GRASS event stream were designed for AI agents to automate game responses. The design docs call this the "Defence Contractor" pattern — an agent that monitors events and acts on behalf of players within scoped permissions.
Common Automation Triggers
| Event | Action | Permission Needed |
|---|
| INLINECODE102 on your extractor | Immediately start INLINECODE103 | Signer key for the player |
| INLINECODE104 on your refinery |
Immediately start next
struct-ore-mine-compute | Signer key for the player |
|
planet_raid_start on your planet | Alert, activate stealth, reposition defenders | Signer key or delegated permission |
|
struct_attack targeting your struct | Log attacker, assess threat, counter-attack if able | Signer key or delegated permission |
|
struct_health showing HP drop | Prioritize defense, consider fleet retreat | Signer key for fleet-move |
|
fleet_move to your planet from unknown fleet | Identify incoming player, assess threat level | Read-only (query) |
Permission Scoping for Automated Agents
When delegating actions to an automation agent (separate key or service):
- 1. Grant minimal permissions: Use
permission-grant-on-object to allow specific actions on specific structs, not blanket access. - Separate keys: The automation agent should use its own signing key, registered as a secondary address on the player via
address-register. - Scope by struct: Grant mine/refine permissions on extractors and refineries only. Grant defense permissions on fleet structs only.
- Revoke when not needed: Use
permission-revoke-on-object to remove automation access during sensitive operations.
Example: Refine-on-Mine-Complete Loop
CODEBLOCK11
This creates a continuous mine-refine loop that runs unattended. Ore is never left unrefined.
Example: Defend-on-Raid-Detected
CODEBLOCK12
Safe Boundaries
- - Never auto-spend Alpha without commander approval (infusion, guild-bank operations)
- Never auto-move fleets away from defended planets without threat assessment
- Always log actions to
memory/ for cross-session audit trail - Rate-limit reactions — one transaction per ~6 seconds per key (sequence number constraint)
See Also
Structs Streaming (GRASS)
GRASS(游戏实时应用流服务)通过NATS传递实时游戏事件。无需重复轮询查询,订阅GRASS即可在事件发生时立即做出反应。
何时使用GRASS
| 场景 | 使用GRASS | 使用轮询 |
|---|
| 检测入侵袭击 | 是 — 即时警报 | 太慢 |
| 等待公会注册后的玩家创建 |
是 — 监听address_register | 每10秒轮询也可行 |
| 监控舰队抵达你的星球 | 是 — fleet_arrive事件 | 可能错过 |
| 战斗中追踪结构体生命值 | 是 — 包含struct
health的planetactivity | 太慢 |
| 查看自己的资源余额 | 否 | 是 — 一次性查询 |
| 读取结构体类型属性 | 否 | 是 — 静态数据 |
经验法则:如果需要响应某事,使用GRASS。如果需要读取某事,使用查询。
查找你的GRASS端点
GRASS WebSocket URL 不是硬编码的 — 它来自公会配置。
- 1. 查询公会列表:curl http://reactor.oh.energy:1317/structs/guild
- 跟随公会的endpointURL获取其配置
- 查找services.grassnatswebsocket
示例(Orbital Hydro公会):
json
{
services: {
grassnatswebsocket: ws://crew.oh.energy:1443,
guild_api: http://crew.oh.energy/api/,
reactor_api: http://reactor.oh.energy:1317/
}
}
grassnatswebsocket的值就是你的NATS WebSocket端点。并非所有公会都提供此服务 — 使用前请确认。
一个可靠的参考端点:ws://crew.oh.energy:1443(Orbital Hydro / Slow Ninja)。
先探索再订阅
在订阅特定主题之前,先订阅>通配符以查看GRASS服务器上的所有流量。这将揭示实际使用的主题模式,可能与文档不同。
javascript
const sub = nc.subscribe(>);
for await (const msg of sub) {
console.log([${msg.subject}], new TextDecoder().decode(msg.data));
}
观察输出30-60秒。你会看到类似structs.planet.2-1、consensus、healthcheck等主题。一旦知道哪些主题承载了你需要的事件,就将订阅范围缩小到这些特定主题。
重要提示:结构体事件(攻击、建造、状态变更)通常出现在星球主题上,而非结构体主题。如果你没有收到预期的结构体事件,请订阅该结构体所属的星球主题。
主题模式
订阅与你关注的实体相匹配的主题:
| 实体 | 通配符 | 特定 | 示例 |
|---|
| 玩家 | structs.player. | structs.player.{guildid}.{playerid} | structs.player.0-1.1-11 |
| 星球 |
structs.planet. | structs.planet.{planet_id} | structs.planet.2-1 |
| 公会 | structs.guild.* | structs.guild.{guild_id} | structs.guild.0-1 |
| 结构体 | structs.struct.* | structs.struct.{struct_id} | structs.struct.5-1 |
| 舰队 | structs.fleet.* | structs.fleet.{fleet_id} | structs.fleet.9-1 |
| 地址 | structs.address.register.* | structs.address.register.{code} | -- |
| 库存 | structs.inventory.> | structs.inventory.{denom}.{guild
id}.{playerid}.{address} | 代币流动 |
| 网格 | structs.grid.* | structs.grid.{object_id} | 属性变更(矿石、电力、负载等) |
| 全局 | structs.global | structs.global | 区块更新 |
| 共识 | consensus | consensus | 链共识事件 |
| 健康检查 | healthcheck | healthcheck | 节点健康状态 |
使用通配符(*)探索存在哪些事件。一旦知道需要什么,就缩小到特定主题。使用>查看所有内容(参见上面的先探索再订阅)。
事件类型
星球事件
| 事件 | 描述 | 响应方式 |
|---|
| raidstatus | 星球上袭击开始/完成 | 激活防御,发出警报 |
| planetactivity |
活动日志,包括struct_health变更 | 追踪战斗伤害 |
| fleet_arrive | 舰队抵达星球 | 准备防御或欢迎 |
| fleet_depart | 舰队离开星球 | 更新威胁评估 |
结构体事件
注意:结构体事件经常出现在星球主题(structs.planet.{id})上,而非结构体主题。如果需要完整覆盖,请同时订阅两者。
| 事件 | 描述 | 响应方式 |
|---|
| structattack | 结构体被攻击 | 反击,修复 |
| structstatus |
结构体状态变更(在线/离线/被摧毁) | 重建,重新分配电力 |
| struct
defenseadd / struct
defenseremove | 防御分配变更 | 更新防御地图 |
| struct
defenderclear | 所有防御关系清除 | 重新分配防御者 |
| struct
blockbuild_start | 建造操作启动 | 在任务列表中追踪 |
| struct
blockore
minestart | 采矿操作启动 | 在任务列表中追踪 |
| struct
blockore
refinestart | 精炼操作启动 | 在任务列表中追踪 |
玩家事件
| 事件 | 描述 | 响应方式 |
|---|
| playerconsensus | 玩家链数据更新 | 更新情报 |
| playermeta |
玩家元数据变更 | 更新情报 |
公会事件
| 事件 | 描述 | 响应方式 |
|---|
| guildconsensus | 公会链数据更新 | 更新公会状态 |
| guildmembership |
成员加入/离开公会 | 更新关系图 |
库存事件
主题:structs.inventory.{denom}.{guildid}.{playerid}.{address}
追踪代币流动 — Alpha物质、公会代币、矿石等。
| 类别 | 描述 | 响应方式 |
|---|
| sent | 从此玩家发送的代币 | 更新余额追踪 |
| received |
此玩家收到的代币 | 更新余额追踪 |
| seized | 通过袭击夺取的代币 | 触发反击或精炼警报 |
| mined | 开采的矿石 | 立即开始精炼 |
| refined | 精炼成Alpha的矿石 | 更新财富追踪 |
| minted | 铸造的公会代币 | 追踪公会经济 |
| infused | 注入反应堆/发电机的Alpha | 更新容量追踪 |
| forfeited | 丢失的代币(罚款等) | 调查原因 |
网格事件
主题:structs.grid.{object_id}
追踪任何游戏对象(玩家、结构体、星球)上的属性变更。
| 类别 | 描述 | 响应方式 |
|---|
| capacity | 电力容量变更 | 检查是否接近离线 |
| connectionCapacity |
连接容量变更 | 更新电力路由 |
| connectionCount | 连接数量变更 | 更新电力路由 |
| fuel | 燃料水平变更 | 监控发电机/反应堆 |
| lastAction | 最后操作时间戳更新 | 追踪活动 |
| load | 电力负载变更 | 检查是否接近离线 |
| nonce | 玩家nonce递增 | 检测活动(对侦察有用) |
| ore | 矿石余额变更 |
立即精炼(如果是你的);袭击目标(如果是他们的) |
| player_consensus | 玩家共识数据更新 | 更新情报 |
| power | 电力水平变更 | 监控能源基础设施 |
| proxyNonce | 代理nonce变更 | 检测代理活动 |
| structsLoad | 结构体负载变更 | 评估舰队实力变化 |
战斗事件负载
struct_attack事件包含详细的逐发解析。示例负载(在星球主题上观察到):
json
{