開発者へ / アプリケーション API

Arcade サーバー

五つのゲームのための WebSocket サーバーと、その傍らのいくつかの HTTP ルートです。五つのうち四つでは、クライアントはゲームの入力をまったく送りません。入力はトランザクションであり、ソケットはそれが着地するのを見るための道です。

API ではなくソケット

一つのプロセスが、一つのイベントループの上で五つのゲームの部屋とロビーを動かし、ノードへの 自分のソケットでチェーンを読み、その結果を接続中のすべてのクライアントへ配ります。 インターフェースは WebSocket です。HTTP のルートは、ヘルスチェックと、サイトの外枠が どのページにも描く二つの数値のために存在します。

ゲームの入力はフレームではなくトランザクションです

アリーナでの操縦、曲線への賭け、スピン、盤面への賭け。どれもプレイヤーがコントラクトへ 送るトランザクションです。このソケットが運ぶのは、あなたが誰を見ているか、生存を知らせる ping、そしてチェーンが何をしたかについてのサーバーの見方です。クライアントがそこへ送る ものは、何の権限も与えず、結果も変えません。

クレーンの部屋が意図的な例外です。クレーンを操るのはフレームごとに 1 件の トランザクションになってしまうので、意図のほうがソケットに乗ります。そこでも継ぎ目は 狭くしてあります。動詞は三つ、どれも位置も時刻も判定も運ばず、操る側は、ブラウザが自分の 引きですでにチェーンに公開した原像によって承認されます。

接続する

エンドポイントValue
ソケットwss://minigame.picklechain.xyz/ws/<room>サイト自身の nginx がアップグレードを転送します
HTTPhttps://minigame.picklechain.xyz/api/同じプロセスで、接頭辞は剥がされます
自分で動かすスタックhttp://127.0.0.1:3008アーケードのサイト。サーバー自身はコンテナネットワーク上にあります
Originアップグレードの時点で検査されます設定された一覧にない Origin はハンドシェイクの前に 403 で拒否されるので、ソケットはそもそも開きません

/ws/ の下の知らないパスはアップグレードの時点で 404 として 拒否され、下の制限を超えた接続は 429 として拒否されます。どちらも WebSocket の ハンドシェイクより前に、生のソケット上の素の HTTP 応答として返ります。ソケットの error イベントしか扱わないクライアントには、ただの失敗として見えます。どちらか 知りたければステータスを読んでください。

六つの部屋

参加時
`hello`, then a full snapshot of the arena.

The snake arena. Steering is a transaction, so the socket carries no input for it.

参加時
`hello`, then the whole round: phase, time left, the commitment, every bet placed and every cash-out judged so far.

The curve. Bets are transactions.

参加時
`hello`, then the cabinet: what is left, where each capsule stands, the machine's constants and every attempt being driven.

The claw cabinet, and the ONLY room whose gameplay input rides this socket rather than the chain - a transaction per frame is not a game.

参加時
`hello`, then the machine: the bet ladder, the paytable, the meter and the spins still open.

The reels. A spin is a transaction; the socket watches it.

参加時
`hello`, then the table: every cell's multiplier, the current snapshot, the line so far and the bets still standing.

The board. Bets are transactions; the socket has one verb, and it buys nothing.

参加時
`hello`, then `stack`: every run being climbed with its rules state, the day's board and the rules' constants.

The tower. A start and every tap are transactions; the socket only listens, and says what the rules made of each tap.

参加時
`hello`, then `derby`: the current and next race, recent history, the rules and the paddock.

The race. A bet is a transaction; the socket only listens, and every runner is one of the chain's own oracle feeds.

参加時
`hello`, then `glide`: the day's board, every run in the air, the canyon cards and the rules.

The canyon. A start and every flap are transactions; the socket only listens, and the canyon it draws comes from a live price feed.

参加時
`hello` alone.

The lobby. Somebody who has not chosen a game sits here, and it is counted in the socket total.

どの部屋も同じ開始の手順で答えます。部屋の名前、遊んでいる人数、見ている人数、決済の運営者の アドレスを運ぶ hello フレーム、続いてその部屋の状態の完全なスナップショットです。 二つの人数はどちらもその部屋に対するものです。以前はどの部屋を求めてもアリーナの 数が返っていて、そこにいないプレイヤーを告知していました。

クライアントが送れるもの

watch

every room
形
{"t":"watch","account":"0x…"}

Says which account this socket is following, so the server can address one player directly - a refused join has to reach the person it was refused for. In the reels room it also arms that account.

知っておくとよいこと。 It AUTHORISES NOTHING. The account is not verified, the contract decides who may play, and the address must match a 40-hex pattern or the message is ignored in silence.

ping

every room
形
{"t":"ping","id":<any>}

Answered with `{t:"pong", id}`. The server also sends protocol-level pings and terminates a socket that stops answering them.

知っておくとよいこと。 This is not the liveness mechanism the server relies on; it is the one a client can observe.

形
{"t":"claim","tx":"0x…","secret":"0x…"} and three verbs after it

Takes the wheel of an attempt and drives it. The proof is a preimage: the hash of the secret must equal the seed the pull published on chain, so the only client that can drive an attempt is the one that made it - no signature, no challenge, no round trip.

知っておくとよいこと。 None of the verbs carries a position, a time or a verdict. A wrong secret is refused and counted and never logged. A reconnect takes the wheel from the previous socket, which is told so.

quote

the trellis room only
形
{"t":"quote", …}

Asks the operator to sign the price already printed in a square.

知っておくとよいこと。 It carries no stake and no account, and the contract checks the clock, the lock and the float itself. A flood of them costs this process signatures and nothing else.

javascript
const socket = new WebSocket("wss://minigame.picklechain.xyz/ws/crash");

socket.addEventListener("open", () => {
  // Tell the server which account to address directly. It verifies nothing:
  // this only decides who a per-player frame is sent to.
  socket.send(JSON.stringify({ t: "watch", account: myAddress }));
});

socket.addEventListener("message", (event) => {
  const frame = JSON.parse(event.data);
  switch (frame.t) {
    case "hello": /* room, playing, watching, operator */ break;
    case "full":  /* the whole room, sent once after hello */ break;
    case "chain": /* resyncing, and the range of mini-blocks lost, if any */ break;
    default:      /* IGNORE. The frame list is not versioned and rooms gain frames. */
  }
});

サーバーが送るもの

Every frame is a JSON object with a `t` discriminator. `hello` arrives first and carries the room, the number playing, the number watching and the operator's address; a full snapshot follows. After that the room pushes what changed.

Across the eight rooms: chain, tick, state, full, bet, settled, settle-failed, refused, refunded, refund-due, fulfil-failed, pull, claimed, released, blocked, armed, quote, feed, unsealed, void, race, started, finished, paddock, board, run, points, flight, over, cancelled, canyons, eat, death, pong.

チェーンの欠落は告知され、その意味は部屋ごとに違います

chain フレームは resyncing と、読み取り側が取得できなかった ミニブロックの範囲を運びます。すべての部屋にブロードキャストされ、それぞれが違う扱いを します。失われた範囲と重なったラウンドは、勝者の一覧が全員であると告げるのではなく、 短いかもしれないと述べます。クレーンの部屋は失われた範囲を自分のログへ送り、その中の 賭け金が見つけられて払い戻せるようにします。このフレームを無視するクライアントは、 不完全なものの絵を自信たっぷりに描くことになります。

HTTP のルート

Params
none
戻り値
{ok, loop:{p50Ms, p99Ms, maxMs, windowMs}}

Liveness, plus the event loop's recent delay. Eight rooms, a physics step and every socket's fan-out share one loop, so a loop that stalls stalls every quote, every echo and every frame at once.

知っておくとよいこと。 This is the figure that answers `the arcade is slow sometimes`. It is measured over ten-second windows and reset each time.

Params
none
戻り値
{ok, reasons, miniBlock, appliedMiniBlock, appliedLagMs, resyncing, lost}

503 when the socket to the node is down, or when the last APPLIED mini-block is more than five seconds old.

知っておくとよいこと。 It measures what was applied, not what arrived. Measuring arrival meant a wedged dispatch chain answered 200 while no input had been applied for minutes, which is the exact failure this probe exists to catch.

Params
none
戻り値
{total:{playing, sockets, spectators}, arena, crash, claw, patch, trellis, stack, derby, glide, …}

The whole arcade in one object: a summary per game and three numbers for the chrome every page draws. `playing` means somebody with something at stake right now, and each game measures that its own way.

知っておくとよいこと。 PUBLIC AND UNAUTHENTICATED, which decides what may appear on it. The crash summary withholds its point until the curve has stopped, and the claw summary carries the published commitment and never a link beneath it - one link is the outcome of the next pull nobody has made.

Params
none
戻り値
{supply, remaining, claimed, cells, price, pullsPerAccount, commit, chainLeft, blocked, machine}

The cabinet on its own, so a page can state what is left without opening a socket.

知っておくとよいこと。 503 when the claw room is not running.

Params
the pull id in the path
戻り値
one finished pull, whole

The seed the player published, every intent the server recorded, the tick the drop landed on and the verdict that came out of them - the arithmetic a reader can rerun.

知っておくとよいこと。 A pull still being driven is a 404, deliberately: its trace is not finished and its link is not revealed. A revealed link is served only because the fulfilment that finished the pull already put it on chain.

Params
none
戻り値
{playing, watching, day, pot, top, leaders}

Stack's day without a socket: the UTC day, its pot in wei, the chain's own top three and the tallest runs this server has finished today.

知っておくとよいこと。 503 when the stack room is not running. The pot and the top three are read off the contract once a minute and after each finish, so they can trail a finish by a moment.

Params
the run id in the path
戻り値
one run, whole: {id, a, t0Us, startMini, taps:[{us, mini, hash}], results, height, why, finished, rules}

Every tap of a run with the mini-block that carried it and what the rules made of it - the arithmetic a reader reruns with arcade-server/src/stack.js to check the height the operator reported.

知っておくとよいこと。 A 404 for a run this process never saw or has already let go of; it keeps the last five hundred. The chain still holds every tap, and the rules still apply to them.

Params
none
戻り値
{playing, watching, k, phase, card, unsettled, current, next, history, rules}

Derby's board for a page that has no socket yet: the current race and the next one, recent history, and the rules (the feed allowlist, the wall and night cards, the stake bounds and the seed).

知っておくとよいこと。 503 when the derby room is not running. `unsettled` counts bets still waiting on a settle, across every race this process still holds.

Params
the race number in the path
戻り値
one race, whole: {k, state, card, gate, line, runners, handicaps, pools, startPrices, startRounds, lineRounds, roundsFrom, scores, winners, why, hashes, bets}

The lineup, the handicaps, the rounds both seals named, the scores, the winners and every bet - the arithmetic a reader reruns with arcade-server/src/derby.js against the same public rounds.

知っておくとよいこと。 A race this process never opened is a 404. `roundsFrom` says whether each end's rounds came from this room sealing them or were recomputed from the tape after a restart - the proof is the same either way.

Params
none
戻り値
{playing, watching, day, pot, top, leaders, canyons}

Glide's board for a page that has no socket yet, with every canyon's card alongside the day's pot and its leaders.

知っておくとよいこと。 503 when the glide room is not running.

Params
the run id in the path
戻り値
one run, whole: {id, a, feed, symbol, r0, sigma, startUs, startMini, startHash, answers, flaps, result, rules}

r0, the rounds of the canyon this run flew, and every flap with the mini-block that carried it - the arithmetic a reader reruns with arcade-server/src/glide.js `replay` to get the same score.

知っておくとよいこと。 A run this process never saw or has already let go of is a 404.

四つとも認証がなく、それがそこに何を載せてよいかを決めています。終わっていないラウンドを 漏らすものは何も公開されません。クラッシュ地点は曲線が止まるまで伏せられ、クレーンの ハッシュ連鎖はそのコミットメントとしてのみ公開されます。/fair で連結が 明かされるのは、その引きを終わらせた履行がすでにそれをチェーンに載せたからにすぎません。

制限と、それを壊すもの

制限Value
Sockets256 total, 8 per addressthe upgrade is refused 429 before the handshake; the slot is released when the raw socket closes, not when the handshake succeeds
Originan allowlistthe upgrade is refused 403, so a page on another origin cannot drive this socket with a visitor's network
Frame size4 KiBtwo orders of magnitude above the largest real message; the library's default is 100 MiB
Inbound rate20 a second, burst 4045 and 90 in the claw room, because its driving is on this socket and a hand working a pad passes ten changes a second without trying
Overflowthe socket is closedclosing is kinder than ignoring - and in the claw room it ends a paid attempt, which is why that allowance is the wider one
Backpressure512 KiB buffereda socket further behind than that is cut and terminated rather than written to; a client that never reads would otherwise grow the process's heap without limit
Livenessa ping every half minuteanything that has not answered by the next one is terminated

遅い読み手はバッファされるのではなく切断されます

ライブラリは書き出せなかったものを、このプロセスのヒープに、上限なくためます。だから 0.5 メガバイト以上遅れたソケットは、また書き込まれるのではなく切って終わらせられます。 ソケットを開いて読まないクライアントが数個あるだけで、そうしなければコンテナを 使い果たせてしまい、鍵を持つプロセスにとってそれは遠隔からの停止です。0.5 メガバイトは、 このサーバーが送る最大のフレームおよそ 50 個ぶんで、読んでいるクライアントがそこまで 遅れることはありません。

メッセージの予算はクレーンの部屋でだけ広く、それは意図的です

ほかの部屋では、クライアントは数秒ごとに ping を送るだけでほかは何も送らないので、 毎秒 20 通は壊れたクライアントか敵対的なクライアントでしかありえません。クレーンの操縦は 意図が変わるたびに 1 通を送り、押すのと離すのは 2 通です。両手でパッドを操るプレイヤーは 苦もなく毎秒 10 通を超えます。そのソケットを切ることは、試行の最中の誰かからハンドルを 取り上げることであり、その人が支払った試行は手すりに当たったまま終わります。だからそこの 上限は、手が出せる量のところではなく、その上に置いてあります。