diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..900dc64 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,43 @@ +name: Deploy Docs Site +on: + push: + branches: + - master + +# Prohibit concurrent workflows +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v6 + - run: corepack enable + - uses: actions/setup-node@v6 + with: + node-version-file: .node-version + cache: yarn + - run: yarn install --immutable + - run: yarn build:docs + - id: deployment + uses: actions/upload-pages-artifact@v3 + with: + path: ./docs + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + permissions: + id-token: write + pages: write + needs: build + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 07e8b5b..9b686a2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,17 +8,17 @@ on: - master jobs: - build: + lint: runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - - uses: actions/checkout@v6 - - run: corepack enable - - uses: actions/setup-node@v6 - with: - node-version-file: .node-version - cache: yarn - - run: yarn install --immutable - - run: yarn lint + - uses: actions/checkout@v6 + - run: corepack enable + - uses: actions/setup-node@v6 + with: + node-version-file: .node-version + cache: yarn + - run: yarn install --immutable + - run: yarn lint diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f341fc7..4cbc868 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,22 +1,23 @@ -name: Publish +name: Publish to NPM on: + # Trigger when a new release is published on the Github repo release: types: [published] jobs: - build: + publish: runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - - uses: actions/checkout@v6 - - run: corepack enable - - uses: actions/setup-node@v6 - with: - node-version-file: .node-version - registry-url: https://registry.npmjs.org/ - cache: yarn - - run: yarn install --immutable - - run: yarn test - - run: yarn npm publish --access public + - uses: actions/checkout@v6 + - run: corepack enable + - uses: actions/setup-node@v6 + with: + node-version-file: .node-version + registry-url: https://registry.npmjs.org/ + cache: yarn + - run: yarn install --immutable + - run: yarn test + - run: yarn npm publish --access public diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a48cb6a..200d1e8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,17 +8,17 @@ on: - master jobs: - build: + test: runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - - uses: actions/checkout@v6 - - run: corepack enable - - uses: actions/setup-node@v6 - with: - node-version-file: .node-version - cache: yarn - - run: yarn install --immutable - - run: yarn test + - uses: actions/checkout@v6 + - run: corepack enable + - uses: actions/setup-node@v6 + with: + node-version-file: .node-version + cache: yarn + - run: yarn install --immutable + - run: yarn test diff --git a/.gitignore b/.gitignore index 6caccf6..aa34a2d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules auth.js dist +docs npm-debug.log screeps.json diff --git a/README.md b/README.md index 20efa52..db8dfbd 100644 --- a/README.md +++ b/README.md @@ -1,182 +1,193 @@ # Screeps API -## This is a nodejs API for the game Screeps +## This is a Node.js API for the game Screeps [![License](https://img.shields.io/npm/l/screeps-api.svg)](https://npmjs.com/package/screeps-api) [![Version](https://img.shields.io/npm/v/screeps-api.svg)](https://npmjs.com/package/screeps-api) [![Downloads](https://img.shields.io/npm/dw/screeps-api.svg)](https://npmjs.com/package/screeps-api) +[![Docs](https://img.shields.io/badge/API-Docs-green)](https://screepers.github.io/node-screeps-api/) [![Test Status](https://github.com/screepers/node-screeps-api/actions/workflows/test.yml/badge.svg)](https://github.com/screepers/node-screeps-api/actions/workflows/test.yml) [![Lint Status](https://github.com/screepers/node-screeps-api/actions/workflows/lint.yml/badge.svg)](https://github.com/screepers/node-screeps-api/actions/workflows/lint.yml) -![npm](https://nodei.co/npm/screeps-api.png "NPM") +[![npm](https://nodei.co/npm/screeps-api.png)](https://npmjs.com/package/screeps-api) -## Notice on authentication +## Application Usage -As of 12/29/2017 Screeps now uses auth tokens obtained via your screeps account settings. -User/pass auth will stop working February 1, 2018! -[Screeps Announcement](http://blog.screeps.com/2017/12/auth-tokens/) +As of v1.0, all endpoint methods are asynchronous. -## CLI Usage - -As of 1.7.0, a small CLI program (`screeps-api`) is included. - -Server config is specified via a `.screeps.yml` file conforming to the [Unified Credentials File format](https://github.com/screepers/screepers-standards/blob/master/SS3-Unified_Credentials_File.md) - -``` -screeps-api - - Usage: [options] [command] - - Options: - - -V, --version output the version number - --server Server config to use (default: main) - -h, --help output usage information - - Commands: - - raw [args...] Execute raw API call - memory [options] [path] Get Memory contents - segment [options] Get segment contents. Use 'all' to get all) - download [options] Download code - upload [options] Upload code +```javascript +import { ScreepsHttpClient } from 'screeps-api' +import { writeFile } from 'node:fs/promises' -``` +// `ScreepsHttpClient.fromConfig()` finds your configuration file and picks +// server and client configs from it. +// It supports @tedivm's Unified Credentials File format +// (https://github.com/screepers/screepers-standards/blob/master/SS3-Unified_Credentials_File.md) +// and screeps.json +// (https://github.com/screepers/screeps-typescript-starter/blob/master/screeps.sample.json) +// This example initializes the HTTP client using the `servers` section 'main' +// and the `configs` section 'nuke-announcer' from the config file. +// In a real application, consider reading the server/app names from env vars, +// or accepting `--server ` and `--app ` CLI arguments. +const api = await ScreepsHttpClient.fromConfig('main', { app: 'nuke-announcer' }) -## API Usage +// Client config can be accessed like this: +console.log(api.appConfig) -As of 1.0, all functions return Promises +// HTTP API -```javascript -const { ScreepsAPI } = require('screeps-api'); -const fs = require('fs'); - -// Supports @tedivm's [Unified Credentials File format](https://github.com/screepers/screepers-standards/blob/34bd4e6e5c8250fa0794d915d9f78d3c45326076/SS3-Unified_Credentials_File.md) (Pending [screepers-standard PR #8](https://github.com/screepers/screepers-standards/pull/8)) -const api = await ScreepsAPI.fromConfig('main', 'appName') -// This loads the server config 'main' and the configs section 'appName' if it exists -// config section can be accessed like this: -// If making a CLI app, its suggested to have a `--server` argument for selection -console.log(api.appConfig.myConfigVar) - -// All options are optional -const api = new ScreepsAPI({ - token: 'Your Token from Account/Auth Tokens' - protocol: 'https', - hostname: 'screeps.com', - port: 443, - path: '/' // Do no include '/api', it will be added automatically -}); - -// You can overwrite parameters if needed -api.auth('screeps@email.com','notMyPass',{ - protocol: 'https', - hostname: 'screeps.com', - port: 443, - path: '/' // Do no include '/api', it will be added automatically -}) - -// If you want to point to the screeps PTR (Public Test Realm), -// you can set the 'path' option to '/ptr' and it will work fine. - -// Dump Memory -api.memory.get() +// Dump entire Memory object to a file +api.userMemoryGet() .then(memory => { - fs.writeFileSync('memory.json', JSON.stringify(memory)) + writeFile('memory.json', JSON.stringify(memory), { encoding: 'utf-8' }) }) - .catch(err => console.error(err)); + .catch(console.error) - -// Dump Memory Path -api.memory.get('rooms.W0N0') - .then(memory => { - fs.writeFileSync('memory.rooms.W0N0.json', JSON.stringify(memory)) +// Dump a subset of Memory to a file +api.userMemoryGet('rooms.W0N0') + .then((memory) => { + writeFile('memory.rooms.W0N0.json', JSON.stringify(memory), { encoding: 'utf-8' }) }) - .catch(err => console.error(err)); + .catch(console.error) // Get user info -api.me().then((user)=>console.log(user)) +api.authMe().then(me => console.log('My user info:', me)).catch(console.error) -// Socket API +// Download code from the "default" branch and log it +api.userCodeGet('default') + .then((data) => console.log('My code:', JSON.stringify(data, undefined, 2))) + .catch(console.error) +// Upload code to the "apiDemo" branch +api.userCodeSet({ + branch: 'apiDemo', + modules: { + main: 'module.exports.loop = function() { console.log("Hello, world!") }' + } +}).catch(console.error) + +// WebSocket API + +// Connect and authenticate api.socket.connect() -// Events have the structure of: + +// Events follow this format: // { -// channel: 'room', -// id: 'E3N3', // Only on certain events +// type: 'room', +// id: 'shard0', // Only on certain events, otherwise undefined +// path: 'E3N3', // Only on certain events, otherwise an empty string // data: { ... } // } -api.socket.on('connected',()=>{ - // Do stuff after connected +api.socket.on('connected', () => { + console.log('Websocket client connected') + // Do stuff after connected }) -api.socket.on('auth',(event)=>{ - event.data.status contains either 'ok' or 'failed' - // Do stuff after auth +api.socket.on('auth', (event) => { + // Contains either 'ok' or 'failed' + console.log('Websocket auth:', event.data.status) + // Do stuff after auth }) -// Events: (Not a complete list) -// connected disconnected message auth time protocol package subscribe unsubscribe console +// Subscriptions can be queued even before the client connects or auths, +// although you may want to subscribe from the connected or auth callback +// to better handle reconnects +function onConsole(event) { + const { messages, error, shard } = event.data + const shardTag = shard ? `[${shard}] ` : '' + if (error) console.error(shardTag + error) -// Subscribtions can be queued even before the socket connects or auths, -// although you may want to subscribe from the connected or auth callback to better handle reconnects + // messages is undefined if nothing was logged or evaluated + if (!messages) return + // `console.log()` output from the previous tick + messages.log.map(l => shardTag + l).forEach(console.info) + + // `POST /api/user/console` results from the previous tick + messages.results.map(r => `< ${r}`).forEach(console.info) +} api.socket.subscribe('console') -api.socket.on('console',(event)=>{ - event.data.messages.log // List of console.log output for tick -}) +api.socket.on('console', onConsole) +// Starting in v1.0, you can also pass a handler straight to subscribe! +api.socket.subscribe('console', onConsole) -// Starting in 1.0, you can also pass a handler straight to subscribe! -api.socket.subscribe('console', (event)=>{ - event.data.messages.log // List of console.log output for tick +// Watch CPU/Memory usage +api.socket.subscribe('cpu', (event) => { + console.log('CPU used:', event.data.cpu) + console.log('Memory used (bytes):', event.data.memory) }) -// More common examples -api.socket.subscribe('cpu',(event)=>console.log('cpu',event.data)) -api.code.get('default').then(data=>console.log('code',data)) -api.code.set('default',{ - main: 'module.exports.loop = function(){ ... }' +// Watch for updates to Memory paths +api.socket.subscribe('memory/stats', (event) => { + console.log('Memory.stats:', JSON.stringify(event.data, undefined, 2)) }) -api.socket.subscribe('memory/stats',(event)=>{ - console.log('stats',event.data) -}) -api.socket.subscribe('memory/rooms.E0N0',(event)=>{ - console.log('E0N0 Memory',event.data) +api.socket.subscribe('memory/rooms.E0N0', (event) => { + console.log('Memory.rooms.E0N0:', JSON.stringify(event.data, undefined, 2)) }) ``` -## Endpoint documentation +## CLI Usage -Server endpoints are listed in the `docs` folder: - * [Endpoints.md](/docs/Endpoints.md) for direct access - * [Websocket_endpoints.md](/docs/Websocket_endpoints.md) for web socket endpoints -Those lists are currently not exhaustive. +As of v1.7, a small CLI program (`screeps-api`) is included. -## Debugging +Server/auth credentials are located using `ScreepsConfigManager.loadConfig()`. All commands aside from `help` accept a `--server ` option to specify the name of the server to use from your config file. -`node-screeps-api` uses the [Debug](https://www.npmjs.com/package/debug) package to expose diagnostic information. Debug output is divided into several namespaces: -* `screepsApi:http`: HTTP requests -* `screepsApi.ratelimit`: HTTP API rate limit state -* `screepsApi.ratelimitexceeded`: HTTP API endpoint rate limit exceeded events -* `screepsApi.socket`: Socket API events/messages +``` +> screeps-api --help +Usage: screeps-api [options] [command] + +Options: + -V, --version output the version number + -h, --help display help for command + +Commands: + call [options] [args...] Call an API endpoint method on ScreepsHttpClient + memory [options] [path] Read from or write to Memory + segment [options] Read or write RawMemory segments + download [options] Download code and WASM binaries + upload [options] Upload code and WASM binaries + help [command] display help for command +``` -Multiple namespaces can be specified by providing a comma-delimited list (ex: `screepsApi:http,screepsApi:ratelimit`). All namespaces can be specified by providing `screepsApi:*`. +## Endpoint Documentation + +Check the [docs](https://screepers.github.io/node-screeps-api/) for a detailed list of supported endpoints: +* HTTP API: [`ScreepsHttpClient`](https://screepers.github.io/node-screeps-api/docs/classes/index.ScreepsHttpClient.html) +* WebSocket API: [`ScreepsSocketClient`](https://screepers.github.io/node-screeps-api/docs/classes/index.ScreepsSocketClient.html) + +Please note that the listed endpoints and WebSocket events are not exhaustive. + +## Development + +This project uses the [yarn package manager](https://yarnpkg.com/). To ensure you're using the correct version instead of v1.x: -To enable debug output in Node, set the `DEBUG` environment variable to the -namespace(s) you want to enable. Here is an example that uses the CLI in bash: ```sh -DEBUG=screepsApi.http,screepsApi.ratelimit screeps-api raw --server main auth.me +# Enable corepack: https://yarnpkg.com/corepack +set corepack enable +# Install the version of yarn specified in package.json's "packageManager" field +yarn install +# You may need to re-run the command after installing Yarn for the first time +# in order to install dependencies +yarn install ``` -These environment variables work when invoking your own apps as well. +### Configuration -You can also enable/disable debug logs dynamically using `ScreepsApi.debug()`: -```ts -const api = ScreepsApi.fromConfig('main', 'appName') +The [documentation](https://screepers.github.io/node-screeps-api/documents/Configuration_and_Credential_Files.html) goes into detail on how to set up a configuration file, but the simplest way to get started is to copy a `screeps.yaml` or `screeps.json` file to the repo root directory. + +### Running Scripts + +`tsx` is used to execute TypeScript scripts without having to run `tsc` to transpile them: + +```sh +# Run an example script +yarn exec tsx examples/console.ts -// Enable debug logging for HTTP requests and rate limits: -api.debug({ http: true, rateLimit: true }) +# Run the screeps-api CLI tool +yarn exec tsx bin/screeps-api.ts call version -// Diable all debug logging -api.debug() +# package.json defines a cli script to make testing the CLI more convenient. +# The following command is functionally identical to the previous one: +yarn cli call version ``` diff --git a/bin/screeps-api.ts b/bin/screeps-api.ts index e01c074..3aba4e4 100755 --- a/bin/screeps-api.ts +++ b/bin/screeps-api.ts @@ -3,7 +3,8 @@ import { Command } from 'commander' import { readFile, writeFile } from 'node:fs/promises' import path from 'node:path' import process from 'node:process' -import { ScreepsAPI } from '../src/ScreepsAPI' +import { ScreepsHttpClient } from '../src' +import { UserCodeSetRequest } from './http' interface CommandOptions { server?: string @@ -11,8 +12,8 @@ interface CommandOptions { type RawApiFn = (...args: unknown[]) => Promise -function init(opts?: CommandOptions): Promise { - return ScreepsAPI.fromConfig(opts?.server ?? 'main') +function init(opts?: CommandOptions): Promise { + return ScreepsHttpClient.fromConfig(opts?.server ?? 'main') } function json(data: unknown) { @@ -31,11 +32,10 @@ async function out(data: unknown) { const program = new Command() -const commandBase = (name: string, args = '') => { +const commandBase = (name: string, args?: string) => { const command = new Command(name) - command - .arguments(args) - .option('--server ', 'Server config to use', 'main') + if (args) command.arguments(args) + command.option('--server ', 'Server config to use', 'main') program.addCommand(command) return command } @@ -46,36 +46,26 @@ const pkg = JSON.parse(await readFile(pkgUrl, 'utf8')) as { version: string } program .version(pkg.version) -commandBase('raw', ' [args...]') - .summary('Call an API endpoint via ScreepsAPI.raw.') - .description(`Call an API endpoint defined on ScreepsAPI.raw. +commandBase('call', ' [args...]') + .summary('Call an API endpoint method on ScreepsHttpClient') + .description(`Call an API endpoint defined on ScreepsHttpClient. - is formatted as it would be if you were calling the endpoint via ScreepsAPI.raw. -[args...] are passed directly to the relevant ScreepsAPI.raw function. + is formatted as it would be if you were calling the endpoint via ScreepsHttpClient. +[args...] are passed directly to the relevant ScreepsHttpClient method. `) .addHelpText('after', `Examples: -# Run 'GET /api/auth/me' on the 'ptr' server from your credentials file -screeps-api --server ptr raw auth.me -# Run 'GET /api/scoreboards/list?limit=20&offset=50' on default server -screeps-api raw scoreboard 20 50 -# Run 'GET /api/scoreboards/list?limit=20&offset=50' on default server -screeps-api raw scoreboard 20 50 -# Fetch entire Memory object from shard0 on 'mmo' server from your credentials file -screeps-api raw user.memory.get "" "shard0" +# Run 'GET /api/auth/me' on the "ptr" server from your credentials file +screeps-api --server ptr call authMe +# Run 'GET /api/scoreboards/list?limit=20&offset=50' on "main" server from your credentials file +screeps-api call scoreboardList 20 50 +# Fetch entire Memory object from shard0 on "mmo" server from your credentials file +screeps-api call userMemoryGet "" "shard0" `) - .action(async function (cmd: string, args: unknown[], opts?: CommandOptions) { + .action(async function (endpoint: string, args: unknown[], opts?: CommandOptions) { const api = await init(opts) - const path = cmd.split('.') - let fn: { [key: string]: unknown } | RawApiFn = api.raw - for (const part of path) { - if (typeof fn === 'function') { - console.error(`Command '${cmd}' not found on ScreepsAPI.raw`) - this.help({ error: true }) // prints to stderr and exits with error code - } - fn = fn[part] as typeof fn - } + const fn = api[endpoint as unknown as keyof typeof api] if (!fn || typeof fn !== 'function') { - console.error(`Command '${cmd}' not found on ScreepsAPI.raw`) + console.error(`Endpoint method '${endpoint}' not found on ScreepsHttpClient`) this.help({ error: true }) // prints to stderr and exits with error code } await out(await (fn as RawApiFn).apply(api, args)) @@ -111,12 +101,12 @@ If --set is used, this command will instead set the value of [path] to th this.help({ error: true }) // prints to stderr and exits with error code } const data = await readFile(opts.set, 'utf8') - await api.memory.set(memPath, data, opts.shard) + await api.userMemorySet(memPath, data, opts.shard) await out('Memory written') return } - const res = await api.memory.get(memPath, opts.shard) + const res = await api.userMemoryGet(memPath, opts.shard) if (!res.data) { return } @@ -137,9 +127,8 @@ interface SegmentOptions extends CommandOptions { } commandBase('segment', '') - .description(`Read or write RawMemory segments. - -Reads/downloads segment data by default. should be a comma-delimited + .summary('Read or write RawMemory segments') + .description(`Reads/downloads segment data by default. should be a comma-delimited list of all segment IDs that should be fetched (or 'all' to get all segments). If --set is used, must be the ID of a single segment. @@ -155,13 +144,13 @@ If --set is used, must be the ID of a single segment. this.help({ error: true }) // prints to stderr and exits with error code } const data = await readFile(opts.set, 'utf8') - await api.memory.segment.set(segment, JSON.parse(data), opts.shard) + await api.userMemorySegmentSet(segment, JSON.parse(data), opts.shard) await out('Segment uploaded') } else { if (segment === 'all') { segment = Array.from({ length: 100 }, (_v, k) => k).join(',') } - const { data } = await api.memory.segment.get(segment, opts.shard) + const { data } = await api.userMemorySegmentGet(segment, opts.shard) const dir = opts.dir const segments = data if (dir) { @@ -194,7 +183,7 @@ commandBase('download') .action(async function (opts: DownloadOptions) { const api = await init(opts) const dir = opts.dir - const { modules } = await api.code.get(opts.branch) + const { modules } = await api.userCodeGet(opts.branch) if (dir) { await Promise.all(Object.keys(modules).map(async (fn) => { const data = modules[fn] @@ -219,7 +208,7 @@ commandBase('upload', '') .option('-b --branch ', 'Code branch', 'default') .action(async function (files: string[], opts: UploadOptions) { const api = await init(opts) - const modules: Api.UserCodeSetRequest['modules'] = {} + const modules: UserCodeSetRequest['modules'] = {} const ps = [] for (const file of files) { ps.push((async (file) => { @@ -234,7 +223,7 @@ commandBase('upload', '') })(file)) } await Promise.all(ps) - await out(api.code.set({ branch: opts.branch, modules })) + await out(api.userCodeSet({ branch: opts.branch, modules })) }) function run() { diff --git a/docs/Endpoints.md b/docs/Endpoints.md deleted file mode 100644 index d278e51..0000000 --- a/docs/Endpoints.md +++ /dev/null @@ -1,236 +0,0 @@ -Copied from [python-screeps](https://github.com/screepers/python-screeps/blob/master/docs/Endpoints.md) - -Start by sending a request to `auth/signin` with your e-mail address and -password in a JSON object in the POST data. The response JSON object contains a -token (a hex string); remember that value. Each time you make a request that -requires authentication (the leaderboard and terrain ones, at least, don't), -send the most recent token value as both the `X-Token` and `X-Username` -headers. The response will contain a new token value in the `X-Token` header -with which you should replace your saved token. (You can send the token on every -request and just check for a new one in the response, so you don't have to know -exactly which calls require authentication.) - -Example request parameters are given below, along with schemata for the server's -responses. - -Memory and console endpoints are from -[bzy-xyz](https://gist.github.com/bzy-xyz/9c4d8c9f9498a2d7983d). - -You can access the PTR by changing `screeps.com` to `screeps.com/ptr` in all URLs. - -# Enumeration values -When an endpoint takes `interval` or `statName` as an argument, the valid values -are the ones listed below. - -- interval: 8, 180, 1440 (8 for 1h, 180 for 24h and 1440 for 7 days) -- statName: creepsLost, creepsProduced, energyConstruction, energyControl, energyCreeps, energyHarvested - -# Authentication -- [POST] `https://screeps.com/api/auth/signin` - - post data: `{ email, password }` - - response: `{ ok, token }` - -# Room information -- `https://screeps.com/api/game/room-overview?interval=8&room=E1N8` - - `{ ok, owner: { username, badge: { type, color1, color2, color3, param, flip } }, stats: { energyHarvested: [ { value, endTime } ], energyConstruction: [ { value, endTime } ], energyCreeps: [ { value, endTime } ], energyControl: [ { value, endTime } ], creepsProduced: [ { value, endTime } ], creepsLost: [ { value, endTime } ] }, statsMax: { energy1440, energyCreeps1440, energy8, energyControl8, creepsLost180, energyHarvested8, energy180, energyConstruction180, creepsProduced8, energyControl1440, energyCreeps8, energyHarvested1440, creepsLost1440, energyConstruction1440, energyHarvested180, creepsProduced180, creepsProduced1440, energyCreeps180, energyControl180, energyConstruction8, creepsLost8 } }` - -- `https://screeps.com/api/game/room-terrain?room=E1N8` - - `{ ok, terrain: [ { room, x, y, type } ] }` - - `type` in each element of `terrain` can be "wall" or "swamp" - -- `https://screeps.com/api/game/room-terrain?room=E1N8&encoded=1` - - `{ ok, terrain: [ { _id, room, terrain, type } ] }` - - `terrain` is a string of digits, giving the terrain left-to-right and top-to-bottom - - 0: plain, 1: wall, 2: swamp, 3: also wall - - encoded argument can be anything non-empty - -- `https://screeps.com/api/game/room-status?room=E1N8` - - `{ _id, status, novice }` - - `status` can at least be "normal" or "out of borders" - - if the room is in a novice area, `novice` will contain the Unix timestamp of the end of the protection (otherwise it is absent) - -- `https://screeps.com/api/experimental/pvp?interval=50` - - `{ ok, time, rooms: [ { _id, lastPvpTime } ] }` - - `time` is the current server tick - - `_id` contains the room name for each room, and `lastPvpTime` contains the last tick pvp occurred - - if neither a valid `interval` nor a valid `start` argument is provided, the result of the call is still `ok`, but with an empty rooms array. - -- `https://screeps.com/api/experimental/pvp?start=14787157` - - `{ ok, time, rooms: [ { _id, lastPvpTime } ] }` - -# Market information - -- `https://screeps.com/api/game/market/orders-index` - - `{"ok":1,"list":[{"_id":"XUHO2","count":2}]}` - - `_id` is the resource type, and there will only be one of each type. - - `count` is the number of orders. - - - `https://screeps.com/api/game/market/my-orders` - - `{ ok, list: [ { _id, created, user, active, type, amount, remainingAmount, resourceType, price, totalAmount, roomName } ] }` - - - `https://screeps.com/api/game/market/orders?resourceType=Z` - - `{ ok, list: [ { _id, created, user, active, type, amount, remainingAmount, resourceType, price, totalAmount, roomName } ] }` - - `resourceType` is one of the RESOURCE_* constants. - - - `https://screeps.com/api/user/money-history` - - `{"ok":1,"page":0,"list":[ { _id, date, tick, user, type, balance, change, market: {} } ] }` - - `page` used for pagination. - - `hasMore` is true if there are more pages to view. - - `market` - - New Order- `{ order: { type, resourceType, price, totalAmount, roomName } }` - - Extended Order- `{ extendOrder: { orderId, addAmount } }` - - Fulfilled Order- `{ resourceType, roomName, targetRoomName, price, npc, amount }` - - Price Change - `{ changeOrderPrice: { orderId, oldPrice, newPrice } }` - -# Leaderboard -- `https://screeps.com/api/leaderboard/seasons` - - `{ ok, seasons: [ { _id, name, date } ] }` - - the `_id` returned here is used for the season name in the other leaderboard calls - -- `https://screeps.com/api/leaderboard/find?mode=world&season=2015-09&username=danny` - - `{ ok, _id, season, user, score, rank }` - - `user` (not `_id`) is the user's _id, as returned by `me` and `user/find` - - `rank` is 0-based - -- `https://screeps.com/api/leaderboard/find?mode=world&username=danny` - - `{ ok, list: [ _id, season, user, score, rank ] }` - - lists ranks in all seasons - -- `https://screeps.com/api/leaderboard/list?limit=10&mode=world&offset=10&season=2015-09` - - `{ ok, list: [ { _id, season, user, score, rank } ], count, users: { : { _id, username, badge: { type, color1, color2, color3, param, flip }, gcl } } }` - -# Messages -- `https://screeps.com/api/user/messages/index` - - `{ ok, messages: [ { _id, message: { _id, user, respondent, date, type, text, unread } } ], users: { : { _id, username, badge: { type, color1, color2, color3, param, flip } } } }` - -- `https://screeps.com/api/user/messages/list?respondent=55605a6654db1fa952de8d90` - - `{ ok, messages: [ { _id, date, type, text, unread } ] }` - -- [POST] `https://screeps.com/api/user/messages/send` - - post data: `{ respondent, text }` - - `respondent` is the long _id of the user, not the username - - response: `{ ok }` - -- `https://screeps.com/api/user/messages/unread-count` - - `{ ok, count }` - -# User information -- `https://screeps.com/api/auth/me` - - `{ ok, _id, email, username, cpu, badge: { type, color1, color2, color3, param, flip }, password, notifyPrefs: { sendOnline, errorsInterval, disabledOnMessages, disabled, interval }, gcl, credits, lastChargeTime, lastTweetTime, github: { id, username }, twitter: { username, followers_count } }` - -- `https://screeps.com/api/user/find?username=danny` - - `{ ok, user: { _id, username, badge: { type, color1, color2, color3, param, flip }, gcl } }` - -- `https://screeps.com/api/user/find?id=55c91dc66e36d62a6167e1b5` - - `{ ok, user: { _id, username, badge: { type, color1, color2, color3, param, flip }, gcl } }` - -- `https://screeps.com/api/user/overview?interval=1440&statName=energyControl` - - `{ ok, rooms: [ ], stats: { : [ { value, endTime } ] }, statsMax }` - -- `https://screeps.com/api/user/respawn-prohibited-rooms` - - `{ ok, rooms: [ ] }` - -- `https://screeps.com/api/user/world-status` - - `{ ok, status }` - - `status` can be `"lost"`, `"empty"` or `"normal"`, lost is when you loose all your spawns, empty is when you have respawned and not placed your spawn yet.; - -- `https://screeps.com/api/user/world-start-room` - - `{ ok, room: [ ] }` - - `room` is an array, but seems to always contain only one element - -- `https://screeps.com/api/xsolla/user` - - `{ ok, active }` - - `active` is the Unix timestamp of the end of your current subscribed time - -- [POST] `https://screeps.com/api/user/console` - - post data: `{ expression }` - - response: `{ ok, result: { ok, n }, ops: [ { user, expression, _id } ], insertedCount, insertedIds: [ ] }` - -- `https://screeps.com/api/user/memory?path=flags.Flag1` - - `{ ok, data }` - - `data` is the string `gz:` followed by base64-encoded gzipped JSON encoding of the requested memory path - - the path may be empty or absent to retrieve all of Memory - -- [POST] `https://screeps.com/api/user/memory` - - post data: `{ path, value }` - - response: `{ ok, result: { ok, n }, ops: [ { user, expression, hidden } ], data, insertedCount, insertedIds }` - -- `https://screeps.com/api/user/memory-segment?segment=[0-99]` - - `{ okay, data }` - - response: `{ ok, data: '' }` - -- [POST ]`https://screeps.com/api/user/memory-segment` - - post data: `{ segment, data }` - - -# Manipulating the game world -- [POST] `https://screeps.com/api/game/gen-unique-object-name` - - post data: `{ type }` - - response: `{ ok, name }` - - `type` can be at least "flag" or "spawn" - -- [POST] `https://screeps.com/api/game/create-flag` - - post data: `{ room, x, y, name, color, secondaryColor }` - - response: `{ ok, result: { nModified, ok, upserted: [ { index, _id } ], n }, connection: { host, id, port } }` - - if the name is new, `result.upserted[0]._id` is the game id of the created flag - - if not, this moves the flag and the response does not contain the id (but the id doesn't change) - - `connection` looks like some internal MongoDB thing that is irrelevant to us - -- [POST] `https://screeps.com/api/game/change-flag` - - post data: `{ _id, room, x, y }` - - response: `{ ok, result: { nModified, ok, n }, connection: { host, id, port } }` - -- [POST] `https://screeps.com/api/game/change-flag-color` - - post data: `{ _id, color, secondaryColor }` - - response: `{ ok, result: { nModified, ok, n }, connection: { host, id, port } }` - -- [POST] `https://screeps.com/api/game/add-object-intent` - - post data: `{ _id, room, name, intent }` - - response: `{ ok, result: { nModified, ok, upserted: [ { index, _id } ], n }, connection: { host, id, port } }` - - `_id` is the game id of the object to affect (except for destroying structures), `room` is the name of the room it's in - - this method is used for a variety of actions, depending on the `name` and `intent` parameters - - remove flag: `name = "remove"`, `intent = {}` - - destroy structure: `_id = "room"`, `name = "destroyStructure"`, `intent = [ {id: , roomName, , user: } ]` - - can destroy multiple structures at once - - suicide creep: `name = "suicide"`, `intent = {id: }` - - unclaim controller: `name = "unclaim"`, `intent = {id: }` - - intent can be an empty object for suicide and unclaim, but the web interface sends the id in it, as described - - remove construction site: `name = "remove"`, `intent = {}` - -- [POST] `https://screeps.com/api/game/set-notify-when-attacked` - - post data: `{ _id, enabled }` - - `enabled` is either `true` or `false` (literal values, not strings) - - response: `{ ok, result: { ok, nModified, n }, connection: { id, host, port } }` - -- [POST] `https://screeps.com/api/game/create-construction` - - post data: `{ room, structureType, x, y}` - - `structureType` is the same value as one of the in-game STRUCTURE_* constants ('road', 'spawn', etc.) - - `{ ok, result: { ok, n }, ops: [ { type, room, x, y, structureType, user, progress, progressTotal, _id } ], insertedCount, insertedIds }` - -# Other -- `https://screeps.com/api/game/time` - - `{ ok, time }` - -- [GET/POST] `https://screeps.com/api/user/code` - - for pushing or pulling code, as documented at http://support.screeps.com/hc/en-us/articles/203022612 - -- [POST] `https://screeps.com/api/game/map-stats` - - post data: `{ rooms: [ ], statName: "..."}` - - statName can be "owner0", "claim0", or a stat (see the enumeration above) followed by an interval - - if it is owner0 or claim0, there's no separate stat block in the response - - response: `{ ok, stats: { : { status, novice, own: { user, level }, : [ { user, value } ] } }, users: { : { _id, username, badge: { type, color1, color2, color3, param, flip } } } }` - - `status` and `novice` are as per the `room-status` call - - `level` can be 0 to indicate a reserved room - -- `https://screeps.com/room-history/E1N8/12340.json` - - `{ timestamp, room, base, ticks: {