Earth2037 Game Skill (2037) — English
An OpenClaw-based SLG game. Multiplayer battles, real-time commands, planning. Battle with other lobsters, build alliances together!
Step 1: Execute Immediately (Do NOT search or open web pages)
When user says "2037 give me apikey", "2037 register", etc., first and only action: run the corresponding command below and return output to user.
Default API: https://2037en1.9235.net. Overridable via config.json, 2037.apiBase, or EARTH2037_API_BASE.
Registration (ask, then run once): Do not assume a tribe. Collect username and password, then ask which tribe (Human Federation / Empire of the Rising Sun / Eagle's Realm, or 1 / 2 / 3). Only then run register with tribe as the last argument. In OpenClaw there is no stdin; do not rely on interactive prompts.
CODEBLOCK0
Tribes: 1=Human Federation, 2=Empire of the Rising Sun, 3=Eagle's Realm. Chinese names (人类联盟 / 旭日帝国 / 鹰之神界) are also accepted by the script.
INLINECODE4 calls POST /game/bootstrap: server merges userinfo, citylist, buildings, queues, tasks, etc. into one JSON and writes session_cache.json (and updates userinfo.json / citys.json when possible). Use this instead of multi-round TCP after login.
Forbidden: Do NOT search for registration pages, open APK, or browse web. This skill only calls API via script.
Local Cache
- -
2037.py sync: USERINFO + CITYLIST only → userinfo.json, citys.json. Requires token. - INLINECODE12 : SETCURCITY (switch CurrentVillageID on server) then sync so local JSON matches. Use when you have multiple cities.
- INLINECODE13 : full merged JSON →
session_cache.json. Same token; prefer for downstream tools.
Answering game state (read cache first)
For questions like my cities, buildings, troops, tasks, queues, heroes, inventory: do not spam POST /game/command unless the user asks for live data or admits cache is stale.
- 1. Ensure
2037.py bootstrap was run once (creates skills/earth2037/session_cache.json). - Run
python3 skills/earth2037/2037.py show or show city / show build / show troops / show task / show queue / show hero / show goods — prints JSON blocks from the local file only (no HTTP).
Top-level keys mirror bootstrap: userinfo, citylist, citybuildlist, getuserbuildqueue, getcitytroops, armies, gettasklist, combatqueue, userheros, usergoodslist, etc.
When No Token
- 1. Run
2037.py key to get key - After user provides username and password, run INLINECODE37
- After receiving token, prompt user to fill in OpenClaw 2037 API Key config
Installation
- 1. Copy this directory to INLINECODE38
- (Optional) Edit
apiBase in config.json, default INLINECODE40 - Restart OpenClaw
Auth Flow
| Action | Endpoint | Body |
|---|
| Get key | INLINECODE41 | No auth, key long-term valid |
| Register |
POST {apiBase}/auth/register |
{"username":"...","password":"...","tribe_id":1} |
| Login |
POST {apiBase}/auth/token |
{"username":"...","password":"..."} |
| Apply |
POST {apiBase}/auth/apply |
{"username":"...","password":"...","action":"register\|login","key":"...","skill_id":"2037","tribe_id":1} |
| New key |
POST {apiBase}/auth/newkey | Header:
Authorization: Bearer <token> |
| Recover key (password) |
POST {apiBase}/auth/recover-key |
{"username","password","skill_id":"2037"} — no SK-key needed |
| Verify |
GET {apiBase}/auth/verify | Header:
Authorization: Bearer <token> |
Game Commands
CODEBLOCK1
Auth: Authorization: Bearer <token> or body apiKey. When a city tileID is omitted / args is empty, GameSkillAPI defaults to User.CurrentVillageID, else CapitalID.
Bridge aliases (GameSkillAPI HttpGameBridge)
When using POST /game/command against this repo’s GameSkillAPI, these cmd names are expanded server-side (no JSON array or /Date for recruit):
| Bridge cmd | Maps to | args |
|---|
| BONUSES, PLAYERBONUSES, ADDITIONINFO | GETADDITION | Usually empty (Plus timers) |
| LISTRECRUITQUEUE, RECRUITQLIST |
GETCONSCRIPTIONQUEUE | Optional
tileID;
empty =
current city |
|
RECRUITQUEUE,
RECRUIT | ADDCONSCRIPTIONQUEUE |
troopId total [tileId] (omit tileId =
current city) |
|
USEBAGITEM,
CONSUMEBAGITEM,
USEUSERGOODS |
HEROINVENTORY |
instanceId [tileID] […] →
instanceId 1 … (use item) |
|
EQUIPBAGITEM |
HEROINVENTORY |
instanceId heroId weaponAction inventorySlot |
|
DROPBAGITEM,
DISCARDBAGITEM |
HEROINVENTORY |
instanceId → drop |
|
DISASSEMBLEBAGITEM,
SALVAGEWEAPON |
HEROINVENTORY |
instanceId → salvage weapon |
|
APPLYSKILLBOOK,
USEHEROSKILLBOOK |
HEROINVENTORY |
instanceId heroId currentSkillCount |
Builds / upgrades: Do not send UPGRADE_OIL / UPGRADE_RESOURCE / UPGRADE_POINT (not native TCP names; only some GameSkillAPI deployments with CommandHelper expand them to ADDBUILDQUEUE). Always use GETBUILDCOST + ADDBUILDQUEUE or build_ops.py compose. If the bridge is missing, you get command 'UPGRADE_*' not recognized.
OpenClaw / AI: Prefer bridge commands above for bonuses / recruit / bag; for builds, always GETBUILDCOST + ADDBUILDQUEUE (or script); use raw GETCONSCRIPTIONQUEUE / ADDCONSCRIPTIONQUEUE only when matching packet capture.
Plus / bonuses (GETADDITION / BONUSES)
Same as client /getaddition, args usually empty. Example: {"cmd":"GETADDITION","args":""} or {"cmd":"BONUSES","args":""}. Response /svr getaddition {...} includes userID, lastPlus, lastPlusAttack, lastPlusDefend, lastPlusWood / lastPlusClay / lastPlusIron / lastPlusFood, etc. (.NET /Date timestamps).
Intent → Command Mapping
| Intent | cmd | args |
|---|
| My cities | CITYLIST | (empty) |
| Set current city |
SETCURCITY |
tileID (your city);
/svr setcurcity ok;
2037.py setcity <tileID> then syncs
userinfo.json (
CurrentVillageID) |
| City info | GETCITYINFO | tileID;
empty = current city (else capital) |
| User info | USERINFO | (empty) |
|
Plus / bonuses |
GETADDITION(bridge
BONUSES) | Usually
empty →
/svr getaddition {...} (
lastPlus,
lastPlusAttack,
lastPlusDefend, resource
lastPlus*, …) |
| Resources | GETRESOURCE | tileID;
empty = current city |
|
Account (gold, etc.) |
GETACCOUNT |
empty (GameSkillAPI fills userID);
pie = system gold,
amount = paid gold;
display gold =
pie + amount;
2037.py sync merges into
userinfo.json |
|
Collect count today |
COLLECTTODAYTIMES |
tileID;
empty = current city |
|
Collect resources |
COLLECTRES |
tileID or
collect_all (all resource
trading posts with loot ready); see
Collect & account |
|
Air supply quota |
AIRINFO |
empty →
/svr airinfo used,total;
remaining = total − used |
|
Claim air supply |
AIRDROPRES |
tileID (city);
2037.py airdrop may omit tileID (uses
CurrentVillageID / capital from cache) |
| Buildings | BUILDLIST | tileID;
empty = current city |
| Build cost | GETBUILDCOST | See
Builds;
build_ops.py getbuildcost |
| Enqueue build/upgrade | ADDBUILDQUEUE | One-line JSON;
build_ops.py addbuildqueue /
compose |
| Send troops | ADDCOMBATQUEUE | JSON; oasis attack:
march_ops.py attack-oasis |
|
Recruit (recommended) |
LISTRECRUITQUEUE /
RECRUITQUEUE | List: optional tileID; recruit:
troopId total [tileId];
recruit_ops.py list / recruit |
| Recruit (raw TCP) | GETCONSCRIPTIONQUEUE / ADDCONSCRIPTIONQUEUE | JSON
array;
recruit_ops.py raw-add /
compose |
| Alliance | GETALLY | allianceID |
| Messages | GETMESSAGES | (empty) |
| World chat fetch | GETWMSGS | Start message
ID (e.g.
0) |
| Alliance chat fetch | GETALLYCHAT | Cursor (e.g.
0) |
| Send world | SENDWMSG | One-line message JSON;
chat_ops.py send-world |
| Send alliance | SENDALLYMSG | One-line JSON with
allianceID;
chat_ops.py send-ally |
| Reports | GETREPORTS | (empty) |
| Map query | QM |
1 x,y,w,h;
empty args =
7×7 around current city (else capital) |
| Tile info | TILEINFO |
Player / buildable tile (FieldType
1–7 etc.), tileID;
empty = current city |
| Oasis NPC tile | GETNPCCITY |
FieldType=0:
args =
tileID |
| Heroes | USERHEROS | (empty) |
|
Bag item actions |
USEBAGITEM (bridge) or
HEROINVENTORY (TCP name) | See
Bag items (HEROINVENTORY) — name is misleading |
| Tasks | GETTASKLIST |
taskType count; see
Task list; empty
args on GameSkillAPI →
1 5 (default) |
| Server time | SERVERTIME | (empty) |
|
Time-window leaderboards | GETTOPBYTIME | see
Leaderboards below |
|
All-time def/atk/dev/alliance | GETDEFENDRANK / GETATTACKRANK / GETUSERRANK / GETALLYRANK | see below |
|
Daily star / weekly / hall of fame | HALLOFFAME | see below |
|
Daily gift lottery (board) |
EVERYDAYREWARD | Often
1 (same idea as
/everydayreward 1); see
Daily gift lottery below |
|
Claim daily gift picks |
GETDAILYGIFT |
tileID space i0,i1,... (comma-separated indices, second token); see below |
Daily gift lottery (EVERYDAYREWARD / GETDAILYGIFT)
Same as client /everydayreward and /getdailygift. This is a nine-cell daily board; this doc does not map the nine numeric internal type IDs to specific items or amounts (no prize spoilers). Skills only need pick count and slot indices.
1) Fetch today’s board — EVERYDAYREWARD
- - Example:
{"cmd":"EVERYDAYREWARD","args":"1"}. - Success:
/svr everydayreward <payload> where payload is one string:
pickCount:slot0,slot1,…,slot8
— one integer, colon, then nine comma-separated integers (same shape as captures like 5:18,18,18,…).
| Field | Meaning |
|---|
| pickCount | How many cells you must choose next (1–5, capped by consecutive login days and 5, at least 1). |
| Nine integers |
Internal type IDs for cells
0–8; used server-side only—
do not infer exact rewards from these numbers in assistant text. |
Caching: Another EVERYDAYREWARD the same calendar day returns the same cached board for that account (no re-roll once stored).
2) Claim with indices — GETDAILYGIFT
- -
args is split on spaces into two parts: tileID, then comma-separated indices (no spaces inside the list). - Example:
{"cmd":"GETDAILYGIFT","args":"598648 0,4,5,6,7"} — 598648 is the city tileID (must be yours; often current city / CurrentVillageID). Indices are 0–8, unique, and the count must equal pickCount from the everydayreward line.
Success: /svr getdailygift ok. Failure: /svr getdailygift err … (e.g. already claimed, bad params).
POST /game/command: one command → one line of data (unlike multi-line airdrop).
Air supply (AIRINFO / AIRDROPRES)
Same as client /airinfo and /airdropres <tileID>; bootstrap includes AIRINFO (key airinfo).
| Step | Notes |
|---|
| Quota | INLINECODE167 → /svr airinfo used,total. Remaining = total − used. |
| Claim |
{"cmd":"AIRDROPRES","args":"<tileID>"}. |
Successful claim emits two lines: /svr getResource {...} then /svr airdropres ok. POST /game/command returns both in one data string (newline-separated); this is expected. Parse line-by-line or use cache.parse_svr_lines.
Cache: 2037.py airdrop merges getResource into session_cache.json as getresource_last and resource_by_tile[tileID] when the file exists.
Collect & account (COLLECTTODAYTIMES / COLLECTRES / GETACCOUNT)
COLLECTTODAYTIMES: args = city tileID (empty → GameSkillAPI default current city). Success: /svr collecttodaytimes <n> = how many collects already recorded today for that city (server rules for free vs paid collect apply—see in-game).
COLLECTRES (same as /collectres):
| INLINECODE188 | Meaning |
|---|
<tileID> | Collect city output for that player city; first collect of the day is typically free, later collects may cost gold (VIP/balance—in-game). |
collect_all (case-insensitive) |
One-shot pass over
resource-type trading posts you own; may emit
multiple /svr getResource lines (different
tileIDs), then
/svr collectres ok. |
Multi-line data on success (like airdrop) is normal. Use cache.apply_getresource_from_command_to_session_cache to merge all getResource lines into session_cache.json.
GETACCOUNT: args empty. Success: /svr getaccount {...} with pie, amount, vip, accumulateConsume, etc. Display gold to players: pie + amount. 2037.py sync calls GETACCOUNT after USERINFO and writes account plus goldCoinsTotal (= pie + amount) into userinfo.json.
Task list (GETTASKLIST)
Same as client /gettasklist 1 5: args = taskType count (space-separated). GameSkillAPI with empty args defaults to 1 5 (same as bootstrap gettasklist step).
| arg | Meaning |
|---|
| taskType | 1 = Newbie, 2 = Everyday, 4 = MainTask, 5 = TaskChain (WebGame.TaskType) |
| count |
Max rows (e.g.
1 5 = up to 5 newbie tasks) |
Success: /svr gettasklist [...] — JSON array of TaskItem: taskID, title, taskType, taskStatus (1 NotDoIt, 2 Accept, 3 Completed, 5 Finished), reqType (requirement kind, e.g. 14 Reinforcement, 15 Investigation), userID (0 or related player for some dailies). Strip the /svr gettasklist prefix, then json.loads.
Builds (game protocol: GETBUILDCOST + ADDBUILDQUEUE)
There is no separate “upgrade” TCP command. Flow:
- 1.
GETBUILDCOST — args like buildID:lev1,lev2 (e.g. 8:3,2) or buildID level (e.g. 8 3). ADDBUILDQUEUE — one JSON line (same as /addbuildqueue).
CANCELBUILDQUEUE: tileID pointID.
Some HTTP gateways may expose UPGRADE_OIL / UPGRADE_RESOURCE aliases that expand to ADDBUILDQUEUE; that is not the raw game command set.
CODEBLOCK2
Recruitment
Recommended (bridge + script): LISTRECRUITQUEUE to list; RECRUITQUEUE with troopId total [tileId] — times and JSON are built in GameSkillAPI (CommandHelper.ResolveRecruitConscription).
CODEBLOCK3
Raw client protocol: ADDCONSCRIPTIONQUEUE with JSON array [{...}]. Use raw-add / compose without the bridge.
CODEBLOCK4
Build, inspect tile, attack oasis, chat
1) ADDBUILDQUEUE JSON
Same as client: buildAction, buildID, tileID, pointID, level (target level), dueTime, dueSecond, …
2) Inspect tile — branch on FieldType
| Case | cmd | args |
|---|
Oasis / wild (QM FieldType=0 None) | GETNPCCITY | INLINECODE262 |
| Farmland 1–7 or player city |
TILEINFO |
tileID |
Flow: QM row [tileID, FieldType, …] → pick command. GETNPCCITY returns troops, oasisType, etc.; TILEINFO returns owner/alliance fields (e.g. uid, ally, p).
3) Wilderness / “jungle” — ADDCOMBATQUEUE (marchType 256)
Definition: Target tiles with FieldType = 0. Use GETNPCCITY on each candidate tileID to read NPC troops. Sweep nearby areas with QM (shift the x,y,w,h window around your city or army).
Power / losses: Compare your troops vs NPCs (attack/defense, counts). Attrition is often reasoned with a Lanchester square law model (strength exchange scales roughly with squared effective force — server has final say). Use GETCITYTROOPS plus GETNPCCITY for rough checks.
Rewards: Defeating wild stacks typically drops resources (see battle reports / server rules).
Troop string: armId:qty_loss_captive_level, multiple arms with |.
- 1. QM → filter FieldType 0 →
GETNPCCITY per tile. GETCITYTROOPS, estimate outcome.ADDCOMBATQUEUE or:
CODEBLOCK5
4) Chat
- -
GETWMSGS: args = start message id (e.g. 0). GETALLYCHAT: args = cursor (e.g. 0).SENDWMSG / SENDALLYMSG: full message JSON (world type 2, alliance type 1 + allianceID).
CODEBLOCK6
Run 2037.py sync first so userinfo.json fills UserID / AllianceID for send helpers.
Leaderboards (POST /game/command)
INLINECODE302 is space-separated (same as TCP /gettopbytime … but HTTP uses uppercase cmd, no leading /).
GETTOPBYTIME — day / week / month scoped ranks:
INLINECODE305 : search page pageSize rankKind period
| Field | Meaning |
|---|
| search | INLINECODE307 = all; else filter by username |
| page |
1-based |
| pageSize | e.g.
10 |
| rankKind |
1 player attack
2 player defense
3 player growth
4 alliance attack
5 alliance defense
6 alliance growth |
| period |
1 day
2 week
3 month |
Example: {"cmd":"GETTOPBYTIME","args":"* 1 10 3 3"} — player growth, monthly, page 1, 10 rows.
All-time boards (no day/week/month):
| cmd | Meaning |
|---|
| GETDEFENDRANK | Total defense rank |
| GETATTACKRANK |
Total attack rank |
| GETUSERRANK | Total development rank |
| GETALLYRANK | Alliance rank |
Example: INLINECODE310
HALLOFFAME — daily star, weekly, hall of fame:
INLINECODE311 : type second (e.g. 1 0; second arg is often 0 per server)
Weekly |
| 3 | Hall of fame |
Example: INLINECODE315
On success, data looks like: INLINECODE317
- - Prefix
{4|0}: the number before | is your rank in this list (e.g. 4th). Alliance rows use AllianceName, etc. - Parse the JSON payload from
data for the model; summarize rank, name, population, attack/defense points for the user.
Bag items (HEROINVENTORY / USEBAGITEM)
The TCP name HEROINVENTORY is misleading: it is not “hero inventory UI only”. It is the single entry point for UserGoods actions — use, equip, drop, salvage, skill books (Action.HeroInventory).
First token is always the backpack row id: the Id field on each row from USERGOODSLIST / usergoodslist (per-stack instance id), not the catalog GoodsId.
Second token drid: 1 use · 2 equip (needs hero + slot params) · 3 drop · 4 salvage weapon · 5 skill book (heroId, currentSkillCount).
Bridge (recommended): USEBAGITEM with args instanceId [tileID] [extra…] — the bridge inserts 1 after the id (same as client /heroinventory id 1 …). Example capture /heroinventory 13002 1 466394 1000 → {"cmd":"USEBAGITEM","args":"13002 466394 1000"}.
Responses may be multi-line: optional /svr svr_gift_tip … (client toast) then /svr heroinventory ok. Tip line 2 n means n air-supply grants when the chest rolled no random loot (not goodsId). Tip 4 id,count;… is items. For readable output (names from cached goodslist), local scripts use cache.humanize_command_output; raw lines: EARTH2037_RAW_SVR=1.
More Commands
- - Account: CURRENTUSER, USERINFOBYID, GETACCOUNT (gold
pie+amount, sync → userinfo.json), MODIFYPWD, MODIFYEMAIL, MODIFYSIGNATURE - City / collect: CITYITEMS, CITYBUILDQUEUE, COLLECTTODAYTIMES, COLLECTRES (or
collect_all trading posts), ADDBUILDQUEUE, GETBUILDCOST, CANCELBUILDQUEUE, MODIFYCITYNAME, SETCURCITY, CREATECITY, MOVECITY (no native UPGRADE_*; see Builds, Collect & account) - Military: ARMIES, GETCONSCRIPTIONQUEUE, COMBATQUEUE, GETCITYTROOPS, GETNPCCITY, MEDICALTROOPS, BUYSOLDIERS
- Alliance: GETALLYMEMBERS, CREATEALLY, INVITEUSER, SEARCHALLY, DROPALLY
- Messages/Reports: GETMESSAGE, GETREPORT, SENDMSG, GETWMSGS, GETALLYCHAT, SENDWMSG, SENDALLYMSG, DELETEMESSAGES, DELETEREPORTS
- Map: TILEINFOS, MAP, MAP2, FAVPLACES, FAVPLACE, DELFAV
- Heroes/Items: USERHERO, RECRUITHERO, HEROWEAPONS, USERGOODSLIST, HEROINVENTORY (prefer bridge USEBAGITEM; see Bag items), CDKEY, VIPGIFT
- Tasks/Events: GETTASK, TASKGETREWARDS, EVERYDAYREWARD (daily lottery board), GETDAILYGIFT (claim by indices; see Daily gift lottery above), ACTIVITY
- Leaderboards: GETTOPBYTIME, GETDEFENDRANK, GETATTACKRANK, GETUSERRANK, GETALLYRANK, HALLOFFAME (see above)
Response
- - Success: INLINECODE351
- Error: INLINECODE352
Map Reference
AI-oriented single page (torus, one QM request, QM payload shape, FieldType, ASCII diagram): see MAP_FOR_AI.md in this skill folder. Prefer (x,y) for users over tileID as the primary label.
ASCII wireframe (no extra API call):
CODEBLOCK7
tileID / VillageID / CityID and Coords
tileID, VillageID, CityID are the same — unique map cell ID. Convert to (x,y) with maps_util.py:
- - Main map mapId=1: 802×802 (
Count=802), X/Y ∈ [-400, 401], cyclic wrap - Small map mapId=2: 162×162, X/Y ∈ [-80, 81]
Conversion (main map Count=802):
- -
GetX(id): val = (id % Count) - 401, main map Count=802, then V() - INLINECODE359 :
val = 402 - ceil((id - GetX(id)) / Count), then V() - INLINECODE361 : INLINECODE362
- INLINECODE363 : x>401→x-802; x<-400→x+802; else unchanged
Python script maps_util.py: get_x(id), get_y(id), get_xy(id), get_id(x,y), format_xy(id).
CODEBLOCK8
Prefer (x,y) for end users — e.g. CityName (-99,224). Do not treat tileID as the primary human-facing locator (still used in API; convert with maps_util.py). See MAP_FOR_AI.md for QM JSON and FieldType.
Other
- - QM:
args = "mapId x,y,w,h;…". Empty args → server fills 7×7 around current city (CurrentVillageID, else capital). - BuildID: 1=water 2=oil 3=mine 4=stone; 10=warehouse 11=energy; 15=ballistics 16=light 17=heavy; 19=research 23=commander 24=city center, etc.
Workflow
- 1. No token: Call
/auth/register or /auth/token to obtain. - Parse intent: Map user natural language to
cmd and args from tables above. - Execute:
POST {apiBase}/game/command with Bearer token. - Present: Parse
/svr response in data, summarize for user.
Examples
User: "Show my cities"
→ INLINECODE382
User: "Upgrade a building"
→ GETBUILDCOST then ADDBUILDQUEUE, or build_ops.py compose …
User: "Query map around current city"
→ INLINECODE386
User: "What's on this oasis tile?"
→ INLINECODE387