Async & networking
s2script integrates Promises with the game frame. Timers and network I/O never block the main thread.
Timers
import { delay, nextTick, nextFrame, threadSleep } from '@s2script/timers';
await nextTick(); // after current microtasks
await nextFrame(); // next OnGameFrame
await delay(1000); // ~1s, frame-integrated
await threadSleep(50); // threadpool sleep, marshalled back on the frame drain HTTP
import { fetch } from '@s2script/http';
const res = await fetch('https://example.com/api', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ steamId }),
timeoutMs: 10_000
});
if (res.ok) {
const data = await res.json();
} HTTP status errors (4xx/5xx) resolve with ok: false (web-fetch parity — useful on the join path). Only network failures and timeouts reject. Bodies are buffered (10MB cap); binary deferred.
WebSocket
import { WebSocket } from '@s2script/ws';
const ws = await WebSocket.connect('wss://example.com/ws');
ws.onMessage((text) => console.log(text));
ws.onClose((code) => console.log('closed', code));
ws.send('hello');
ws.close(); Subscribe to onMessage synchronously after await connect so early frames are not missed. Connections are owner-scoped and closed on plugin unload.
Database
import { Database } from '@s2script/db';
const db = await Database.open('myplugin');
await db.execute('CREATE TABLE IF NOT EXISTS boots (id INTEGER PRIMARY KEY)');
const rows = await db.query('SELECT * FROM boots');
await db.close(); API is Promise-based (SQLite runs in-process today; the contract stays async for a future threadpool move). Handles are opaque integers, owner-scoped, and ledgered.