Caputchin
Marketplace-Spiel-Entwicklung

Spiel-SDK-Referenz

@caputchin/game-sdk ist der Vertrag, gegen den ein Spiel-Autor schreibt: ein register-Helfer, eine Push-only-Bridge, der Pro-Runde-GameContext und die TypeScript-Typen für die Preset-Blöcke des Manifests. Es wird separat vom nutzergewandten Widget veröffentlicht, sodass Spiele die Widget-Runtime nie transitiv bündeln. Hauseigene Spiele nutzen dieselbe öffentliche API; es gibt keinen privaten Vertrag.

Für einen geführten Build sieh dir Ein Marketplace-Spiel bauen an. Für das Manifest, das die Presets deklariert, die dieses SDK zum Vorschein bringt, sieh dir Das caputchin.json-Manifest an.

register(factory)

Der einzelne Registrierungs-Einstiegspunkt. Übergib deine Spiel-Factory; du übergibst das Manifest nicht (der Server liest caputchin.json zur Index-Zeit und liefert aufgelöste Presets als Kontext der Factory hinunter). Zweimal zu registrieren loggt eine Konsolen-Warnung und der letzte Schreibvorgang gewinnt; es gibt keine Plattform-Durchsetzung.

register(factory: GameFactory): void

Die Spiel-Factory

type GameFactory = (
  container: HTMLElement,
  bridge: Bridge,
  ctx?: GameContext,
) => (() => void) | void
ParameterBedeutung
containerEin Element im gesandboxten Iframe des Spiels. Render hier. Style ist natürlich beschränkt, der Iframe ist sein eigenes Dokument.
bridgeDer Push-only-Kanal zum Host (siehe Die Brücke). Das Spiel emittiert aufwärts; es subscribt nicht. Dass die Factory aufgerufen wird, ist das Startsignal.
ctxDer Pro-Runde-Kontext (Seed plus aufgelöste Presets). Optional im Typ, weil das Widget ein Spiel außerhalb einer verifizierten Session laufen lassen kann.
returnEine optionale Cleanup-Funktion, die das Widget aufruft, wenn es dein Spiel unmountet.

Die Brücke

Die Brücke ist Push-only: das Spiel meldet Events an den Host und lauscht nie. Ihre Member:

MemberFormEffekt
passpass(result: { trace: string }): voidSignalisiert, dass die Runde bestanden wurde, und reicht dem Host den opaken trace. Der Server lässt das run des Spiels unter dem ausgegebenen Seed erneut laufen, um das maßgebliche Urteil zu berechnen, also meldet das Spiel hier keine Punktzahl. Der erste Aufruf gibt die gehaltene Verifizierung frei und sperrt das Token; spätere Aufrufe werden ignoriert.
errorerror(err: { code: string; message?: string }): voidDas Spiel selbst schlug fehl (Asset-Load, interne Exception). Bringt ein error-Event zum Nutzer zum Vorschein. Verschieden von einem verlierenden Spieler, der signalisiert wird, indem nichts aufgerufen wird.
setSizesetSize(width: number, height: number): voidDen Iframe explizit auf den Inhalt anpassen. Ruf nach dem ersten Paint; das Widget misst auch das erste Kind des Spiels automatisch, also brauchen die meisten Spiele das nie.
layoutreadonly layout: 'inline' | 'modal' | 'fullscreen' | nullDie aufgelöste Präsentation, unter der das Spiel läuft, oder null, wenn unbekannt.

Es gibt bewusst kein complete, keinen start-Listener, keine unmount-Methode (gib stattdessen eine Cleanup-Funktion zurück) und kein Fehler-Signal über Stille hinaus.

Der Kontext

interface GameContext {
  seed:   Seed          | null
  locale: ResolvedLocale | null
  skin:   ResolvedSkin   | null
  config: ResolvedConfig | null
}
FeldBedeutung
seedDer Pro-Runde-Replay-Seed (ein Seed aus dem Replay-Vertrag). Seede jede Spiel-Zufälligkeit daraus. null, wenn das Spiel außerhalb einer verifizierten Session läuft.
localeDas aufgelöste Sprach-Objekt: _lang (BCP-47), _direction, plus deine abgeflachten Text-Schlüssel. null, wenn dein Manifest keinen locales-Block ausliefert.
skinDer aufgelöste Skin: _theme plus deine abgeflachten Farb-/Asset-Schlüssel, Asset-URLs schon absolut. _theme ist immer der konkrete Modus, für den der Skin aufgelöst wurde, light oder dark (nie any), sodass ein theme-agnostisches Preset den tatsächlichen Modus des Besuchers meldet. null, wenn kein skins-Block.
configDie aufgelöste Konfiguration: deine abgeflachten typisierten Skalare. null, wenn kein configurations-Block.

Jedes ist null, wenn der entsprechende Manifest-Block abwesend ist, also stell immer einen eingebauten Fallback bereit.

Preset-Typen

Das SDK exportiert TypeScript-Typen zum Verfassen von caputchin.json (und den geteilten .caputchin/-Dateien) mit Typprüfung. Diese spiegeln die Manifest-Blöcke:

TypBeschreibt
GameManifestDas ganze caputchin.json. Autoren- + Indexer-Wahrheitsquelle; nie im Browser gelesen.
LocalePreset / ResolvedLocaleEin deklariertes Locale-Preset / das aufgelöste Objekt, das das Spiel empfängt.
SkinPreset / ResolvedSkin / SkinSchemaEntry / SkinValueTypeSkin-Presets, der aufgelöste Skin und das Pro-Schlüssel-Typ-Schema.
ConfigPreset / ResolvedConfig / ConfigSchemaEntry / ConfigValueTypeKonfigurations-Presets, die aufgelöste Config und das Pro-Schlüssel-Typ-Schema.
LocalesFile / SkinsFile / ConfigurationsFileDas Top-Level-Objekt jeder .caputchin/<axis>.json-Split-Datei.
MarketplaceMetadata / PreferredPresentationDer marketplace-Block und der preferred-Footprint-Hinweis.

Verfass eine Split-Datei mit einem satisfies-Import, sodass der Compiler sie prüft:

import type { LocalesFile } from "@caputchin/game-sdk";
export default { schema: { /* ... */ }, presets: { /* ... */ } } satisfies LocalesFile;

Was bewusst abwesend ist

  • Keine sessionId oder Plattform-Identifier erreichen das Spiel.
  • Keine start / pause / resume-Lebenszyklus-Haken; der Factory-Aufruf ist der Start.
  • Kein maxScore oder Plattform-Score-Bereich; die Punktzahl des Urteils ist, was auch immer dein run zurückgibt.

Der Vertrag ist bewusst winzig, sodass er über Jahre des Katalog-Wachstums stabil bleibt.

Siehe auch

Auf dieser Seite