@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +93 -0
- package/README.md +36 -2
- package/dist/bundling/logic-exports.d.ts +34 -0
- package/dist/bundling/node-builtins.d.ts +21 -0
- package/dist/bundling/transpiler.d.ts +7 -0
- package/dist/cli.js +27368 -0
- package/dist/client/dev-server.d.ts +59 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/stub-services.d.ts +32 -0
- package/dist/client/test-session.d.ts +195 -0
- package/dist/client.js +16884 -0
- package/dist/core/asset-reference-translator.d.ts +46 -0
- package/dist/core/clock.d.ts +57 -0
- package/dist/core/faults.d.ts +65 -0
- package/dist/core/game-settings.d.ts +65 -0
- package/dist/core/history.d.ts +89 -0
- package/dist/core/listen-with-fallback.d.ts +30 -0
- package/dist/core/mutation-queue.d.ts +61 -0
- package/dist/core/package-scenario.d.ts +89 -0
- package/dist/core/participant-registry.d.ts +109 -0
- package/dist/core/pending-changes.d.ts +14 -0
- package/dist/core/protocol.d.ts +83 -0
- package/dist/core/scenario-drafts.d.ts +32 -0
- package/dist/core/session-endpoints.d.ts +108 -0
- package/dist/core/session-gates.d.ts +75 -0
- package/dist/core/session-host.d.ts +94 -0
- package/dist/core/session-refusals.d.ts +56 -0
- package/dist/core/session-rewind.d.ts +32 -0
- package/dist/core/session-roster.d.ts +128 -0
- package/dist/core/session-surfaces.d.ts +32 -0
- package/dist/core/session-ticks.d.ts +18 -0
- package/dist/core/session-timers.d.ts +84 -0
- package/dist/core/session-writes.d.ts +40 -0
- package/dist/core/session.d.ts +385 -0
- package/dist/core/settings-locks.d.ts +22 -0
- package/dist/core/team-names.d.ts +52 -0
- package/dist/core/team-state-store.d.ts +209 -0
- package/dist/core/tick-timer.d.ts +75 -0
- package/dist/dev/blob-links.d.ts +10 -0
- package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
- package/dist/dev/console/index.html +12 -0
- package/dist/dev/console-path.d.ts +19 -0
- package/dist/dev/lockfile.d.ts +124 -0
- package/dist/dev/package-watcher.d.ts +21 -0
- package/dist/dev/routes.d.ts +67 -0
- package/dist/dev/shell.d.ts +260 -0
- package/dist/dev/surfaces.d.ts +41 -0
- package/dist/dev/tools-identity.d.ts +23 -0
- package/dist/dev/view/batch.d.ts +172 -0
- package/dist/dev/view/commands.d.ts +75 -0
- package/dist/dev/view/route.d.ts +58 -0
- package/dist/dev/view/view.d.ts +78 -0
- package/dist/dev/view/viewer.d.ts +32 -0
- package/dist/discovery/config.d.ts +9 -0
- package/dist/discovery/manifest-paths.d.ts +21 -0
- package/dist/discovery/removed-fields.d.ts +90 -0
- package/dist/docs/api/deck.md +386 -0
- package/dist/docs/api/index.md +67 -0
- package/dist/docs/api/sdk-commands.md +286 -0
- package/dist/docs/api/sdk-e2e.md +433 -0
- package/dist/docs/api/sdk-projection.md +207 -0
- package/dist/docs/api/sdk-testing.md +214 -0
- package/dist/docs/api/sdk-tools-client.md +560 -0
- package/dist/docs/api/sdk-ui-testing.md +231 -0
- package/dist/docs/api/sdk-ui.md +1269 -0
- package/dist/docs/api/sdk.md +2140 -0
- package/dist/docs/changelog-0.x.md +1156 -0
- package/dist/docs/changelog.md +236 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +154 -0
- package/dist/docs/cli-reference.md +2488 -0
- package/dist/docs/cli-simulation-testing.md +222 -0
- package/dist/docs/concepts/index.md +46 -0
- package/dist/docs/concepts/terminology.md +322 -0
- package/dist/docs/environment-variables.md +102 -0
- package/dist/docs/getting-started.md +145 -0
- package/dist/docs/guides/ai-facilitation.md +220 -0
- package/dist/docs/guides/capabilities.md +460 -0
- package/dist/docs/guides/endpoints.md +335 -0
- package/dist/docs/guides/engine-testing.md +477 -0
- package/dist/docs/guides/engine.md +527 -0
- package/dist/docs/guides/file-handling.md +405 -0
- package/dist/docs/guides/games.md +176 -0
- package/dist/docs/guides/global-settings.md +94 -0
- package/dist/docs/guides/guide.md +123 -0
- package/dist/docs/guides/hooks.md +183 -0
- package/dist/docs/guides/index.md +51 -0
- package/dist/docs/guides/internationalization.md +288 -0
- package/dist/docs/guides/llm-integration.md +635 -0
- package/dist/docs/guides/multi-team-games.md +586 -0
- package/dist/docs/guides/patterns.md +391 -0
- package/dist/docs/guides/presentation.md +196 -0
- package/dist/docs/guides/production-constraints.md +130 -0
- package/dist/docs/guides/scenario-editor.md +143 -0
- package/dist/docs/guides/scenarios.md +515 -0
- package/dist/docs/guides/session.md +972 -0
- package/dist/docs/guides/settings.md +458 -0
- package/dist/docs/guides/setup.md +193 -0
- package/dist/docs/guides/speech-generation.md +233 -0
- package/dist/docs/guides/starter-game.md +164 -0
- package/dist/docs/guides/testing.md +575 -0
- package/dist/docs/guides/tick-controller.md +569 -0
- package/dist/docs/guides/type-safety.md +516 -0
- package/dist/docs/guides/ui.md +1389 -0
- package/dist/docs/guides/upgrading.md +636 -0
- package/dist/docs/guides/validators.md +217 -0
- package/dist/docs/guides/waiting-room.md +244 -0
- package/dist/docs/index.md +41 -0
- package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
- package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
- package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
- package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
- package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
- package/dist/docs/recipes/create-capability.md +66 -0
- package/dist/docs/recipes/create-endpoint.md +127 -0
- package/dist/docs/recipes/create-engine.md +111 -0
- package/dist/docs/recipes/create-game.md +70 -0
- package/dist/docs/recipes/create-guide.md +75 -0
- package/dist/docs/recipes/create-hook.md +80 -0
- package/dist/docs/recipes/create-presentation.md +142 -0
- package/dist/docs/recipes/create-scenario-editor.md +102 -0
- package/dist/docs/recipes/create-scenario.md +98 -0
- package/dist/docs/recipes/create-session-ui.md +116 -0
- package/dist/docs/recipes/create-settings.md +88 -0
- package/dist/docs/recipes/create-setup.md +115 -0
- package/dist/docs/recipes/create-tick-controller.md +65 -0
- package/dist/docs/recipes/create-user-interface.md +96 -0
- package/dist/docs/recipes/create-validator.md +67 -0
- package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
- package/dist/docs/recipes/declare-api-uses.md +67 -0
- package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
- package/dist/docs/recipes/distribute-package.md +80 -0
- package/dist/docs/recipes/e2e-testing.md +116 -0
- package/dist/docs/recipes/end-game-on-condition.md +154 -0
- package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
- package/dist/docs/recipes/index.md +86 -0
- package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
- package/dist/docs/recipes/negotiate-across-teams.md +164 -0
- package/dist/docs/recipes/observe-other-teams-state.md +119 -0
- package/dist/docs/recipes/publish-a-package.md +139 -0
- package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
- package/dist/docs/recipes/review-a-past-day.md +73 -0
- package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
- package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
- package/dist/docs/recipes/show-all-teams-participants.md +109 -0
- package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
- package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
- package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
- package/dist/docs/recipes/test-user-interface.md +95 -0
- package/dist/docs/recipes/test-with-engine.md +115 -0
- package/dist/docs/recipes/upload-and-display-files.md +148 -0
- package/dist/docs/recipes/use-request-play.md +65 -0
- package/dist/docs/recipes/use-request-tick.md +75 -0
- package/dist/docs/troubleshooting.md +816 -0
- package/dist/execution/blob-inputs.d.ts +57 -0
- package/dist/execution/bundle-reload.d.ts +42 -0
- package/dist/execution/config-resolver.d.ts +127 -0
- package/dist/execution/configuration-builder.d.ts +66 -0
- package/dist/execution/context-composition.d.ts +161 -0
- package/dist/execution/endpoint-actions.d.ts +9 -0
- package/dist/execution/endpoint-payloads.d.ts +32 -0
- package/dist/execution/endpoint-runner.d.ts +129 -0
- package/dist/execution/engine-state.d.ts +40 -0
- package/dist/execution/executor-callbacks.d.ts +56 -0
- package/dist/execution/executor.d.ts +436 -0
- package/dist/execution/gates.d.ts +45 -0
- package/dist/execution/hook-chain.d.ts +25 -0
- package/dist/execution/logic-calls.d.ts +190 -0
- package/dist/execution/logic-failure.d.ts +84 -0
- package/dist/execution/logic-loader.d.ts +54 -0
- package/dist/execution/logic-result.d.ts +76 -0
- package/dist/execution/tick-controller-call.d.ts +32 -0
- package/dist/execution/types.d.ts +48 -0
- package/dist/generation/ts-type.d.ts +39 -0
- package/dist/guide/locale.d.ts +12 -0
- package/dist/guide/video-registry.d.ts +42 -0
- package/dist/help-examples.d.ts +125 -0
- package/dist/host/adapter.d.ts +67 -0
- package/dist/host/host-channel.d.ts +222 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/protocol.d.ts +783 -0
- package/dist/host/surfaces.d.ts +42 -0
- package/dist/host/theme.d.ts +55 -0
- package/dist/host.js +583 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/schema-builder.d.ts +29 -0
- package/dist/prompts/types.d.ts +108 -0
- package/dist/prompts/validators.d.ts +32 -0
- package/dist/runtime/capability-scope.d.ts +6 -0
- package/dist/runtime/context-merge.d.ts +29 -0
- package/dist/runtime/context-projection.d.ts +41 -0
- package/dist/runtime/guarded-callbacks.d.ts +15 -0
- package/dist/runtime/index.d.ts +27 -0
- package/dist/runtime/manifest-version.d.ts +55 -0
- package/dist/runtime/tick-validation.d.ts +25 -0
- package/dist/runtime.js +346 -0
- package/dist/schema.json +1546 -0
- package/dist/server/vite-plugin.d.ts +46 -0
- package/dist/services/tts/client.d.ts +51 -0
- package/dist/services/tts/elevenlabs.d.ts +24 -0
- package/dist/services/tts/types.d.ts +44 -0
- package/dist/storage/asset-resolver.d.ts +49 -0
- package/dist/storage/blob-store.d.ts +41 -0
- package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
- package/dist/templates/endpoint/logic.ts.liquid +25 -0
- package/dist/templates/engine/init.json.liquid +7 -0
- package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
- package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
- package/dist/templates/engine/logic.test.ts.liquid +61 -0
- package/dist/templates/engine/logic.ts.liquid +32 -0
- package/dist/templates/engine/run.json.liquid +7 -0
- package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
- package/dist/templates/guide/02-preparation.md.liquid +18 -0
- package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
- package/dist/templates/hook/logic.test.ts.liquid +46 -0
- package/dist/templates/hook/logic.ts.liquid +12 -0
- package/dist/templates/package/CLAUDE.md.template +338 -0
- package/dist/templates/package/DESIGN.md.liquid +144 -0
- package/dist/templates/package/README.md.liquid +143 -0
- package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
- package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
- package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
- package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
- package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
- package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
- package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
- package/dist/templates/package/claude/settings.json.template +16 -0
- package/dist/templates/package/gitignore.template +15 -0
- package/dist/templates/package/global-setup.ts.template +21 -0
- package/dist/templates/package/globals.d.ts.template +2 -0
- package/dist/templates/package/oxfmtrc.json.template +14 -0
- package/dist/templates/package/oxlintrc.json.template +238 -0
- package/dist/templates/package/package.json.liquid +65 -0
- package/dist/templates/package/playwright.config.ts.liquid +19 -0
- package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
- package/dist/templates/package/test-setup.ts.liquid +7 -0
- package/dist/templates/package/tsconfig.json.liquid +15 -0
- package/dist/templates/package/vite.config.ts.liquid +19 -0
- package/dist/templates/package/vitest.config.ts.liquid +25 -0
- package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
- package/dist/templates/presentation/index.html.liquid +20 -0
- package/dist/templates/presentation/src/App.tsx.liquid +49 -0
- package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
- package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
- package/dist/templates/presentation/src/main.tsx.liquid +14 -0
- package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
- package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
- package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
- package/dist/templates/session/src/App.test.tsx.liquid +60 -0
- package/dist/templates/session/src/App.tsx.liquid +128 -0
- package/dist/templates/session/src/i18n.ts.liquid +53 -0
- package/dist/templates/session/src/main.tsx.liquid +48 -0
- package/dist/templates/session/src/theme.ts.liquid +46 -0
- package/dist/templates/settings-editor/index.html.liquid +12 -0
- package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
- package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
- package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
- package/dist/templates/setup/src/App.tsx.liquid +102 -0
- package/dist/templates/setup/src/i18n.ts.liquid +47 -0
- package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
- package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
- package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
- package/dist/templates/starter-game/engine/defaults.ts +81 -0
- package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
- package/dist/templates/starter-game/engine/logic.test.ts +221 -0
- package/dist/templates/starter-game/engine/logic.ts +149 -0
- package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
- package/dist/templates/starter-game/engine/types.ts +139 -0
- package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
- package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
- package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
- package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
- package/dist/templates/starter-game/guide/guide.test.ts +108 -0
- package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
- package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
- package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
- package/dist/templates/starter-game/package.json +68 -0
- package/dist/templates/starter-game/playwright.config.ts +19 -0
- package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
- package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
- package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
- package/dist/templates/starter-game/session/defaults.ts +80 -0
- package/dist/templates/starter-game/session/index.html +12 -0
- package/dist/templates/starter-game/session/session.sdk.json +11 -0
- package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
- package/dist/templates/starter-game/session/src/App.tsx +309 -0
- package/dist/templates/starter-game/session/src/Section.tsx +37 -0
- package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
- package/dist/templates/starter-game/session/src/format.ts +8 -0
- package/dist/templates/starter-game/session/src/i18n.ts +51 -0
- package/dist/templates/starter-game/session/src/main.tsx +48 -0
- package/dist/templates/starter-game/session/src/theme.ts +45 -0
- package/dist/templates/starter-game/session/types.ts +135 -0
- package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
- package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
- package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
- package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
- package/dist/templates/starter-game/test-setup.ts +7 -0
- package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
- package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
- package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
- package/dist/templates/starter-game/tsconfig.json +19 -0
- package/dist/templates/starter-game/ui/defaults.ts +60 -0
- package/dist/templates/starter-game/ui/index.html +12 -0
- package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
- package/dist/templates/starter-game/ui/src/App.tsx +175 -0
- package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
- package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
- package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
- package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
- package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
- package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
- package/dist/templates/starter-game/ui/src/format.ts +12 -0
- package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
- package/dist/templates/starter-game/ui/src/main.tsx +48 -0
- package/dist/templates/starter-game/ui/src/menu.ts +18 -0
- package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
- package/dist/templates/starter-game/ui/src/theme.ts +45 -0
- package/dist/templates/starter-game/ui/src/writes.ts +43 -0
- package/dist/templates/starter-game/ui/types.ts +137 -0
- package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
- package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
- package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
- package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
- package/dist/templates/starter-game/vite.config.ts +19 -0
- package/dist/templates/starter-game/vitest.config.ts +15 -0
- package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
- package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
- package/dist/templates/starter-game/waiting_room/index.html +12 -0
- package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
- package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
- package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
- package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
- package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
- package/dist/templates/starter-game/waiting_room/types.ts +59 -0
- package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
- package/dist/templates/tick-controller/logic.ts.liquid +8 -0
- package/dist/templates/ui/index.html.liquid +12 -0
- package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
- package/dist/templates/ui/src/App.tsx.liquid +103 -0
- package/dist/templates/ui/src/i18n.ts.liquid +47 -0
- package/dist/templates/ui/src/main.tsx.liquid +48 -0
- package/dist/templates/ui/src/theme.ts.liquid +46 -0
- package/dist/templates/validator/logic.test.ts.liquid +42 -0
- package/dist/templates/validator/logic.ts.liquid +21 -0
- package/dist/templates/waiting-room/index.html.liquid +12 -0
- package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
- package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
- package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
- package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
- package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
- package/dist/templating/resource-metadata.d.ts +131 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/atomic-write.d.ts +40 -0
- package/dist/utils/cli-error.d.ts +64 -0
- package/dist/utils/document-parser-client.d.ts +20 -0
- package/dist/utils/env-loader.d.ts +40 -0
- package/dist/utils/error-codes.d.ts +335 -0
- package/dist/utils/errors.d.ts +19 -0
- package/dist/utils/global-settings.d.ts +65 -0
- package/dist/utils/llm-client.d.ts +34 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/output.d.ts +109 -0
- package/dist/utils/platform.d.ts +14 -0
- package/dist/utils/prompt.d.ts +36 -0
- package/dist/utils/real-path.d.ts +6 -0
- package/dist/utils/schema-properties.d.ts +35 -0
- package/dist/validation/ajv.d.ts +17 -0
- package/dist/validation/facilitator-guide-validator.d.ts +27 -0
- package/dist/validation/findings.d.ts +40 -0
- package/dist/validation/game-constraints.d.ts +10 -0
- package/dist/validation/game-validator.d.ts +34 -0
- package/dist/validation/package-validation.d.ts +41 -0
- package/dist/validation/package-validator.d.ts +19 -0
- package/dist/validation/resource-validator.d.ts +79 -0
- package/dist/validation/schema-defaults-validator.d.ts +29 -0
- package/dist/validation/schema-validation.d.ts +115 -0
- package/dist/validation/semver.d.ts +9 -0
- package/package.json +122 -3
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
# Settings
|
|
2
|
+
|
|
3
|
+
Settings are a game's read-only parameters: a price, a difficulty, a round length, a menu, an answer key. This guide covers declaring them, reading them from logic and surfaces, overriding them per scenario and per session, and setting them from the CLI.
|
|
4
|
+
|
|
5
|
+
Not to be confused with [global settings](./global-settings.md), the file in your home directory that holds API keys for every project.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
A settings resource is a schema and nothing else. It lives at `settings/<identifier>/settings.sdk.json`, and a package may hold any number of them, as it does capabilities.
|
|
10
|
+
|
|
11
|
+
- **Every property declares a default.** The default is the value the game plays with unless a scenario or the session overrides it
|
|
12
|
+
- **Read-only.** No logic, surface or endpoint writes a setting. Values change only through the override layers, and only before play starts
|
|
13
|
+
- **Declared by identifier.** A resource lists the settings it reads in `settings`, exactly as `reads` names capabilities, and receives exactly those
|
|
14
|
+
- **Fixed when play starts.** Settings are resolved when a session is created on the platform, or once the configuration step completes on the dev server, and do not change during play
|
|
15
|
+
|
|
16
|
+
Settings replace the per-resource `configurationSchema` of earlier versions. If you are upgrading, see [step 2 of the upgrading guide](./upgrading.md#step-2-move-configuration-into-settings-resources).
|
|
17
|
+
|
|
18
|
+
## Declaring settings
|
|
19
|
+
|
|
20
|
+
Create a settings resource with the CLI. The JSON names the identifier, which becomes the directory name:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm run sdk new settings '{
|
|
24
|
+
"definition": {
|
|
25
|
+
"identifier": "demand",
|
|
26
|
+
"settingsSchema": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"BaseDemand": {
|
|
30
|
+
"type": "number",
|
|
31
|
+
"description": "Base demand per tick",
|
|
32
|
+
"default": 100,
|
|
33
|
+
"minimum": 0
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"additionalProperties": false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Run `pnpm run sdk new settings` with no JSON to be prompted for each property instead. Either way it writes `settings/demand/settings.sdk.json`, where the directory names the resource and the file carries no identifier:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"definition": {
|
|
47
|
+
"settingsSchema": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"BaseDemand": {
|
|
51
|
+
"type": "number",
|
|
52
|
+
"description": "Base demand per tick",
|
|
53
|
+
"default": 100,
|
|
54
|
+
"minimum": 0
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"additionalProperties": false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`settingsSchema` is a full JSON Schema Draft 7 object. Two rules are stricter than for a capability's `variablesSchema`:
|
|
64
|
+
|
|
65
|
+
- Every property needs a `default`. `new settings`, `update settings` and `sdk build` all refuse a property without one
|
|
66
|
+
- Every default must fit its own schema, at any depth. `sdk build` refuses a string default on a number property, a default outside its range or `enum`, and an object default missing a key its own `required` lists, naming the settings resource and the path. The generated `Settings` type comes from the schema, so the compiler cannot catch a default that disagrees with it
|
|
67
|
+
- A top-level `required` is refused, because every property always has a value. For a value that may be absent, use `"type": ["string", "null"]` with `"default": null`
|
|
68
|
+
|
|
69
|
+
A `required` inside an object-valued property is allowed. It holds the property's `default` to its keys, never a scenario or a setup change: those are partial at every depth, so a scenario or a setup UI may set one key of the object and leave the rest to the layer beneath. An array is the exception: it replaces the one beneath whole, so each item an override supplies must be complete.
|
|
70
|
+
|
|
71
|
+
### Describe every setting
|
|
72
|
+
|
|
73
|
+
A settings schema is read by more than the build. A hosted runtime's generic settings form, a [scenario editor](./scenario-editor.md), and an AI agent authoring a scenario for your released game all work from it, and an agent can edit a game with no editor UI at all if the schema says what each value means. Give every property:
|
|
74
|
+
|
|
75
|
+
- A `title` and a `description` saying what the value is and what changing it does
|
|
76
|
+
- Its units, in the description or title ('in pounds', 'per tick', 'as a fraction of revenue')
|
|
77
|
+
- A range, with `minimum` and `maximum`, wherever one exists
|
|
78
|
+
- An `enum` where the choices are closed
|
|
79
|
+
|
|
80
|
+
A `title` on a value is safe to write: the generated `types.ts` ignores it, so any wording works, 'Settings' and 'Context' included. Only a `title` on an object names a type there.
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
"DemandPriceModifier": {
|
|
84
|
+
"type": "number",
|
|
85
|
+
"title": "Price sensitivity",
|
|
86
|
+
"description": "Units of demand lost for each pound added to the price",
|
|
87
|
+
"minimum": 0,
|
|
88
|
+
"maximum": 5000,
|
|
89
|
+
"default": 500
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Lock a setting
|
|
94
|
+
|
|
95
|
+
Some values are part of the game's design rather than something to tune: a constant the scoring depends on, or a rule every edition shares. Mark the property `readOnly` and only your default applies:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
"LowProductionThreshold": {
|
|
99
|
+
"type": "integer",
|
|
100
|
+
"title": "Low production threshold",
|
|
101
|
+
"description": "Units per round below which the price rises. Fixed by the game",
|
|
102
|
+
"default": 80,
|
|
103
|
+
"readOnly": true
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- **Nothing can change a locked value.** The build refuses a scenario that sets it, with `SETTINGS_LOCKED` pointing at the value, even when it sets the same value as your default. A setup screen or scenario editor that changes it is refused as `denied`, with a message naming the setting (`"engine.LowProductionThreshold" is read-only`), and none of that write applies
|
|
108
|
+
- **Sending it back unchanged is fine.** An editor that spreads the settings it was given sends the locked value back as it was, and only what changed is applied, so spreading is still safe
|
|
109
|
+
- **Locking an object locks everything in it.** `readOnly` on an object-valued property fixes every key inside it, at any depth
|
|
110
|
+
- **Saving a scenario leaves locked values out.** The dev console's Save writes every other setting and drops the locked ones, so the scenario still builds
|
|
111
|
+
- **Editors are not sent the schema.** A setup screen or scenario editor receives the values, not the schema, so show a locked field as read-only yourself, as the factory example's scenario editor does
|
|
112
|
+
- **Adding a lock is a scenario-format change.** A custom scenario saved before the lock may hold a value for it, so bump `scenarioFormat` when you lock a setting a released version left open (see [Scenario format: when to bump it](./scenarios.md#scenario-format-when-to-bump-it))
|
|
113
|
+
|
|
114
|
+
### How to divide settings
|
|
115
|
+
|
|
116
|
+
Split settings by who reads them rather than keeping one per resource. A package may hold one `engine` settings resource read only by the engine, or several named by topic (`pricing`, `documents`, `answers`) that several resources share. The starter game does both: its `menu` settings, the prices and the stall's capacity, are read by the engine, every surface, a hook and a validator, while each item's usual demand sits in an `engine` resource only the engine reads. Several resources may declare the same settings resource, and each receives the same values, so a surface never needs a copy of a value the engine already has.
|
|
117
|
+
|
|
118
|
+
## Reading settings
|
|
119
|
+
|
|
120
|
+
List the settings a resource reads in its bundle's `settings` array. Every resource that runs logic or renders a surface may do so: the engine, `ui`, `session`, `waiting_room`, `presentation`, `tick_controller`, hooks, validators, and endpoints.
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"definition": {
|
|
125
|
+
"reads": ["price"],
|
|
126
|
+
"writes": ["sales"],
|
|
127
|
+
"settings": ["demand"]
|
|
128
|
+
},
|
|
129
|
+
"logic": {
|
|
130
|
+
"directory": ".",
|
|
131
|
+
"entrypoint": "logic.ts"
|
|
132
|
+
},
|
|
133
|
+
"rootDirectory": "."
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Add it by hand, or with `pnpm run sdk update engine --patch '[{"op":"add","path":"/definition/settings","value":["demand"]}]'`. `new engine`, `new ui` and the other interactive `new` commands ask which settings the resource reads. The build fails on an identifier the package does not declare.
|
|
138
|
+
|
|
139
|
+
Then run `pnpm run generate`. The resource's generated `types.ts` gains a `Settings` type keyed by settings identifier, and its `defaults.ts` a `defaultSettings` holding the defaults, for use in tests.
|
|
140
|
+
|
|
141
|
+
### In logic
|
|
142
|
+
|
|
143
|
+
The engine, hooks, validators, the tick controller and endpoints receive `settings` beside `context`, keyed by settings identifier:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import type { RunFunction } from '@seriousgaming/sdk';
|
|
147
|
+
import type { Context, Settings } from './types';
|
|
148
|
+
|
|
149
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, settings }) => {
|
|
150
|
+
const { BaseDemand } = settings.demand;
|
|
151
|
+
return {
|
|
152
|
+
capabilities: {
|
|
153
|
+
...context,
|
|
154
|
+
sales: { ...context.sales, Sales: BaseDemand - context.price.Price * 2 },
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A hook reads `settings` the same way (`({ context, settings, participant })`), a validator's `UpdateData` and `RequestTick` receive it beside `actor`, and an endpoint's `Handle` receives it beside `request` and `caller`. A resource that declares no settings receives `{}`.
|
|
161
|
+
|
|
162
|
+
### In a surface
|
|
163
|
+
|
|
164
|
+
A game UI, session UI and waiting room UI read `settings` from their data hook:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { useUserInterfaceData } from '@seriousgaming/sdk/ui';
|
|
168
|
+
import type { Context, Settings } from '../types';
|
|
169
|
+
|
|
170
|
+
function PricePanel() {
|
|
171
|
+
const { data, settings } = useUserInterfaceData<Context, Settings>();
|
|
172
|
+
const { MinPrice, MaxPrice } = settings.ui;
|
|
173
|
+
// ...
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
A session UI uses `useSessionUserInterfaceData<SessionData, Settings>()`. The host delivers a surface's settings once on load, in the `setSettings` message, rather than with every context write.
|
|
178
|
+
|
|
179
|
+
In component tests, pass them to `renderUserInterface` or `renderSessionUserInterface`, keyed the same way:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
renderUserInterface(<App />, {
|
|
183
|
+
data: { price: { Price: 5 } },
|
|
184
|
+
settings: { ui: { MinPrice: 1, MaxPrice: 500 } },
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
In unit tests of logic, spread the generated defaults and override only what the case is about:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import { defaultContext, defaultSettings } from './defaults';
|
|
192
|
+
|
|
193
|
+
const settings: Settings = {
|
|
194
|
+
...defaultSettings,
|
|
195
|
+
demand: { ...defaultSettings.demand, BaseDemand: 500 },
|
|
196
|
+
};
|
|
197
|
+
const { capabilities } = await Run({ context: defaultContext, settings, meta, callbacks });
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Reading settings rather than copying them
|
|
201
|
+
|
|
202
|
+
Before settings resources, a surface could not see the engine's settings, so games copied it into a capability at `Init`. That copy is per team, stored in every snapshot and re-sent on every write, and it goes stale if the configuration changes. Declare the settings resource on the surface instead, and delete the copy.
|
|
203
|
+
|
|
204
|
+
Keep a value in a capability only when it changes during play, or when it is computed from settings and several readers need the result.
|
|
205
|
+
|
|
206
|
+
## The game settings
|
|
207
|
+
|
|
208
|
+
Every game has one built-in settings resource, `game`, holding the game-level settings: how many ticks (rounds) it plays, how many teams a session may hold, how many participants a team is meant for, what the teams are called to begin with, and whether the waiting room opens with the session. It is a settings resource like any other, with two differences: the SDK owns its schema, and the game sets its values in `seriousgaming.sdk.json` rather than in `settings/game/`.
|
|
209
|
+
|
|
210
|
+
| Setting | The game's value, in `seriousgaming.sdk.json` | What it does |
|
|
211
|
+
| --------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
212
|
+
| `maxTicks` | `maxTicks`, or `null` for no fixed limit | The game is complete once this many ticks have run |
|
|
213
|
+
| `teams.min`, `teams.max` | `teams.min` and `teams.max`, or `null` for no bound | The fewest teams play can start with, and the most a session can hold |
|
|
214
|
+
| `teams.perTeam.minParticipants`, `.maxParticipants` | `teams.perTeam`, or `null` | Guidance on team size shown to the facilitator, never a limit on joining |
|
|
215
|
+
| `teams.defaultNames` | `teams.defaultNames`, or `[]` | The names teams start with, by team index. A facilitator or the team can rename |
|
|
216
|
+
| `waitingRoom.openOnStart` | `waitingRoom.openOnStart`, or `false` | Whether a new session starts with participants held in the waiting room |
|
|
217
|
+
|
|
218
|
+
`teams.mode` stays in the manifest and is not a setting: a game is independent or shared-state, and nothing can change that.
|
|
219
|
+
|
|
220
|
+
Override the game settings as you would any other, under `game`:
|
|
221
|
+
|
|
222
|
+
- **A scenario** sets them in its `settings`, for example `"settings": { "game": { "maxTicks": 6 } }` for a six-round edition
|
|
223
|
+
- **Setup** sets them for one session when it lists `game` in `configures`. A setup screen asking 'how many rounds today?' writes `updateSettings({ game: { ...settings.game, maxTicks: rounds } })`
|
|
224
|
+
- **`sdk configure`** sets them on the dev server: `pnpm run sdk configure '{"game":{"maxTicks":6}}'`
|
|
225
|
+
- **Any resource can read them** by listing `game` in its `settings`, and its generated `types.ts` types them
|
|
226
|
+
|
|
227
|
+
Two rules are particular to these settings:
|
|
228
|
+
|
|
229
|
+
- **`waitingRoom.openOnStart` is set by the game or a scenario, never by setup.** It takes effect as the session is created, before setup runs, so a setup value would do nothing, and is refused
|
|
230
|
+
- **Play cannot start outside the team bounds.** Setup can change `teams.min` and `teams.max` after the teams were created, so play is refused while the team count is outside them. Add or remove teams, or change the setting, and start again
|
|
231
|
+
|
|
232
|
+
A settings resource of your own may not be called `game`, and is refused with a message saying what to change.
|
|
233
|
+
|
|
234
|
+
### Narrow or lock the game settings
|
|
235
|
+
|
|
236
|
+
You own your own settings' schemas, but the SDK owns this one, so you narrow it in `seriousgaming.sdk.json` instead, with `gameSettingsConstraints`. It follows the shape of the `game` settings, and each entry takes only `minimum`, `maximum`, `enum` and `readOnly`:
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"maxTicks": 8,
|
|
241
|
+
"teams": { "mode": "independent", "min": 2, "max": 6 },
|
|
242
|
+
"gameSettingsConstraints": {
|
|
243
|
+
"maxTicks": { "minimum": 4, "maximum": 10 },
|
|
244
|
+
"teams": { "max": { "readOnly": true } }
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
This game plays four to ten rounds, eight unless a scenario or setup chooses otherwise, and its six-team ceiling is fixed. The build merges the constraints into the `game` settings schema it emits, so a scenario, setup and `sdk configure` are held to them exactly as they are to your own settings' `minimum`, `maximum` and `readOnly`.
|
|
250
|
+
|
|
251
|
+
- **A constraint can only narrow.** The build refuses, with `GAME_CONSTRAINT_INVALID`, a `minimum` below the SDK's own (every count starts at 1), a `minimum` above its `maximum`, a bound on a value that is not a number, and an `enum` value the setting could never hold. An unknown setting or keyword is refused like any other manifest typo
|
|
252
|
+
- **Your own values must meet them.** A `maxTicks` of 12 with a `maximum` of 10 is refused with `GAME_CONSTRAINT_VIOLATED`, pointing at `maxTicks`
|
|
253
|
+
- **A bound rules out 'no limit'.** With a `minimum` or `maximum`, `null` is no longer allowed, so set the value in the manifest. The generated `types.ts` types it as a number
|
|
254
|
+
- **Adding or tightening a constraint is a scenario-format change**, as adding a lock is
|
|
255
|
+
|
|
256
|
+
A game is single-team only when it declares `teams.max: 1` and locks it:
|
|
257
|
+
|
|
258
|
+
```json
|
|
259
|
+
{
|
|
260
|
+
"teams": { "mode": "independent", "max": 1 },
|
|
261
|
+
"gameSettingsConstraints": { "teams": { "max": { "readOnly": true } } }
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
A `teams.max` of 1 that is not locked could be raised by a scenario or setup, so the build treats it as multi-team, as it treats an omitted `max`. That matters for an independent game whose engine writes a shared capability: the build allows it only in a single-team game, and its message says to lock `teams.max` when the value is already 1.
|
|
266
|
+
|
|
267
|
+
## Keeping a value off participant screens
|
|
268
|
+
|
|
269
|
+
A resource receives only the settings it declares, so the way to keep a value from participants is to put it in a settings resource that no participant surface declares. An answer key, a hidden target or a scoring rubric goes in its own resource, for example `settings/answers/`, listed only in the engine's `settings`:
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"definition": {
|
|
274
|
+
"reads": ["submission"],
|
|
275
|
+
"writes": ["score"],
|
|
276
|
+
"settings": ["rules", "answers"]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The game UI lists `"settings": ["rules"]` and never receives `answers`, not even in its browser's memory. An endpoint that marks a submission can declare `answers` too, since it runs on the server.
|
|
282
|
+
|
|
283
|
+
## Overriding settings
|
|
284
|
+
|
|
285
|
+
This is the one place the resolution order is described; other pages link here.
|
|
286
|
+
|
|
287
|
+
A settings resource's values resolve through three layers, each overriding the one before:
|
|
288
|
+
|
|
289
|
+
1. **Defaults**, the `default` of each property in its `settingsSchema`
|
|
290
|
+
2. **The active scenario**, from the scenario's `settings`, under the settings identifier. With no scenario chosen, the one marked `default` is active, and with none marked, this layer is empty
|
|
291
|
+
3. **The session**, from the game's setup screen on the platform, or `sdk configure` on the dev server, under the settings identifier
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
settings defaults → scenario overrides → session overrides (wins)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
- **Every layer is partial.** It names only the properties it changes, and every property it leaves out falls through to the layer beneath
|
|
298
|
+
- **Layers merge key by key.** A property a layer names replaces the value beneath it, and within an object-valued setting each key it names replaces that key alone, at any depth, so `{ "game": { "teams": { "max": 4 } } }` keeps the other team settings. An array is a single value, replaced whole
|
|
299
|
+
- **There is no game-level layer.** To change a value for every session, edit its default
|
|
300
|
+
- **Resolution happens before play starts.** The values are fixed for the session, and `Init` already receives them resolved
|
|
301
|
+
- **A resource receives only what it declares.** An override under `engine` reaches every resource that lists `engine` in its `settings`, and no other
|
|
302
|
+
|
|
303
|
+
A surface shown outside a session, such as a presentation with no session running, reads the first two layers only.
|
|
304
|
+
|
|
305
|
+
### In a scenario
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"definition": {
|
|
310
|
+
"locales": {
|
|
311
|
+
"en": { "name": "Hard Mode" }
|
|
312
|
+
},
|
|
313
|
+
"settings": {
|
|
314
|
+
"demand": { "BaseDemand": 5000 },
|
|
315
|
+
"session": { "NoticeboardLabel": "Crisis bulletin" }
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
"rootDirectory": "."
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
See [Scenarios](./scenarios.md) for the rest of a scenario. Because hooks and validators read settings resources too, a scenario can now configure them, which it could not before.
|
|
323
|
+
|
|
324
|
+
### In setup or the scenario editor
|
|
325
|
+
|
|
326
|
+
The `setup` and `scenario_editor` slots each list the settings they edit in `configures`, and are seeded with the resolved values of exactly those, keyed by settings identifier:
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"definition": {
|
|
331
|
+
"configures": ["engine", "ui"]
|
|
332
|
+
},
|
|
333
|
+
"rootDirectory": ".",
|
|
334
|
+
"ui": {
|
|
335
|
+
"entrypoint": "index.html"
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
What setup changes lands in the session layer; what the scenario editor produces is saved as a scenario. See [Building a setup screen](./setup.md) and [Building a scenario editor](./scenario-editor.md).
|
|
341
|
+
|
|
342
|
+
### From the CLI
|
|
343
|
+
|
|
344
|
+
`sdk configure` sets the session layer on the dev server, keyed by settings identifier. The keys below are for a game whose engine reads a settings resource named `engine`, with `BaseDemand` and `CostPerUnit` properties: use your own.
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
cat > config.json <<EOF
|
|
348
|
+
{
|
|
349
|
+
"engine": {
|
|
350
|
+
"BaseDemand": 15000,
|
|
351
|
+
"CostPerUnit": 2
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
EOF
|
|
355
|
+
|
|
356
|
+
pnpm run sdk configure -f config.json
|
|
357
|
+
pnpm run sdk tick
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Or inline:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
pnpm run sdk configure '{"engine":{"BaseDemand":15000}}'
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Each call replaces the previous session overrides rather than adding to them, and restarts the session on the new values, because settings are fixed once play starts. Every property it does not name keeps its default or scenario value.
|
|
367
|
+
|
|
368
|
+
Options:
|
|
369
|
+
|
|
370
|
+
- `-f, --file [PATH]`: load the overrides from a JSON file
|
|
371
|
+
- `-w, --working-dir [PATH]`: the package directory (default: the current directory)
|
|
372
|
+
- `-j, --json`: print the result as one JSON document (see [JSON output](../cli-reference.md#json-output))
|
|
373
|
+
|
|
374
|
+
## Validation
|
|
375
|
+
|
|
376
|
+
Every override source is validated against the `settingsSchema` of the settings resource it names, scenario overrides and session overrides alike. Neither has to name every property, but both are refused for:
|
|
377
|
+
|
|
378
|
+
- A key naming no settings resource the package declares
|
|
379
|
+
- A property the schema does not declare, such as a misspelling
|
|
380
|
+
- A value of the wrong type, or outside a `minimum`, `maximum` or `enum`
|
|
381
|
+
- A value the schema locks with `readOnly` (see [Lock a setting](#lock-a-setting))
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
Error: Settings validation failed:
|
|
385
|
+
Settings "engine": /BaseDemand must be number
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
Error: Settings validation failed:
|
|
390
|
+
Unknown settings "nope": expected one of engine, session, ui
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
| Error | Cause | Fix |
|
|
394
|
+
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
395
|
+
| `must be number` | A number was quoted | Write `"BaseDemand": 200`, not `"BaseDemand": "200"` |
|
|
396
|
+
| `must be <= X` or `must be >= X` | The value is outside the schema's range | Bring it within range |
|
|
397
|
+
| `must NOT have additional properties` | The property is not declared in the `settingsSchema` | Check the property name in `settings/<identifier>/settings.sdk.json` |
|
|
398
|
+
| `Unknown settings "x"` | The override is keyed by something that is not a settings identifier, often an old slot name | Key it by the settings resource's directory name |
|
|
399
|
+
| `reads settings "x", which this package does not declare` | A resource's `settings` names a missing resource | Create it with `new settings`, or remove it from `settings` |
|
|
400
|
+
| `declares no default` | A `settingsSchema` property has no `default` | Add one |
|
|
401
|
+
| `is read-only` | The override sets a value the game locks | Remove it from the override |
|
|
402
|
+
|
|
403
|
+
At `init` or any other CLI load, an invalid override fails the command. In a running dev server, an edited scenario that no longer validates is rejected, the last good values keep running, and the failure is shown as a dismissible error.
|
|
404
|
+
|
|
405
|
+
## Where the dev server keeps overrides
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
.seriousgaming-sdk/
|
|
409
|
+
├── configuration.json # the session overrides (sparse)
|
|
410
|
+
└── configuration-scenario # the scenario they were saved under
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`configuration.json` holds only what `configure` or the setup screen set, keyed by settings identifier. Defaults and the active scenario's values are resolved beneath it each time rather than baked in, and overrides saved under a different scenario are not reused.
|
|
414
|
+
|
|
415
|
+
The file is yours. If a settings schema changes so that a saved override no longer validates, `init` names the offending key rather than overwriting it:
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
Session settings are invalid or incompatible with current schemas:
|
|
419
|
+
Settings "engine": data must NOT have additional properties (unexpected property: "Incrementt")
|
|
420
|
+
|
|
421
|
+
It is authored content, so correct it in place:
|
|
422
|
+
.seriousgaming-sdk/configuration.json
|
|
423
|
+
Deleting the file starts again from the schema defaults.
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Correct the typo, or delete the file and start again from the defaults:
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
rm .seriousgaming-sdk/configuration.json
|
|
430
|
+
pnpm run sdk init
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
`sdk play`, `sdk scenario` and `sdk reconfigure` report the resolved values under `settings`.
|
|
434
|
+
|
|
435
|
+
## Settings and patches
|
|
436
|
+
|
|
437
|
+
Settings and JSON patches do different jobs:
|
|
438
|
+
|
|
439
|
+
| Aspect | Settings | Patch |
|
|
440
|
+
| ---------- | ---------------------------------------- | ------------------------------------- |
|
|
441
|
+
| Purpose | Parameters the game runs on | Simulate a participant's decisions |
|
|
442
|
+
| Applied | Before play starts, fixed thereafter | Before one tick |
|
|
443
|
+
| Written by | Defaults, scenarios, the session layer | The CLI, a surface's `setData` |
|
|
444
|
+
| Use case | Difficulty, prices, content, answer keys | Prices chosen, workers hired, answers |
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
pnpm run sdk configure '{"engine":{"BaseDemand":15000}}'
|
|
448
|
+
pnpm run sdk tick --patch '[{"op":"replace","path":"/price/Price","value":7}]'
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Related
|
|
452
|
+
|
|
453
|
+
- [Create settings](../recipes/create-settings.md): the task, step by step
|
|
454
|
+
- [Building a setup screen](./setup.md): the form that edits settings at session creation
|
|
455
|
+
- [Building a scenario editor](./scenario-editor.md): the screen that authors lasting scenarios
|
|
456
|
+
- [Scenarios](./scenarios.md): variants of a game that override settings
|
|
457
|
+
- [Drive a game from the CLI](../cli-simulation-testing.md): configure, tick, patch, and read a session from the terminal
|
|
458
|
+
- [Shaping games](./games.md): the rest of `seriousgaming.sdk.json`
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Building a setup screen
|
|
2
|
+
|
|
3
|
+
The `setup` slot holds the small form a facilitator fills in when they create a session. What it writes applies to that session only. This guide covers creating one, building its form, and how it behaves in the dev server.
|
|
4
|
+
|
|
5
|
+
## What you'll learn
|
|
6
|
+
|
|
7
|
+
- What the setup slot is for, and how it differs from the scenario editor
|
|
8
|
+
- How to create a setup screen and choose the settings it edits
|
|
9
|
+
- How to read and write settings with `useSettingsEditor`
|
|
10
|
+
- How to start play from your own control with `requestPlay`
|
|
11
|
+
|
|
12
|
+
## What setup is
|
|
13
|
+
|
|
14
|
+
A game holds at most one setup screen, at `setup/`. It is a slot, like `session` or `waiting_room`, so it carries no name of its own: the slot is its identifier.
|
|
15
|
+
|
|
16
|
+
| Aspect | Setup | Scenario editor |
|
|
17
|
+
| ---------------- | --------------------------------------- | --------------------------------------- |
|
|
18
|
+
| **When shown** | When a session is created | At any time, with no session |
|
|
19
|
+
| **What it does** | A short form for this session | Authoring a lasting scenario |
|
|
20
|
+
| **Output** | Session overrides for this session only | A saved scenario |
|
|
21
|
+
| **Starts play** | Yes, with `requestPlay` or host Play | No |
|
|
22
|
+
| **Guide** | This page | [Scenario editor](./scenario-editor.md) |
|
|
23
|
+
|
|
24
|
+
A game with a `setup` slot opens at the **configuration step**: no team has a context yet, participants who arrive early wait, and play begins when the facilitator presses Play. A game without one starts at tick 0. Settings are fixed once play starts, so what setup writes reaches the engine and every surface that declares those settings from the first tick.
|
|
25
|
+
|
|
26
|
+
Keep it small. Setup is part of somebody creating a session on the day, often in front of a room. Put the full authoring flow in the [scenario editor](./scenario-editor.md).
|
|
27
|
+
|
|
28
|
+
## Creating a setup screen
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm run sdk new setup '{
|
|
32
|
+
"definition": {
|
|
33
|
+
"configures": ["engine"]
|
|
34
|
+
},
|
|
35
|
+
"rootDirectory": ".",
|
|
36
|
+
"ui": { "entrypoint": "index.html" }
|
|
37
|
+
}'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`configures` names the [settings resources](./settings.md) the screen edits, by identifier: here the one at `settings/engine/`. The screen receives the resolved values of exactly those settings and can change nothing else: an `updateSettings` call naming any other settings resource is refused, and none of it is applied. The build fails if you name a settings resource the package does not declare.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
setup/
|
|
44
|
+
├── setup.sdk.json
|
|
45
|
+
├── index.html
|
|
46
|
+
├── types.ts # generated
|
|
47
|
+
├── defaults.ts # generated
|
|
48
|
+
└── src/
|
|
49
|
+
├── main.tsx
|
|
50
|
+
├── App.tsx
|
|
51
|
+
├── i18n.ts
|
|
52
|
+
└── theme.ts
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`src/main.tsx` wraps the app in `SettingsEditorWrapper`, the wrapper both settings editor slots share.
|
|
56
|
+
|
|
57
|
+
## Building the form
|
|
58
|
+
|
|
59
|
+
`useSettingsEditor()` returns three things:
|
|
60
|
+
|
|
61
|
+
- `settings`: the resolved values of the settings this screen configures, keyed by settings identifier
|
|
62
|
+
- `updateSettings(update)`: merges an update and sends the host only what changed. It returns a promise that rejects with a `MutationError` when the host refuses the write
|
|
63
|
+
- `configures`: the identifiers this screen was sent
|
|
64
|
+
|
|
65
|
+
Spreading `settings` into an update is safe: an unchanged value is never stored as a facilitator override, so a scenario chosen later still applies to it.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { useSettingsEditor } from '@seriousgaming/sdk/ui';
|
|
69
|
+
import { Stack, TextField } from '@mui/material';
|
|
70
|
+
import type { Settings } from '../types';
|
|
71
|
+
|
|
72
|
+
function SetupForm() {
|
|
73
|
+
const { settings, updateSettings } = useSettingsEditor<Settings>();
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Stack spacing={2}>
|
|
77
|
+
<TextField
|
|
78
|
+
label="Base demand"
|
|
79
|
+
type="number"
|
|
80
|
+
value={settings.engine.BaseDemand}
|
|
81
|
+
onChange={(e) =>
|
|
82
|
+
void updateSettings({
|
|
83
|
+
engine: { ...settings.engine, BaseDemand: Number(e.target.value) },
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
/>
|
|
87
|
+
</Stack>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The generated `Settings` in `types.ts` has one entry per settings resource in `configures`, and `defaultSettings` in `defaults.ts` holds each one's defaults, which is the fixture to start a test from.
|
|
93
|
+
|
|
94
|
+
### Setting the number of rounds
|
|
95
|
+
|
|
96
|
+
The number of ticks and the team bounds are the built-in `game` settings, which a setup screen edits like any other once it lists `game` in `configures`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{ "definition": { "configures": ["game", "engine"] } }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<TextField
|
|
104
|
+
label="Rounds"
|
|
105
|
+
type="number"
|
|
106
|
+
value={settings.game.maxTicks ?? ''}
|
|
107
|
+
onChange={(e) =>
|
|
108
|
+
void updateSettings({ game: { ...settings.game, maxTicks: Number(e.target.value) } })
|
|
109
|
+
}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Two of the game settings behave differently here. `waitingRoom.openOnStart` takes effect before setup runs, so writing it is refused. And play cannot start while the team count is outside the `teams.min` and `teams.max` setup chose: the host's Play control and `requestPlay` are both refused until the facilitator adds or removes teams, or changes the setting. See [The game settings](./settings.md#the-game-settings).
|
|
114
|
+
|
|
115
|
+
A write is also refused, as `denied` and with none of it applied, when it changes a setting the game locks with `readOnly` or sets a value outside its schema's range, the ranges a game sets on its own `game` settings with `gameSettingsConstraints` included. The screen is sent the values, not the schemas, so show a locked field as read-only and keep inputs within range yourself. Sending a locked value back unchanged, as a screen that spreads `settings` does, is not a change and is accepted. See [Lock a setting](./settings.md#lock-a-setting).
|
|
116
|
+
|
|
117
|
+
## Starting play from your own control
|
|
118
|
+
|
|
119
|
+
`useSettingsEditorCallbacks()` gives the screen `requestPlay`, which leaves the configuration step exactly as the host's Play control does:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { useSettingsEditorCallbacks } from '@seriousgaming/sdk/ui';
|
|
123
|
+
import { Button } from '@mui/material';
|
|
124
|
+
|
|
125
|
+
function StartButton() {
|
|
126
|
+
const { requestPlay } = useSettingsEditorCallbacks();
|
|
127
|
+
return (
|
|
128
|
+
<Button variant="contained" onClick={() => requestPlay()}>
|
|
129
|
+
Start game
|
|
130
|
+
</Button>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The host always renders its own Play control on the configuration step, and a game cannot hide it. `requestPlay` adds a second way out rather than replacing it, so a bug in your control cannot strand a facilitator. `requestPlay` means something only in setup: the host ignores it from the scenario editor. See [Use requestPlay in a setup screen](../recipes/use-request-play.md).
|
|
136
|
+
|
|
137
|
+
## Other hooks
|
|
138
|
+
|
|
139
|
+
- `useSettingsEditorLocale()`: the current locale, for `src/i18n.ts`
|
|
140
|
+
- `useSettingsEditorTheme()`: the host's theme, so colours follow its light and dark scheme
|
|
141
|
+
- `useSettingsEditorThemePreference()`: the facilitator's theme preference; set it with `setThemePreference` from the callbacks
|
|
142
|
+
- `useSettingsEditorCallbacks()`: also `uploadBlob`, `getAssetUrl`, `readAsset`, and the `endpoints` the bundle declares
|
|
143
|
+
|
|
144
|
+
An endpoint called from setup sees `caller.role` as `'facilitator'`.
|
|
145
|
+
|
|
146
|
+
## Rendering inside the host
|
|
147
|
+
|
|
148
|
+
Setup is embedded in the host's page, not a page of its own:
|
|
149
|
+
|
|
150
|
+
- **Leave the ground to the host.** Do not paint a page background. MUI's `CssBaseline` paints the body, so override it with `<GlobalStyles styles={{ body: { backgroundColor: 'transparent' } }} />`
|
|
151
|
+
- **Prefer full-screen dialogs.** A centred modal inside an iframe is centred within the frame
|
|
152
|
+
- **Do not reproduce host chrome.** The language picker, the theme control, and the Play action live outside your iframe
|
|
153
|
+
|
|
154
|
+
The [session UI guide](./session.md#rendering-inside-the-host) covers the same ground in more detail.
|
|
155
|
+
|
|
156
|
+
## Dev server behaviour
|
|
157
|
+
|
|
158
|
+
`pnpm run sdk dev` opens a game with a setup slot at its configuration step, on `/setup`. Fill in the form and press Play (or your own control). Once play starts, `/setup` shows that the step has closed; **Reconfigure** in the dev drawer returns the session to it and every team to tick 0. Overrides persist in `.seriousgaming-sdk/` between runs, and `pnpm run sdk configure` sets them from the command line.
|
|
159
|
+
|
|
160
|
+
## Managing the slot
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pnpm run sdk update setup '{
|
|
164
|
+
"definition": { "configures": ["engine", "ui"] },
|
|
165
|
+
"rootDirectory": ".",
|
|
166
|
+
"ui": { "entrypoint": "index.html" }
|
|
167
|
+
}'
|
|
168
|
+
pnpm run sdk delete setup
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Deleting the slot removes the configuration step: the game then starts at tick 0.
|
|
172
|
+
|
|
173
|
+
## Testing
|
|
174
|
+
|
|
175
|
+
`renderSettingsEditor` from `@seriousgaming/sdk/ui/testing` renders a component inside every settings editor provider:
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { renderSettingsEditor } from '@seriousgaming/sdk/ui/testing';
|
|
179
|
+
import { defaultSettings } from '../defaults';
|
|
180
|
+
|
|
181
|
+
renderSettingsEditor(<SetupForm />, {
|
|
182
|
+
settings: defaultSettings,
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
See [Test a user interface](../recipes/test-user-interface.md).
|
|
187
|
+
|
|
188
|
+
## See also
|
|
189
|
+
|
|
190
|
+
- [Scenario editor](./scenario-editor.md): authoring lasting scenarios
|
|
191
|
+
- [Settings](./settings.md): declaring, reading, and overriding settings
|
|
192
|
+
- [Scenarios](./scenarios.md)
|
|
193
|
+
- [Create a setup screen](../recipes/create-setup.md)
|