개발자를 위한 문서 / 애플리케이션 API

Arcade 서버

다섯 게임을 위한 WebSocket 서버이며, 그 옆에 몇 개의 HTTP 라우트가 있습니다. 다섯 중 넷에서 클라이언트는 게임 입력을 전혀 보내지 않습니다. 입력은 트랜잭션이고, 소켓은 그것이 착지하는 것을 지켜보는 방법입니다.

API가 아니라 소켓

하나의 프로세스가 하나의 이벤트 루프에서 다섯 개의 게임 방과 로비를 돌리고, 노드로 가는 자기 소켓으로 체인을 읽으며, 그 결과를 연결된 모든 클라이언트에 부채처럼 펼칩니다. 인터페이스는 WebSocket입니다. HTTP 라우트는 헬스 체크와, 사이트의 공통 요소가 모든 페이지에 그리는 두 수치를 위해 존재합니다.

게임 입력은 프레임이 아니라 트랜잭션입니다

아레나에서의 조종, 곡선 위의 베팅, 스핀, 보드 위의 베팅 - 전부 플레이어가 컨트랙트로 보내는 트랜잭션입니다. 이 소켓은 여러분이 누구를 보고 있는지, 살아 있음을 알리는 핑, 그리고 체인이 무엇을 했는지에 대한 서버의 시각을 실어 나릅니다. 클라이언트가 그것으로 보내는 어떤 것도 무엇을 허가하거나 결과를 바꾸지 않습니다.

크레인 방은 의도된 예외입니다. 크레인을 모는 것은 프레임마다 트랜잭션 하나가 될 테니, 의도는 소켓을 탑니다. 거기서도 이음매는 좁습니다. 동사는 셋이고 어느 것도 위치나 시간이나 판정을 실어 나르지 않으며, 조종자는 브라우저가 자기 당기기에서 이미 온체인에 발표한 프리이미지로 인가됩니다.

연결하기

엔드포인트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

느린 독자는 버퍼되는 것이 아니라 끊깁니다

라이브러리는 쓰지 못한 것을 이 프로세스의 힙에 한도 없이 버퍼합니다. 그래서 반 메가바이트 넘게 뒤처진 소켓은 다시 쓰이는 대신 잘리고 종료됩니다 - 소켓을 열어 놓고 읽지 않는 클라이언트 몇 개가 그러지 않으면 컨테이너를 고갈시킬 것이고, 키를 들고 있는 프로세스에서 그것은 원격 살해이기 때문입니다. 반 메가바이트는 이 서버가 보내는 가장 큰 프레임 쉰 개쯤에 해당합니다. 읽고 있는 클라이언트가 그만큼 뒤처지는 일은 없습니다.

메시지 예산은 크레인 방에서 더 넓으며, 그것은 의도된 것입니다

다른 곳에서 클라이언트는 몇 초마다 핑 하나를 보낼 뿐 그 밖에는 아무것도 보내지 않으므로, 초당 스무 개의 메시지는 망가졌거나 적대적인 클라이언트일 수밖에 없습니다. 크레인의 조종은 의도가 바뀔 때마다 메시지 하나를 보냅니다 - 누름과 뗌이 둘입니다 - 그리고 양손으로 패드를 다루는 플레이어는 애쓰지 않고도 초당 열 개를 넘깁니다. 그 소켓을 끊는 것은 시도 도중인 사람에게서 조종간을 빼앗는 일이고, 그가 돈을 내고 산 시도는 난간에 부딪힌 채 끝나 버립니다. 그래서 거기의 상한은 사람 손이 낼 수 있는 수준에 붙어 있는 것이 아니라 그 위에 있습니다.