summer-engine 4.3.0 → 4.6.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +3 -1
- package/.codex-plugin/plugin.json +5 -3
- package/.cursor-plugin/plugin.json +4 -2
- package/.factory-plugin/plugin.json +4 -2
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +100 -0
- package/README.md +67 -27
- package/commands/start.md +19 -15
- package/commands/summer.md +1 -0
- package/dist/cli/command-usage.d.ts +3 -1
- package/dist/cli/command-usage.js +10 -4
- package/dist/cli/commands/create.d.ts +1 -25
- package/dist/cli/commands/create.js +10 -138
- package/dist/cli/commands/dev.d.ts +66 -0
- package/dist/cli/commands/dev.js +234 -0
- package/dist/cli/commands/install.d.ts +15 -58
- package/dist/cli/commands/install.js +31 -481
- package/dist/cli/commands/login.js +1 -1
- package/dist/cli/commands/ops.d.ts +17 -1
- package/dist/cli/commands/ops.js +97 -9
- package/dist/cli/commands/preview.d.ts +58 -0
- package/dist/cli/commands/preview.js +181 -0
- package/dist/cli/commands/run.d.ts +1 -17
- package/dist/cli/commands/run.js +17 -206
- package/dist/cli/commands/setup.js +175 -32
- package/dist/cli/index.js +5 -0
- package/dist/core/agent-host.d.ts +3 -0
- package/dist/core/agent-host.js +1 -1
- package/dist/core/api-client.d.ts +10 -2
- package/dist/core/api-client.js +13 -8
- package/dist/core/capabilities/agent-playbook.js +1 -0
- package/dist/core/capabilities/apps.d.ts +9 -9
- package/dist/core/capabilities/asset-import.d.ts +4 -1
- package/dist/core/capabilities/asset-import.js +15 -6
- package/dist/core/capabilities/capture-gameplay.js +1 -1
- package/dist/core/capabilities/connect-signal.d.ts +2 -2
- package/dist/core/capabilities/content-make/index.d.ts +81 -0
- package/dist/core/capabilities/content-make/index.js +706 -0
- package/dist/core/capabilities/content-make/media.d.ts +57 -0
- package/dist/core/capabilities/content-make/media.js +172 -0
- package/dist/core/capabilities/content-make/plan.d.ts +130 -0
- package/dist/core/capabilities/content-make/plan.js +433 -0
- package/dist/core/capabilities/content-make/render.d.ts +64 -0
- package/dist/core/capabilities/content-make/render.js +370 -0
- package/dist/core/capabilities/content-make/study.d.ts +13 -0
- package/dist/core/capabilities/content-make/study.js +165 -0
- package/dist/core/capabilities/content-make/types.d.ts +173 -0
- package/dist/core/capabilities/content-make/types.js +6 -0
- package/dist/core/capabilities/content-make/voice.d.ts +7 -0
- package/dist/core/capabilities/content-make/voice.js +27 -0
- package/dist/core/capabilities/events.d.ts +2 -2
- package/dist/core/capabilities/export-game.d.ts +4 -1
- package/dist/core/capabilities/export-game.js +4 -3
- package/dist/core/capabilities/export-presets.d.ts +2 -1
- package/dist/core/capabilities/export-presets.js +2 -1
- package/dist/core/capabilities/go-live.d.ts +14 -0
- package/dist/core/capabilities/go-live.js +408 -0
- package/dist/core/capabilities/image-generation.d.ts +6 -6
- package/dist/core/capabilities/item-manifest.d.ts +92 -0
- package/dist/core/capabilities/item-manifest.js +247 -0
- package/dist/core/capabilities/placement.d.ts +7 -7
- package/dist/core/capabilities/projects-local.d.ts +68 -0
- package/dist/core/capabilities/projects-local.js +172 -0
- package/dist/core/capabilities/publish-build.d.ts +13 -2
- package/dist/core/capabilities/publish-build.js +87 -21
- package/dist/core/capabilities/publish-download.js +1 -1
- package/dist/core/capabilities/runtime-control.d.ts +7 -2
- package/dist/core/capabilities/runtime-control.js +27 -1
- package/dist/core/capabilities/seeing/args.d.ts +2 -2
- package/dist/core/capabilities/store-checks.d.ts +45 -0
- package/dist/core/capabilities/store-checks.js +85 -0
- package/dist/core/capabilities/store-details.d.ts +37 -0
- package/dist/core/capabilities/store-details.js +94 -0
- package/dist/core/capabilities/summer-bundle.d.ts +21 -2
- package/dist/core/capabilities/summer-bundle.js +54 -7
- package/dist/core/capabilities/sync-items.d.ts +89 -0
- package/dist/core/capabilities/sync-items.js +352 -0
- package/dist/core/capabilities/tool-dispatch.js +66 -6
- package/dist/core/capabilities/video-ogv.d.ts +49 -0
- package/dist/core/capabilities/video-ogv.js +260 -0
- package/dist/core/capability-skew.js +2 -2
- package/dist/core/cli-usage-beacon.js +26 -2
- package/dist/core/cli-usage.d.ts +5 -2
- package/dist/core/cli-usage.js +8 -2
- package/dist/core/engine-install.d.ts +53 -0
- package/dist/core/engine-install.js +93 -0
- package/dist/core/engine-installer.d.ts +134 -0
- package/dist/core/engine-installer.js +760 -0
- package/dist/core/engine-launch.d.ts +126 -0
- package/dist/core/engine-launch.js +398 -0
- package/dist/core/engine-self-start.d.ts +63 -0
- package/dist/core/engine-self-start.js +282 -0
- package/dist/core/engine.d.ts +7 -1
- package/dist/core/engine.js +9 -2
- package/dist/core/error-codes.d.ts +4 -1
- package/dist/core/error-codes.js +7 -3
- package/dist/core/library-read.d.ts +2 -2
- package/dist/core/library-search.d.ts +2 -2
- package/dist/core/local-project.d.ts +50 -0
- package/dist/core/local-project.js +234 -0
- package/dist/core/ops/areas.js +9 -3
- package/dist/core/ops/catalog.d.ts +9 -0
- package/dist/core/ops/catalog.js +56 -4
- package/dist/core/ops/engine-answer.d.ts +4 -1
- package/dist/core/ops/engine-answer.js +16 -3
- package/dist/core/ops/engine.js +4 -4
- package/dist/core/ops/save-files.d.ts +6 -1
- package/dist/core/ops/save-files.js +67 -50
- package/dist/core/ops/search.js +9 -2
- package/dist/core/preview/input.d.ts +35 -0
- package/dist/core/preview/input.js +105 -0
- package/dist/core/preview/launch-config.d.ts +52 -0
- package/dist/core/preview/launch-config.js +137 -0
- package/dist/core/preview/page.d.ts +22 -0
- package/dist/core/preview/page.js +232 -0
- package/dist/core/preview/preview-source.d.ts +179 -0
- package/dist/core/preview/preview-source.js +350 -0
- package/dist/core/preview/server.d.ts +26 -0
- package/dist/core/preview/server.js +218 -0
- package/dist/core/telemetry.d.ts +20 -16
- package/dist/core/telemetry.js +92 -16
- package/dist/core/templates.d.ts +20 -0
- package/dist/core/templates.js +300 -3
- package/dist/core/tool-profiles.d.ts +2 -2
- package/dist/core/tool-profiles.js +1 -0
- package/dist/core/usage-fields.d.ts +41 -0
- package/dist/core/usage-fields.js +150 -0
- package/dist/core/util/sleep.d.ts +3 -0
- package/dist/core/util/sleep.js +12 -0
- package/dist/installer/agent-config.d.ts +29 -0
- package/dist/installer/agent-config.js +75 -11
- package/dist/installer/agent-detect.d.ts +44 -0
- package/dist/installer/agent-detect.js +166 -0
- package/dist/installer/agent-table.d.ts +19 -0
- package/dist/installer/agent-table.js +130 -0
- package/dist/installer/setup-error.d.ts +19 -0
- package/dist/installer/setup-error.js +34 -0
- package/dist/installer/version-check.d.ts +5 -0
- package/dist/installer/version-check.js +21 -0
- package/dist/mcp/ops-surface.d.ts +7 -0
- package/dist/mcp/ops-surface.js +40 -3
- package/dist/mcp/server.d.ts +14 -4
- package/dist/mcp/server.js +42 -8
- package/dist/mcp/tool-surface.d.ts +8 -0
- package/dist/mcp/tool-surface.js +19 -2
- package/dist/mcp/tools/creator-tools.js +92 -14
- package/dist/mcp/tools/debug-tools.js +2 -0
- package/dist/mcp/tools/local-project-tools.d.ts +6 -0
- package/dist/mcp/tools/local-project-tools.js +33 -0
- package/dist/mcp/tools/navigation-tools.js +2 -1
- package/dist/mcp/tools/project-tools.js +5 -8
- package/dist/mcp/tools/seeing-tools.js +1 -1
- package/dist/mcp/tools/visual-tools.js +1 -1
- package/dist/mcp/tools/with-engine.js +1 -1
- package/dist/project-memory/agent-instructions.js +11 -3
- package/gemini-extension.json +3 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +18 -5
- package/library/skills/README.md +12 -8
- package/library/skills/animated-loop/SKILL.md +10 -16
- package/library/skills/cinematic-cutscene/SKILL.md +7 -18
- package/library/skills/headless-scripting/SKILL.md +7 -4
- package/library/skills/in-game-purchases/SKILL.md +25 -8
- package/library/skills/in-game-purchases/resource.yaml +2 -1
- package/library/skills/launch-ready/SKILL.md +351 -0
- package/library/skills/launch-ready/resource.yaml +45 -0
- package/library/skills/make-content/SKILL.md +202 -0
- package/library/skills/make-content/resource.yaml +32 -0
- package/library/skills/make-game/SKILL.md +6 -4
- package/library/skills/make-game/resource.yaml +2 -1
- package/library/skills/navigate-summer/SKILL.md +4 -2
- package/library/skills/new-project/SKILL.md +15 -22
- package/library/skills/new-project/resource.yaml +2 -1
- package/library/skills/painted-25d-world/SKILL.md +366 -0
- package/library/skills/painted-25d-world/_building-blocks/blender-route.md +33 -0
- package/library/skills/painted-25d-world/_building-blocks/blockout_builder.gd +149 -0
- package/library/skills/painted-25d-world/_building-blocks/capture_paint_view.gd +32 -0
- package/library/skills/painted-25d-world/_building-blocks/paint_projection.gdshader +23 -0
- package/library/skills/painted-25d-world/_building-blocks/paintcam_check.gd +25 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_door.gd +17 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_location.gd +56 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_player.gd +125 -0
- package/library/skills/painted-25d-world/_building-blocks/painted_world.gd +111 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/cam.py +57 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/detail4k.py +81 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/overlay.py +17 -0
- package/library/skills/painted-25d-world/_building-blocks/tools/preview.py +60 -0
- package/library/skills/painted-25d-world/resource.yaml +39 -0
- package/library/skills/publish-your-game/SKILL.md +13 -14
- package/library/skills/publish-your-game/resource.yaml +4 -3
- package/library/skills/release-checklist/SKILL.md +188 -0
- package/library/skills/release-checklist/resource.yaml +40 -0
- package/library/skills/release-readiness/SKILL.md +1 -1
- package/library/skills/release-readiness/resource.yaml +1 -1
- package/library/skills/store-art/SKILL.md +1 -1
- package/library/skills/store-art/resource.yaml +1 -1
- package/library/skills/store-listing/SKILL.md +1 -1
- package/library/skills/store-listing/resource.yaml +1 -1
- package/library/skills/summer-cli/SKILL.md +2 -0
- package/library/skills/summer-store/SKILL.md +295 -39
- package/library/skills/summer-store/resource.yaml +9 -3
- package/library/skills/trailer-shot/SKILL.md +3 -3
- package/library/templates/README.md +5 -0
- package/library/tools/content-make/resource.yaml +132 -0
- package/library/tools/frame-shot/GUIDE.md +1 -1
- package/library/tools/go-live/resource.yaml +67 -0
- package/library/tools/import-from-url/GUIDE.md +1 -0
- package/library/tools/import-from-url-batch/GUIDE.md +1 -1
- package/library/tools/play/GUIDE.md +2 -0
- package/library/tools/projects-create-local/resource.yaml +55 -0
- package/library/tools/projects-open-local/resource.yaml +51 -0
- package/library/tools/publish-build/resource.yaml +3 -3
- package/library/tools/screenshot/GUIDE.md +1 -1
- package/library/tools/store-set-details/resource.yaml +114 -0
- package/library/tools/sync-items/resource.yaml +72 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +6 -0
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +3 -1
- package/registry/generated/index.json +463 -37
- package/registry/generated/marketplace.claude.json +1 -1
- package/registry/generated/plugin.claude-directory.json +4 -2
- package/registry/generated/plugin.claude.json +3 -1
- package/registry/generated/plugin.codex.json +5 -3
- package/registry/generated/plugin.cursor.json +4 -2
- package/registry/generated/plugin.factory.json +4 -2
- package/registry/generated/server.json +2 -2
- package/registry/generated/skills-index.md +12 -8
- package/registry/generated/skills-registry.json +62 -5
- package/registry/operations/legacy-names.json +40 -0
- package/registry/operations/public-catalog.json +10144 -387
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: summer-store
|
|
3
|
-
description: "Sell Summer items for Sparks
|
|
3
|
+
description: "Sell Summer items for Sparks: shop, checkout, item slugs, bundles and sales, owned items, summer.items.json sync, authority checks."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: multiplayer-and-networking
|
|
7
7
|
user-invocable: true
|
|
8
|
-
allowed-tools: Read Grep Edit Write summer_get_script_errors summer_project_setting summer_play summer_stop summer_get_diagnostics
|
|
9
|
-
paths: ["**/*.gd", ".summer/test-users.json"]
|
|
8
|
+
allowed-tools: Read Grep Edit Write summer_get_script_errors summer_project_setting summer_play summer_stop summer_get_diagnostics summer_run_script summer_sync_items
|
|
9
|
+
paths: ["**/*.gd", ".summer/test-users.json", "summer.items.json"]
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# /summer-store — sell items, check what players own
|
|
@@ -22,12 +22,13 @@ balances or approval:
|
|
|
22
22
|
4. Summer charges the Sparks and issues the item into the player's **inventory**.
|
|
23
23
|
5. The game sees the item in `Summer.client.items` (client) and `Summer.authority.items` (authority).
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
The ids, kept apart:
|
|
26
26
|
|
|
27
27
|
| Id | Where it comes from | Use it for |
|
|
28
28
|
| --- | --- | --- |
|
|
29
|
+
| Slug ("Item key" in Studio) | You choose it, e.g. `party_hat`; the same in staging and production | Recognizing an item in game code |
|
|
30
|
+
| Definition id | Summer assigns it (`item_…`); **differs between staging and production** | The same, on Summer versions without the slug getters |
|
|
29
31
|
| Offer id | `item.price.offer_id` in the store listing | `SummerCheckoutLine.create(offer_id, quantity)` |
|
|
30
|
-
| Definition id | Your item definition in the Summer catalog | "Does this player own a hat?" |
|
|
31
32
|
| Owned item id | One owned copy (`item.get_owned_item_id()`) | Pointing at a specific owned copy |
|
|
32
33
|
|
|
33
34
|
The rule that makes it cheat-proof: **the authority decides what a player may
|
|
@@ -39,15 +40,154 @@ proves nothing.
|
|
|
39
40
|
| Surface | Store and checkout | Inventory |
|
|
40
41
|
| --- | --- | --- |
|
|
41
42
|
| Hosted | Need the Summer app's `player.store.read@1` / `player.store.checkout@1` | Needs `player.items@1` |
|
|
42
|
-
| Local Play | `unavailable` | Works from a fixture: empty unless `.summer/test-users.json` lists item templates (step
|
|
43
|
+
| Local Play | `unavailable` | Works from a fixture: empty unless `.summer/test-users.json` lists item templates (step 7) |
|
|
43
44
|
| Guests, older Summer apps | Readiness reports `unavailable` | Readiness reports `unavailable` |
|
|
44
45
|
|
|
45
|
-
Items and offers are created in your game's Studio pages (`studio.open` target
|
|
46
|
-
`game`). Code never creates items.
|
|
47
|
-
|
|
48
46
|
This skill extends the Courtyard game from `multiplayer-project` and
|
|
49
47
|
`multiplayer-state`.
|
|
50
48
|
|
|
49
|
+
## Where items come from
|
|
50
|
+
|
|
51
|
+
**Game code never creates or grants items at runtime.** The creator defines
|
|
52
|
+
them, Summer reviews each one, and the owner activates it. Two ways in:
|
|
53
|
+
|
|
54
|
+
| Way | How | Who writes |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| Studio | The game's item pages (`studio.open` target `game`): create, Item key, price, submit for review | The owner |
|
|
57
|
+
| `summer.items.json` | A file in the project root, synced with `summer_sync_items` (CLI `summer economy sync-items`) | An agent **proposes**; the owner approves in Studio, and Studio applies it |
|
|
58
|
+
|
|
59
|
+
An agent never writes an item or a price itself. Bundles and sales (step 5)
|
|
60
|
+
are not in the file; they belong to the store page draft, which the owner
|
|
61
|
+
publishes.
|
|
62
|
+
|
|
63
|
+
### How an item becomes buyable
|
|
64
|
+
|
|
65
|
+
Today an item goes on sale only after all of these:
|
|
66
|
+
|
|
67
|
+
1. **Defined.** In Studio, or from a sync plan the owner approved (Studio
|
|
68
|
+
applies it). Approving the plan does not put anything on sale.
|
|
69
|
+
2. **Reviewed.** Each item is submitted for Summer's review. Its page shows
|
|
70
|
+
the state; "Check verification status" reads the result.
|
|
71
|
+
3. **Activated, one item at a time.** Once Summer approves an item, the owner
|
|
72
|
+
presses **Review activation** on that item's page in Studio and confirms.
|
|
73
|
+
Nothing activates it for them: not the sync, not an agent.
|
|
74
|
+
4. **The Game is live.** An active item is buyable only while its Game's
|
|
75
|
+
store page is live. It stops selling while the Game is unlisted and
|
|
76
|
+
resumes when the Game is listed again. An item can be activated before
|
|
77
|
+
the Game goes live; it sells from the moment the Game is live.
|
|
78
|
+
|
|
79
|
+
**Editing a live item makes a new revision.** In Studio that is "Review new
|
|
80
|
+
revision"; in a sync plan it is an `update` with `"revision": "new_draft"`.
|
|
81
|
+
Players keep buying the live revision while the new one is reviewed, and
|
|
82
|
+
the new one replaces it only after the owner's Review activation of it.
|
|
83
|
+
|
|
84
|
+
**Coming:** Summer has decided that items will go on sale without waiting for
|
|
85
|
+
Summer's review. Staff will review an item after it goes live and can take
|
|
86
|
+
it down. The store change that does this is being built and has not shipped.
|
|
87
|
+
Until it ships, follow the four steps above. When it ships, steps 2 and 3
|
|
88
|
+
no longer hold up the sale; a live Game is still required.
|
|
89
|
+
|
|
90
|
+
### `summer.items.json`
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"schema": "summer.items/v1",
|
|
95
|
+
"items": [
|
|
96
|
+
{
|
|
97
|
+
"slug": "party_hat",
|
|
98
|
+
"name": "Party hat",
|
|
99
|
+
"description": "A paper crown for the courtyard.",
|
|
100
|
+
"class": "COSMETIC",
|
|
101
|
+
"cosmetic": {"familyId": "hat", "variantKey": "party"},
|
|
102
|
+
"ownershipMode": "INSTANCE",
|
|
103
|
+
"ownership": "OWNED",
|
|
104
|
+
"priceSparks": 150,
|
|
105
|
+
"media": ["art/items/party_hat.png"]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"slug": "confetti",
|
|
109
|
+
"name": "Confetti",
|
|
110
|
+
"description": "One burst of confetti.",
|
|
111
|
+
"ownershipMode": "QUANTITY",
|
|
112
|
+
"consumable": true,
|
|
113
|
+
"priceSparks": 10,
|
|
114
|
+
"media": ["art/items/confetti.png"]
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| Field | Rule |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `slug` | Required, unique in the file: 1–64 lowercase letters and digits, words joined by one `.`, `_` or `-`, starting with a letter |
|
|
123
|
+
| `name`, `description` | Required; at most 120 and 5000 characters |
|
|
124
|
+
| `class` | `STANDARD` (default) or `COSMETIC`; a cosmetic needs `cosmetic.familyId` and `variantKey` |
|
|
125
|
+
| `ownershipMode` | `INSTANCE` (default; one copy is one owned item) or `QUANTITY` (a stack). Class and mode never change later |
|
|
126
|
+
| `maxOwned` | Optional cap on copies per player (step 4) |
|
|
127
|
+
| `ownership` | `SOULBOUND`, `OWNED` (default) or `MARKETABLE`; `consumable` is true or false |
|
|
128
|
+
| `supply` | Optional lifetime limit; omit for unlimited |
|
|
129
|
+
| `priceSparks` | Whole Sparks. Omitted: a new item is not sold, an existing price stays |
|
|
130
|
+
| `media` | Project-relative `.png`, `.jpg`, `.webp` or `.glb`, in store order. **A new item needs at least one image** |
|
|
131
|
+
|
|
132
|
+
### Sync it
|
|
133
|
+
|
|
134
|
+
1. **Plan (dry run).** `summer_sync_items {"gameId": "game_..."}` reads the
|
|
135
|
+
file and the Game's items by slug, and changes nothing. It answers create,
|
|
136
|
+
update (with the changed fields), unchanged, blocked (with the reason),
|
|
137
|
+
and the Game's items the file does not name (listed, never changed).
|
|
138
|
+
2. **Show the plan to the user** and ask before sending it.
|
|
139
|
+
3. **Confirm.** `summer_sync_items {"gameId": "game_...", "confirm": true}`
|
|
140
|
+
uploads the changed images and sends the plan to the owner. The answer is
|
|
141
|
+
`awaiting_owner_approval` with an approval link: give it to the owner.
|
|
142
|
+
Nothing changes until they approve in Studio. Then each item is created
|
|
143
|
+
or revised, priced and submitted for Summer's review. Each one still
|
|
144
|
+
needs Summer's approval and the owner's Review activation before players
|
|
145
|
+
can buy it ("How an item becomes buyable").
|
|
146
|
+
4. **One approval takes at most 50 items.** A larger plan lists `batches`;
|
|
147
|
+
confirm each one with `"slugs": [...]` and pass on each approval link.
|
|
148
|
+
|
|
149
|
+
Activating an item (Review activation), retiring it, and the Game's own
|
|
150
|
+
price are not part of the sync. The owner does those in Studio. A Passport
|
|
151
|
+
collectible (a stamp, pin or badge) with the same slug is **blocked**:
|
|
152
|
+
Passport items have their own lifecycle and are never synced; give the
|
|
153
|
+
store item another slug.
|
|
154
|
+
|
|
155
|
+
**Staging and production.** One file serves both, because items match by
|
|
156
|
+
slug. Game ids differ, so take `gameId` from that environment's game list.
|
|
157
|
+
The machine holds one store sign-in. To switch, set `SUMMER_GATEWAY_URL` and
|
|
158
|
+
`SUMMER_MCP_RESOURCE_URL` for the other environment, then run
|
|
159
|
+
`summer login --store --force`. Until then the sync stops with
|
|
160
|
+
`store_login_other_environment`.
|
|
161
|
+
|
|
162
|
+
### Slugs
|
|
163
|
+
|
|
164
|
+
- Set in Studio ("Item key", on create or on the item page) or from the file.
|
|
165
|
+
- Unique within the Game, and the same in staging and production.
|
|
166
|
+
- **Frozen once the item's first publication is claimed**, even if that
|
|
167
|
+
publication fails or the item is retired. Before that it can change, and an
|
|
168
|
+
item without a slug can get one at any time.
|
|
169
|
+
- Never reused by another item of the Game, so game code and the authority
|
|
170
|
+
can safely name items by slug.
|
|
171
|
+
|
|
172
|
+
**Availability: the platform sends slugs; the game needs the getters.**
|
|
173
|
+
Since 2026-10-16 Summer sends every item's slug to games: the store listing
|
|
174
|
+
carries `StoreItem.slug` and the inventory `InventoryItem.definition_slug`.
|
|
175
|
+
Game code reads them with `SummerStoreItem.get_slug()`,
|
|
176
|
+
`SummerInventoryItem.get_slug()`, `SummerClientItems.has_owned_slug()` and
|
|
177
|
+
`SummerItemSelection.allowed_slugs`. Those getters are not in Summer 0.7.3;
|
|
178
|
+
they come with the next Summer Engine release that carries them (0.7.4, in
|
|
179
|
+
progress). Check the version the game runs on before relying on them. Local
|
|
180
|
+
Play fixtures carry slugs too (step 7).
|
|
181
|
+
|
|
182
|
+
| Check | Result |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| `summer_run_script {"scenePath": "res://main.tscn", "source": "func run(ctx):\n\tctx.report(\"slugs\", ClassDB.class_has_method(\"SummerClientItems\", \"has_owned_slug\"))"}` | `slugs: true`: this Summer version has the getters |
|
|
185
|
+
| No MCP: write the slug code, then `summer_get_script_errors` | A "not found" error on `has_owned_slug` or `get_slug`: this version lacks them |
|
|
186
|
+
| Hosted: `get_slug()` returns `""` | That item has no Item key yet: set one in Studio or in `summer.items.json` |
|
|
187
|
+
|
|
188
|
+
Without the getters, use the definition-id variants in steps 1–3, with each
|
|
189
|
+
environment's definition ids.
|
|
190
|
+
|
|
51
191
|
## Steps
|
|
52
192
|
|
|
53
193
|
### 1. The client shop
|
|
@@ -60,7 +200,7 @@ extends Node
|
|
|
60
200
|
## A shop for Summer items, paid in Sparks. Summer shows its own purchase
|
|
61
201
|
## sheet; the game never sees money and never grants an item itself.
|
|
62
202
|
|
|
63
|
-
signal shelf_changed(rows: Array) # [{name, sparks, offer_id}]
|
|
203
|
+
signal shelf_changed(rows: Array) # [{name, sparks, offer_id, slug, owned}]
|
|
64
204
|
signal checkouts_changed(busy: bool, rows: Array)
|
|
65
205
|
signal status(text: String)
|
|
66
206
|
|
|
@@ -96,7 +236,8 @@ func open() -> void:
|
|
|
96
236
|
var rows := []
|
|
97
237
|
for item in page.items:
|
|
98
238
|
if item.price != null and item.price.offer_id != "":
|
|
99
|
-
rows.append({"name": item.name, "sparks": item.price.sparks_amount, "offer_id": item.price.offer_id
|
|
239
|
+
rows.append({"name": item.name, "sparks": item.price.sparks_amount, "offer_id": item.price.offer_id,
|
|
240
|
+
"slug": item.slug, "owned": owns(item.slug)})
|
|
100
241
|
shelf_changed.emit(rows)
|
|
101
242
|
if items.check_refresh_readiness().ok:
|
|
102
243
|
items.refresh()
|
|
@@ -122,22 +263,36 @@ func _render_checkouts(operation: SummerCheckoutOperation = null) -> void:
|
|
|
122
263
|
for checkout in view.statuses:
|
|
123
264
|
rows.append({"id": checkout.request_id, "state": checkout.state, "error": checkout.error_code})
|
|
124
265
|
checkouts_changed.emit(view.has_pending_checkouts(), rows)
|
|
125
|
-
|
|
126
|
-
|
|
266
|
+
var failure := view.submission_failure
|
|
267
|
+
if failure != null:
|
|
268
|
+
var reason: StringName = failure.service_error.reason if failure.service_error != null else failure.code
|
|
269
|
+
if reason == &"already_owned":
|
|
270
|
+
status.emit("You already own this.") # show the row as Owned; nothing was charged
|
|
271
|
+
else:
|
|
272
|
+
status.emit("The purchase did not start: %s" % reason)
|
|
127
273
|
|
|
128
274
|
|
|
129
275
|
## Ownership comes only from the inventory, never from a checkout result.
|
|
130
|
-
func owns(
|
|
131
|
-
return items.is_projection_loaded() and items.
|
|
276
|
+
func owns(slug: String) -> bool:
|
|
277
|
+
return items.is_projection_loaded() and items.has_owned_slug(slug)
|
|
132
278
|
|
|
133
279
|
|
|
134
280
|
func _on_items_changed() -> void:
|
|
135
281
|
status.emit("Inventory updated.")
|
|
136
282
|
```
|
|
137
283
|
|
|
284
|
+
On a Summer version without slugs, drop `"slug"` from the rows, pass the
|
|
285
|
+
item's per-environment definition id, and use:
|
|
286
|
+
|
|
287
|
+
```gdscript
|
|
288
|
+
func owns(definition_id: String) -> bool:
|
|
289
|
+
return items.is_projection_loaded() and items.has_owned_definition(definition_id)
|
|
290
|
+
```
|
|
291
|
+
|
|
138
292
|
Bind `checkouts_changed` to the Buy buttons: disable them while `busy` is
|
|
139
|
-
true. Update rows by `id`; a status can be
|
|
140
|
-
rendering must be idempotent. Show each
|
|
293
|
+
true, and for rows with `owned` true. Update rows by `id`; a status can be
|
|
294
|
+
delivered again after a crash, so rendering must be idempotent. Show each
|
|
295
|
+
checkout state:
|
|
141
296
|
|
|
142
297
|
| `state` | Show |
|
|
143
298
|
| --- | --- |
|
|
@@ -150,11 +305,13 @@ rendering must be idempotent. Show each checkout state:
|
|
|
150
305
|
|
|
151
306
|
### 2. Unlock from the inventory only
|
|
152
307
|
|
|
153
|
-
Unlock cosmetics in menus with `owns(
|
|
308
|
+
Unlock cosmetics in menus with `owns(slug)`, and refresh the menu on
|
|
154
309
|
`refreshed`, `item_replaced` and `item_removed`. Never unlock from a
|
|
155
310
|
`purchased` status: the receipt proves the debit, the inventory proves
|
|
156
|
-
ownership, including after a reinstall or on another device. An empty
|
|
157
|
-
before `is_projection_loaded()` is "still loading", not "owns
|
|
311
|
+
ownership, including after a reinstall or on another device. An empty
|
|
312
|
+
inventory before `is_projection_loaded()` is "still loading", not "owns
|
|
313
|
+
nothing". Match by slug, never by item name, and never hard-code one
|
|
314
|
+
environment's definition ids.
|
|
158
315
|
|
|
159
316
|
### 3. The authority checks entitlement
|
|
160
317
|
|
|
@@ -164,13 +321,20 @@ the authority reads that Session's inventory. Add `"equip"` to the `match` in
|
|
|
164
321
|
|
|
165
322
|
```gdscript
|
|
166
323
|
"equip":
|
|
167
|
-
_equip(request, str(command.get("
|
|
324
|
+
_equip(request, str(command.get("hat", "")))
|
|
168
325
|
```
|
|
169
326
|
|
|
170
327
|
```gdscript
|
|
328
|
+
## The hats this game knows, by slug: the same in staging and production.
|
|
329
|
+
const HATS := ["party_hat", "top_hat"]
|
|
330
|
+
|
|
331
|
+
|
|
171
332
|
## A player asks to wear a hat. The authority reads that Session's inventory
|
|
172
333
|
## itself; a client saying "I own it" proves nothing.
|
|
173
|
-
func _equip(request: SummerNetworkCommandRequest,
|
|
334
|
+
func _equip(request: SummerNetworkCommandRequest, slug: String) -> void:
|
|
335
|
+
if not HATS.has(slug):
|
|
336
|
+
request.refuse(&"unknown_item")
|
|
337
|
+
return
|
|
174
338
|
var session := request.get_session()
|
|
175
339
|
var read := Summer.authority.items.inventory_for_session(session)
|
|
176
340
|
var result: SummerResult = await read.get_result_or_completed_signal()
|
|
@@ -178,39 +342,105 @@ func _equip(request: SummerNetworkCommandRequest, definition_id: String) -> void
|
|
|
178
342
|
request.refuse(&"inventory_unavailable") # retryable: try again later
|
|
179
343
|
return
|
|
180
344
|
for item in read.items:
|
|
181
|
-
if item.is_owned() and item.
|
|
345
|
+
if item.is_owned() and item.get_slug() == slug:
|
|
182
346
|
var hats: Dictionary = match_doc.get("hats", {})
|
|
183
|
-
hats[session.player.user_id] =
|
|
347
|
+
hats[session.player.user_id] = slug
|
|
184
348
|
match_doc["hats"] = hats
|
|
185
349
|
match_group.reset(Game.pack(match_doc))
|
|
186
|
-
request.accept({}, Game.pack({"equipped":
|
|
350
|
+
request.accept({}, Game.pack({"equipped": slug}))
|
|
187
351
|
return
|
|
188
352
|
request.refuse(&"not_owned")
|
|
189
353
|
```
|
|
190
354
|
|
|
191
|
-
The client sends `await command({"c": "equip", "
|
|
192
|
-
|
|
193
|
-
|
|
355
|
+
The client sends `await command({"c": "equip", "hat": slug})` and every client
|
|
356
|
+
draws hats from `match_doc.hats`. Other players see the hat only because the
|
|
357
|
+
authority published it.
|
|
358
|
+
|
|
359
|
+
**Without slugs** (Summer versions without the getters, or an item with no Item key):
|
|
360
|
+
keep each environment's definition ids, send the definition id, and compare
|
|
361
|
+
`item.get_definition_id() == definition_id` instead of `item.get_slug() == slug`.
|
|
362
|
+
|
|
363
|
+
For an equipment slot, `SummerItemSelection` does this check for you: set
|
|
364
|
+
`selection.allowed_slugs = PackedStringArray(["party_hat"])`, then call
|
|
365
|
+
`configure_slot` with an empty definition allowlist. An item whose inventory
|
|
366
|
+
carries no slug never matches, so the slot fails closed until it has one.
|
|
194
367
|
|
|
195
368
|
`Summer.authority.items.inventory_changed(session, change)` hints that a
|
|
196
369
|
player's inventory changed during the match. To drop an item that was
|
|
197
370
|
refunded mid-match, re-read with `inventory_for_session` (or
|
|
198
371
|
`refresh_inventory`) on that hint.
|
|
199
372
|
|
|
200
|
-
### 4.
|
|
373
|
+
### 4. Owned items and `maxOwned`
|
|
201
374
|
|
|
202
|
-
|
|
375
|
+
| Item | Can a player buy another? |
|
|
376
|
+
| --- | --- |
|
|
377
|
+
| Unique (`INSTANCE`, not consumable) | No, once they own one |
|
|
378
|
+
| `maxOwned` N (not stackable, not consumable) | Until they own N |
|
|
379
|
+
| Stackable (`QUANTITY`) or consumable | Yes; unaffected |
|
|
380
|
+
|
|
381
|
+
Summer counts owned copies plus purchases still settling. A checkout with no
|
|
382
|
+
room left is refused with **`409 already_owned`** before anything is charged,
|
|
383
|
+
and of two simultaneous purchases of the last copy, one is accepted. In game
|
|
384
|
+
code that is a `submission_failure` whose reason is `already_owned` (step 1):
|
|
385
|
+
show "Owned", not an error. The Summer apps' shops already show **Owned**
|
|
386
|
+
instead of Buy; in your own shop, set `owned` from the inventory and disable Buy.
|
|
387
|
+
|
|
388
|
+
### 5. Bundles and timed sales
|
|
389
|
+
|
|
390
|
+
They live on the store page draft; saving never publishes. Players see them
|
|
391
|
+
once the owner publishes the store page again (`store.publish`, owner-approved).
|
|
392
|
+
|
|
393
|
+
**In Studio** (`studio.open` target `/studio/store/:gameId`, the **Store page** tab):
|
|
394
|
+
|
|
395
|
+
- **Bundles** card: **Add bundle**, name it (the key comes from the name and is fixed once published), pick the Items (one without a price can't be picked; a stackable takes a quantity), set the Sparks price. The line under the price shows the Items total and "Save N%". Optional: a minimum price, and "On sale from" / "Until" dates. **Save bundles**. Removing a bundle removes its sales too.
|
|
396
|
+
- **Sales** card: **Add sale**, pick an Item or a bundle, the percent off and the start and end dates; it shows the result ("400 → 280 Sparks"). **Save sales**. A sale that has ended is dropped on save.
|
|
397
|
+
- **Sold only in bundles:** the checkbox on an Item's row in a bundle, or the toggle on the Item's page. A refusal shows at the field to fix.
|
|
398
|
+
|
|
399
|
+
**As an agent:** ask the owner before saving, then use `store.set_bundles` and `store.set_sales`.
|
|
400
|
+
`store.get_listing` returns `itemStore`: the bundles with `itemsTotalSparks` and `savePercent`, the sales with `listSparks` and `saleSparks`, `bundleOnlyItemIds`, and the Game's Items with ids and Sparks prices.
|
|
401
|
+
Each call **replaces the whole list**, so send every bundle or sale to keep; `[]` clears it. A bundle without a key keeps the saved key of the same name; `bundleOnlyItemIds` on `store.set_bundles` sets the bundle-only Items. A refusal names the field, e.g. `storeBundles[0].priceSparks`.
|
|
402
|
+
|
|
403
|
+
| | Rules |
|
|
404
|
+
| --- | --- |
|
|
405
|
+
| **Bundle** | A fixed list of 1–16 of the Game's items, 2–64 units in all (1 of a unique item, up to its stack for a stackable). A stable key, a name of at most 40 characters, up to 20 per Game. A purchased bundle never changes |
|
|
406
|
+
| Bundle price | The creator's Sparks price: at least 1 Spark per unit, below what its items cost alone. Shown with the items' total crossed out and "Save N%". Optional minimum price, and an optional available-from/until window (a limited-time bundle) |
|
|
407
|
+
| **Sale** | 1–90% off one item or one bundle, between a start and an end at most 90 days apart. The old price shows crossed out, with the end time |
|
|
408
|
+
| **Bundle-only item** | Sold only inside its bundles, never listed or bought alone (`409 not_sold_alone`) |
|
|
409
|
+
|
|
410
|
+
**A player who owns some of a bundle's unique items** gets those left out,
|
|
411
|
+
and the price drops by their share of the regular base prices:
|
|
412
|
+
`bundle price × base of the items not owned ÷ base of all its items`. It is
|
|
413
|
+
computed exactly, held at least at the minimum and at 1 Spark per unit, then
|
|
414
|
+
rounded up once, and never costs more than buying the remaining items
|
|
415
|
+
separately. Stackable and consumable items always count. Owning everything
|
|
416
|
+
shows **Owned**. Only the bundle total is promised; each item's share on the
|
|
417
|
+
receipt just splits it.
|
|
418
|
+
|
|
419
|
+
**Where players see them:** the Game shop in the Summer apps (website,
|
|
420
|
+
desktop, iPhone, Android) shows a Bundles row, "Save N%", "You own 2 of 5",
|
|
421
|
+
"Ends in …", and "This offer ended" (nothing charged) when a sale or bundle
|
|
422
|
+
window closes before the purchase.
|
|
423
|
+
|
|
424
|
+
**In your in-game shop:** `list_items` shows a running sale's price as the
|
|
425
|
+
item's price (no crossed-out price) and leaves bundle-only items out. Bundles
|
|
426
|
+
are not sold in game yet; point players to the Game shop in the Summer app.
|
|
427
|
+
|
|
428
|
+
### 6. Rules and why
|
|
429
|
+
|
|
430
|
+
- **Offer ids and quantities only.** Never send a price, an amount or a buyer. The platform prices everything, sales and bundles included, and a client-side price is what a cheater edits.
|
|
203
431
|
- **One `request_checkout` is one purchase.** Each call gets a new identity. Never call it again because a result is slow; `is_outcome_unknown()` means it may already be charged, and the status will still arrive.
|
|
204
432
|
- **Connect `checkout_status_changed` normally, before reading statuses.** A synchronous handler is how the SDK knows the result was delivered; a deferred-only handler leaves it undelivered.
|
|
205
433
|
- **`pending` and `manual_review` are not failures.** Don't show success or failure until a terminal state.
|
|
206
434
|
- **Carts:** 1–16 different offers, at most 64 units, as one atomic purchase.
|
|
207
435
|
- **Hide what's unavailable.** `check_browse_readiness()` and `check_checkout_readiness()` return a `SummerResult`; show the shop only when they're `ok`.
|
|
208
436
|
|
|
209
|
-
###
|
|
437
|
+
### 7. Test
|
|
210
438
|
|
|
211
439
|
**Local Play.** The store and checkout report `unavailable`, so the hidden-shop
|
|
212
440
|
path is what you test. Inventory and the authority check work from a fixture:
|
|
213
|
-
create `.summer/test-users.json` with item templates.
|
|
441
|
+
create `.summer/test-users.json` with item templates. Give them slugs with
|
|
442
|
+
`catalog_item_slugs`, only on a Summer version with slug getters; older ones
|
|
443
|
+
refuse the field.
|
|
214
444
|
|
|
215
445
|
```json
|
|
216
446
|
{
|
|
@@ -221,6 +451,7 @@ create `.summer/test-users.json` with item templates.
|
|
|
221
451
|
"world_definition": "world.json",
|
|
222
452
|
"service_outcomes": [],
|
|
223
453
|
"catalog_item_templates": ["hat"],
|
|
454
|
+
"catalog_item_slugs": [{"template": "hat", "slug": "party_hat"}],
|
|
224
455
|
"personas": [
|
|
225
456
|
{"persona_id": "alice", "display_name": "Alice", "participant_role": "player", "eligible_for_outcome": true, "session_lifetime": "match_bound"},
|
|
226
457
|
{"persona_id": "bob", "display_name": "Bob", "participant_role": "player", "eligible_for_outcome": true, "session_lifetime": "match_bound"}
|
|
@@ -231,29 +462,48 @@ create `.summer/test-users.json` with item templates.
|
|
|
231
462
|
What the fixture gives you:
|
|
232
463
|
|
|
233
464
|
- Every persona owns one item per template.
|
|
234
|
-
-
|
|
235
|
-
-
|
|
236
|
-
|
|
237
|
-
**Staging.**
|
|
465
|
+
- Each item has the slug you gave its template: Bob's hat is `party_hat`, so slug code behaves as it will once slugs are delivered.
|
|
466
|
+
- Other ids are local stand-ins: owned item id `local-item:bob:0`, definition id `local-definition:courtyard:0`, `item_template_version_id` `"hat"`. Without slugs, equip with the local definition id the client reads from `items.get_owned_items()`.
|
|
467
|
+
|
|
468
|
+
**Staging.** Summer staging is a separate environment: its own sign-in,
|
|
469
|
+
Games and definition ids (one `summer.items.json` still serves both). Your
|
|
470
|
+
production account and Games are not in it; if you have no staging account,
|
|
471
|
+
ask Summer for access. Purchases there use test payments: Sparks bought in
|
|
472
|
+
staging are sandbox Sparks, nothing real is charged, and they earn the
|
|
473
|
+
creator nothing. Test a purchase with a staging account that bought Sparks
|
|
474
|
+
in the staging Summer app.
|
|
475
|
+
|
|
476
|
+
Before testing, the item must be buyable in staging: reviewed, activated by
|
|
477
|
+
the owner, and the staging Game live ("How an item becomes buyable").
|
|
238
478
|
Walk through every outcome:
|
|
239
479
|
|
|
240
480
|
- decline the sheet;
|
|
241
481
|
- buy with too few Sparks;
|
|
482
|
+
- buy a unique item you already own: `already_owned`, nothing charged;
|
|
242
483
|
- buy, then kill the game while the purchase is pending and relaunch: the result must arrive exactly once.
|
|
243
484
|
|
|
244
485
|
## Checklist
|
|
245
486
|
|
|
246
487
|
- [ ] Local Play: the shop button is hidden, and there are no script errors.
|
|
247
488
|
- [ ] Local Play with the fixture: `items.refresh()` succeeds, the owned item is listed, `equip` is accepted for it and refused with `not_owned` for anything else.
|
|
248
|
-
- [ ]
|
|
249
|
-
- [ ]
|
|
250
|
-
- [ ] `
|
|
489
|
+
- [ ] Game code names items by slug, or by each environment's definition ids where slugs are not available.
|
|
490
|
+
- [ ] No code path creates an item, sends a price, or grants an item from a checkout status.
|
|
491
|
+
- [ ] Buy is disabled while `has_pending_checkouts()` is true, and for owned unique items.
|
|
492
|
+
- [ ] `manual_review` shows "confirming", and `already_owned` shows "Owned".
|
|
493
|
+
- [ ] Every `summer.items.json` sync: the dry-run plan was shown, then confirmed, then the owner got the approval link.
|
|
494
|
+
- [ ] Every new or changed item: Summer approved it, the owner pressed Review activation, and the Game is live.
|
|
251
495
|
- [ ] Staging: a purchase shows up in the inventory, and the equipped item appears for every player.
|
|
252
496
|
|
|
253
497
|
## Common mistakes
|
|
254
498
|
|
|
255
499
|
| Don't | Do | Why |
|
|
256
500
|
| --- | --- | --- |
|
|
501
|
+
| Create or grant items from game code | Define them in Studio, or in `summer.items.json` synced with `summer_sync_items` | Items are reviewed, and the owner approves every change |
|
|
502
|
+
| Hard-code definition ids | Match by slug | Definition ids differ between staging and production |
|
|
503
|
+
| Assume slug getters work everywhere | Check the Summer version; fall back to definition ids without them | 0.7.3 and older lack them; an item without an Item key reads `""` |
|
|
504
|
+
| Tell the owner an approved sync plan is on sale | Say each item still needs Summer's review, Review activation, and a live Game | Approving the plan only creates or revises the items |
|
|
505
|
+
| Confirm a sync without showing the plan | Dry run, show it, then `confirm` | The plan is what the owner approves |
|
|
506
|
+
| Treat `already_owned` as an error | Show "Owned" | A unique item is one per player |
|
|
257
507
|
| Unlock on `purchased` | Unlock from `Summer.client.items` | Ownership lives in the inventory; receipts can replay |
|
|
258
508
|
| Let the client tell the authority what it owns | `Summer.authority.items.inventory_for_session(session)` | Client claims are what cheaters forge |
|
|
259
509
|
| Send prices or amounts | Offer id + quantity only | The platform prices every purchase |
|
|
@@ -277,10 +527,16 @@ No Summer MCP tools in this session? Run each one as a command; when `summer --v
|
|
|
277
527
|
|
|
278
528
|
| In this skill | Command |
|
|
279
529
|
|---|---|
|
|
530
|
+
| `summer_sync_items` | `summer economy sync-items` |
|
|
280
531
|
| `summer_get_diagnostics` | `summer engine get-diagnostics` |
|
|
281
532
|
| `summer_get_script_errors` | `summer engine get-script-errors` |
|
|
282
533
|
| `summer_play` | `summer engine play` |
|
|
283
534
|
| `summer_project_setting` | `summer engine project-setting` |
|
|
535
|
+
| `summer_run_script` | `summer engine run-script` |
|
|
284
536
|
| `summer_stop` | `summer engine stop` |
|
|
537
|
+
| `store.get_listing` | `summer store get-listing` |
|
|
538
|
+
| `store.publish` | `summer store publish` |
|
|
539
|
+
| `store.set_bundles` | `summer store set-bundles` |
|
|
540
|
+
| `store.set_sales` | `summer store set-sales` |
|
|
285
541
|
| `studio.open` | `summer studio open` |
|
|
286
542
|
<!-- /shell-commands -->
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
id: skill/summer-store
|
|
2
2
|
kind: skill
|
|
3
|
-
version: 1.
|
|
4
|
-
summary: "Sell Summer items for Sparks
|
|
3
|
+
version: 1.2.0
|
|
4
|
+
summary: "Sell Summer items for Sparks: shop, checkout, item slugs, bundles and sales, owned items, summer.items.json sync, authority checks."
|
|
5
5
|
use_when:
|
|
6
6
|
- "adding a shop, in-game purchases, cosmetics or skins sold for Sparks to a Summer game"
|
|
7
7
|
- "checking on the authority that a player really owns an item before letting them use or equip it"
|
|
8
|
-
- "checkout shows pending, manual_review, insufficient_funds or unavailable, or bought items don't appear"
|
|
8
|
+
- "checkout shows pending, manual_review, insufficient_funds, already_owned or unavailable, or bought items don't appear"
|
|
9
|
+
- "recognizing items in game code by slug (item key) so one build works in staging and production"
|
|
10
|
+
- "defining items in summer.items.json and syncing them to the store for the owner's approval"
|
|
11
|
+
- "store bundles, Save %, timed sales, bundle-only items, or Owned instead of Buy"
|
|
9
12
|
facets:
|
|
10
13
|
lifecycle:
|
|
11
14
|
- build
|
|
@@ -17,7 +20,10 @@ facets:
|
|
|
17
20
|
compatibility:
|
|
18
21
|
engine: ">=4.6"
|
|
19
22
|
related:
|
|
23
|
+
tools:
|
|
24
|
+
- tool/sync-items
|
|
20
25
|
skills:
|
|
26
|
+
- skill/in-game-purchases
|
|
21
27
|
- skill/multiplayer
|
|
22
28
|
- skill/multiplayer-state
|
|
23
29
|
- skill/multiplayer-testing
|
|
@@ -114,11 +114,11 @@ If "regenerate", revise *one* axis (camera move, lighting, or framing) — don't
|
|
|
114
114
|
|
|
115
115
|
Trailer footage usually lives on the user's local disk and goes straight into the NLE, where the generated `.mp4` is exactly what you want.
|
|
116
116
|
|
|
117
|
-
For shots that double as **in-engine** splash content,
|
|
117
|
+
For shots that double as **in-engine** splash content, import the `.mp4` as it is. Summer Engine plays video only as Ogg Theora (`.ogv`), so the import converts it and saves it at the path with `.ogv`; the answer names it in `videoConversions`:
|
|
118
118
|
|
|
119
119
|
```
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
summer_import_from_url(url="<the shot's fileUrl>", path="res://assets/video/trailers/hero_boss_reveal.mp4")
|
|
121
|
+
→ res://assets/video/trailers/hero_boss_reveal.ogv
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
For pure trailer use (DaVinci / Premiere), the user can grab the `fileUrl` and download directly.
|
|
@@ -18,6 +18,11 @@ runtime (`src/core/templates.ts`).
|
|
|
18
18
|
2. Recompute `tree_digest` with the formula below and **refuse** on mismatch — the
|
|
19
19
|
directory is removed and nothing is written.
|
|
20
20
|
3. `git checkout <commit>` (detached). `.git` is removed unless `--keep-git`.
|
|
21
|
+
|
|
22
|
+
Without `--keep-git`, a github.com pin is first tried without git: the codeload
|
|
23
|
+
tarball of exactly `commit` is unpacked beside the target, the same digest is
|
|
24
|
+
computed from the archive (blob ids, bytewise path order, git's default path
|
|
25
|
+
quoting) and must match strictly; any failure falls back to the git steps above.
|
|
21
26
|
3. **Built-in template** (`builtin: true`, no pin): generated in-process by
|
|
22
27
|
`BUILTIN_GENERATORS` in `src/cli/commands/create.ts` — no download.
|
|
23
28
|
4. **Record** the pin into the project's `.summer/project.json`
|