gamesteel 0.5.3 → 0.5.4
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/dist/gamesteel.js +167 -130
- package/dist/pkgs/audio/dist/worker.js +3 -3
- package/dist/pkgs/model/dist/worker.js +3 -3
- package/dist/pkgs/paint/dist/worker.js +3 -3
- package/dist/pkgs/studio/dist/server/playtest-worker.js +138 -36
- package/dist/resolved/@gamesteel/online/worker/runtime.js +10 -10
- package/package.json +10 -10
package/dist/gamesteel.js
CHANGED
|
@@ -25339,7 +25339,7 @@ interface Purchases {
|
|
|
25339
25339
|
restore(): void
|
|
25340
25340
|
}
|
|
25341
25341
|
|
|
25342
|
-
/** The game's own server, for a game whose
|
|
25342
|
+
/** The game's own server, for a game whose Server page turned it on (the guide's online topic). Each ask goes to the
|
|
25343
25343
|
* server through the page; what comes back reaches every script on a later tick as onEvent('online', heard), where
|
|
25344
25344
|
* heard is { kind: 'answer', id, name, result, data?, reason? } (result 'ok', 'error', 'busy', 'resting' or 'refused'),
|
|
25345
25345
|
* { kind: 'message', room, data }, or { kind: 'joined' | 'left' | 'full', room }. A tick never waits on the network. */
|
|
@@ -25696,7 +25696,7 @@ interface Game {
|
|
|
25696
25696
|
/** An event every onEvent hears on the next tick; it also counts as the tick's event. */
|
|
25697
25697
|
emit(name: string, data?: Plain): void
|
|
25698
25698
|
/** The game's seeded random number in [0, 1); Math.random is the same stream. Each
|
|
25699
|
-
* play in the
|
|
25699
|
+
* play in the editor starts from a seed of its own; a playtest starts from seed 1,
|
|
25700
25700
|
* so its runs repeat. */
|
|
25701
25701
|
random(): number
|
|
25702
25702
|
/** A whole number from lo to hi, both included. */
|
|
@@ -25751,7 +25751,7 @@ interface Game {
|
|
|
25751
25751
|
/** Opens the game's privacy policy, as a game on the app stores must from inside itself: call it when the player
|
|
25752
25752
|
* taps the game's own Privacy button. The page knows where the policy is; with none yet, nothing opens. */
|
|
25753
25753
|
openPrivacy(): void
|
|
25754
|
-
/** Kept across restarts and levels, and between plays in the
|
|
25754
|
+
/** Kept across restarts and levels, and between plays in the editor (in that
|
|
25755
25755
|
* browser). A playtest starts with nothing saved. */
|
|
25756
25756
|
save(key: string, value: Plain): void
|
|
25757
25757
|
load(key: string): Plain
|
|
@@ -91906,7 +91906,7 @@ var SCRIPTS_PRIMER = [
|
|
|
91906
91906
|
" and sets that language and game.languages lists them. The HUD, the end screens and the menus write @hello for a",
|
|
91907
91907
|
" key's text. The person picks the language in the player's menu; the edit operation game.setStrings writes the tables.",
|
|
91908
91908
|
"- The person exports a game from the toolbar's Export: a zip whose page plays it from the disk or any web host and",
|
|
91909
|
-
" installs on a phone, playing as the
|
|
91909
|
+
" installs on a phone, playing as the editor does, saves in the browser and all.",
|
|
91910
91910
|
"- Mix freely: rules and scripts run in the same game, and a script may read what a rule changed.",
|
|
91911
91911
|
"",
|
|
91912
91912
|
"A kind's script:",
|
|
@@ -96179,7 +96179,7 @@ interface GameRequest {
|
|
|
96179
96179
|
json(): Json
|
|
96180
96180
|
}
|
|
96181
96181
|
|
|
96182
|
-
/** Players' files and the game's own, in the game's storage (turned on
|
|
96182
|
+
/** Players' files and the game's own, in the game's storage (turned on, on its Server page, with its database): a
|
|
96183
96183
|
* player's under players/<their id>/, so "Delete my online data" reaches them, the game's under game/. */
|
|
96184
96184
|
interface Files {
|
|
96185
96185
|
/** An address the player's page uploads exactly size bytes to, for 15 minutes or the seconds given: at most the
|
|
@@ -96195,7 +96195,7 @@ interface Files {
|
|
|
96195
96195
|
list(prefix: string): Promise<Array<{ key: string; size: number }>>
|
|
96196
96196
|
}
|
|
96197
96197
|
|
|
96198
|
-
/** Notifications to players' phones, through the game's own Firebase project (turned on
|
|
96198
|
+
/** Notifications to players' phones, through the game's own Firebase project (turned on, on its Server page, with its
|
|
96199
96199
|
* database). A player is reached once their app asked them (game.online.askPush) and they said yes. Each push costs
|
|
96200
96200
|
* one event. Never required to play, never an ad without the player's consent, never a reward for saying yes. */
|
|
96201
96201
|
interface Push {
|
|
@@ -96205,7 +96205,7 @@ interface Push {
|
|
|
96205
96205
|
|
|
96206
96206
|
/** What every function of the server is given. */
|
|
96207
96207
|
interface Ctx {
|
|
96208
|
-
/** The game's own database (Postgres): turned on
|
|
96208
|
+
/** The game's own database (Postgres): turned on, on its Server page. Keep each player's rows with a player_id column,
|
|
96209
96209
|
* so "Delete my online data" reaches them. */
|
|
96210
96210
|
readonly db: { query(sql: string, params?: readonly Json[]): Promise<{ rows: Record<string, Json>[] }> }
|
|
96211
96211
|
/** Live rooms: send to everyone in a room, or to one player in it. Who is in a room is room.players, in its hooks. */
|
|
@@ -96646,7 +96646,7 @@ var SOUND_TOPIC = [
|
|
|
96646
96646
|
"a noise layer, a brighter wave, a filter opened. It is muddy: highpass it, or lower the body. A click at its end: a",
|
|
96647
96647
|
"longer release. A loop jumps at its seam: end on notes that lead back. Compare two of the game's own with listen",
|
|
96648
96648
|
"{ names: ['own:hit', 'own:hit-heavy'] }. A playtest plays no audio, so the readout is how you hear it; the person",
|
|
96649
|
-
"hears it in the
|
|
96649
|
+
"hears it when they play it in the editor."
|
|
96650
96650
|
];
|
|
96651
96651
|
|
|
96652
96652
|
// ../agent/dist/sight.js
|
|
@@ -97058,7 +97058,7 @@ var PLAYTEST_TOOLS = [
|
|
|
97058
97058
|
},
|
|
97059
97059
|
{
|
|
97060
97060
|
name: "playtest_look",
|
|
97061
|
-
description: "a picture of the game now, as the person sees it in the
|
|
97061
|
+
description: "a picture of the game now, as the person sees it when they play it in the editor, and the data of the objects you name; or, with frames, a strip of moments side by side in one picture, each labelled with its time. Pictures cost money: look at the moments that matter, not after every step.",
|
|
97062
97062
|
inputSchema: object2({
|
|
97063
97063
|
game: GAME,
|
|
97064
97064
|
scene: SCENE,
|
|
@@ -97929,7 +97929,7 @@ var FILM = [
|
|
|
97929
97929
|
" second, its sound mixed from the ticks that asked for it. Nobody watches it live as it renders, so a film too",
|
|
97930
97930
|
" heavy to play smoothly live comes out as smooth as a light one.",
|
|
97931
97931
|
"- Each later answer about the game says how the render goes, then how it ended. With no editor open, ask the person",
|
|
97932
|
-
" to open the game and press Render video in
|
|
97932
|
+
" to open the game and press Render video in Upload \u203A Films.",
|
|
97933
97933
|
"- The exported game plays a film's video in the film's place, in the player's language, with pause, chapters and",
|
|
97934
97934
|
" skip; a film with no video in that language plays live. After any edit a video is older than the game: render it",
|
|
97935
97935
|
" again before the person exports.",
|
|
@@ -97942,11 +97942,11 @@ var FILM = [
|
|
|
97942
97942
|
var PUBLISH = [
|
|
97943
97943
|
"# Publishing a game",
|
|
97944
97944
|
"",
|
|
97945
|
-
"A finished game goes out from the editor's Upload
|
|
97945
|
+
"A finished game goes out from the editor's Upload page. Every account it goes to is the person's own: their GitHub,",
|
|
97946
97946
|
"their Google Play and App Store developer accounts, their AdMob. Gamesteel makes the files and walks them through each",
|
|
97947
97947
|
"step; the stores' clicks, checkboxes and agreements are theirs.",
|
|
97948
97948
|
"",
|
|
97949
|
-
"## The Upload
|
|
97949
|
+
"## The Upload page",
|
|
97950
97950
|
"",
|
|
97951
97951
|
"- Export: a zip whose index.html plays the game from the disk, and installs as an app once any website serves it.",
|
|
97952
97952
|
"- GitHub: one repo per game, in the person's own GitHub account, which they connect in the menu; each save can go to it.",
|
|
@@ -97979,7 +97979,7 @@ var PUBLISH = [
|
|
|
97979
97979
|
"",
|
|
97980
97980
|
"A game going to the App Store or Google Play opens its privacy policy from inside itself: add a small Privacy button on",
|
|
97981
97981
|
"its title or pause menu, drawn like its other buttons, that calls this.game.openPrivacy() (the scripts topic shows",
|
|
97982
|
-
"one). The person writes and confirms the policy in the editor's Upload
|
|
97982
|
+
"one). The person writes and confirms the policy in the editor's Upload page, in the App Store's wizard or Google",
|
|
97983
97983
|
"Play's. A game with ads gets an ads part in the policy's draft, and the policy is confirmed again whenever the",
|
|
97984
97984
|
"game's ads change. A game with its own server gets an online part, and its Privacy button offers Delete my online",
|
|
97985
97985
|
"data: the online topic has it. A game that sells gets a purchases part: the purchases topic has it.",
|
|
@@ -98004,7 +98004,7 @@ var PUBLISH = [
|
|
|
98004
98004
|
" It warns of a break while the player is in the middle of play, and of a banner beside one of the game's buttons.",
|
|
98005
98005
|
"- In a store app, the Privacy button also opens Ad choices, where Google's consent asks for them: the player's ad",
|
|
98006
98006
|
" choices, which the law asks they can change.",
|
|
98007
|
-
"- A game whose audience includes children under 13 (
|
|
98007
|
+
"- A game whose audience includes children under 13 (Upload \u203A App asks) is made for children: it shows no ads, every",
|
|
98008
98008
|
" ad is refused, and its builds carry no ad library. Never place ads in a game for children.",
|
|
98009
98009
|
"- An APK to try and the Xcode project download show Google's test ads, whatever ids the person gave; a release shows",
|
|
98010
98010
|
" their real ones. Tell the person never to tap their own real ads, even to test: AdMob can close the account. They",
|
|
@@ -98029,7 +98029,7 @@ var ONLINE = () => [
|
|
|
98029
98029
|
"",
|
|
98030
98030
|
"A game's own server does what one device can't: a leaderboard, a save that follows the player, players together, an",
|
|
98031
98031
|
"outside service with a secret key. It is the person's choice and money: the person turns it on, sizes it, pays for it",
|
|
98032
|
-
"and publishes it
|
|
98032
|
+
"and publishes it on the editor's Server page. You write its code; you never turn it on, resize it, spend or publish.",
|
|
98033
98033
|
"",
|
|
98034
98034
|
"## The code",
|
|
98035
98035
|
"",
|
|
@@ -98040,7 +98040,7 @@ var ONLINE = () => [
|
|
|
98040
98040
|
" is given the player (an anonymous id, checked before the code runs), the data the game sent, and ctx; what it returns",
|
|
98041
98041
|
" is the answer's data, and what it throws the answer's error, in its words.",
|
|
98042
98042
|
"- ctx.db is the game's own Postgres database, once the person turns it on. ctx.secrets holds the game's secrets by",
|
|
98043
|
-
" name, whose values the person sets
|
|
98043
|
+
" name, whose values the person sets on the Server page: never write a key in code. The rest of ctx is below.",
|
|
98044
98044
|
"- Check every field a player sends, its type and its range, as the patterns do.",
|
|
98045
98045
|
"",
|
|
98046
98046
|
"The server API, exactly as server code is checked against it:",
|
|
@@ -98064,16 +98064,16 @@ var ONLINE = () => [
|
|
|
98064
98064
|
"",
|
|
98065
98065
|
"- Every table keeps the player's id in a player_id column. Delete my online data, behind the game's Privacy button,",
|
|
98066
98066
|
" deletes every row whose player_id is the player's, then runs onForget, where the code deletes anything else it keeps",
|
|
98067
|
-
" of them. The
|
|
98067
|
+
" of them. The Server page marks a table with no player_id.",
|
|
98068
98068
|
"- The privacy policy and the store forms follow the game's online, as they follow its ads: the person confirms them",
|
|
98069
|
-
" again in the Upload
|
|
98069
|
+
" again in the Upload page's wizards when its server, database, live rooms, files, push or purchases change (the",
|
|
98070
98070
|
" publish topic).",
|
|
98071
98071
|
"- onPurchase, onRevoke and onReward hear what players buy and the ads they watch for a reward, each checked first",
|
|
98072
98072
|
" with its store or AdMob: the purchases topic has them.",
|
|
98073
98073
|
"",
|
|
98074
98074
|
"## Players' files",
|
|
98075
98075
|
"",
|
|
98076
|
-
"- ctx.files keeps the game's files beside its database, once the person turns them on
|
|
98076
|
+
"- ctx.files keeps the game's files beside its database, once the person turns them on, on the Server page: a player's",
|
|
98077
98077
|
" under players/<their id>/, the game's own under game/; a key is at most 512 bytes, with no '..'.",
|
|
98078
98078
|
"- this.game.online.upload('saveLevel', file, data) sends a file of plain data (at most 1,000,000 bytes as JSON);",
|
|
98079
98079
|
" uploadScreen('saveShot', data) sends the frame as a PNG. The page asks that call with { size, type, data }; the call",
|
|
@@ -98094,15 +98094,15 @@ var ONLINE = () => [
|
|
|
98094
98094
|
"- Ask after a moment that shows why (a match ended, a level shared), never at launch. Push is never needed to play,",
|
|
98095
98095
|
" never an ad the player didn't agree to, never a reward for saying yes (App Store Review Guideline 4.5.4, Google",
|
|
98096
98096
|
" Play's notifications policy).",
|
|
98097
|
-
"- The person's steps, in their own Firebase project, which the
|
|
98097
|
+
"- The person's steps, in their own Firebase project, which the Server page says: add the game's Android and Apple apps;",
|
|
98098
98098
|
" Apple's push key goes into Project settings \u203A Cloud Messaging; the service account and the two app files go into",
|
|
98099
|
-
" the
|
|
98099
|
+
" the Server page, and each app build carries its own.",
|
|
98100
98100
|
"",
|
|
98101
98101
|
"## Test and live",
|
|
98102
98102
|
"",
|
|
98103
|
-
"-
|
|
98103
|
+
"- Playing in the editor runs the game against its test server, and players against live. Each save that changes the",
|
|
98104
98104
|
" server code goes to test a moment later; the person sends test's code to players with Publish to players in the",
|
|
98105
|
-
"
|
|
98105
|
+
" Server page, and can undo it there. Test and live each have their own database and secrets: test's data is never",
|
|
98106
98106
|
" players'.",
|
|
98107
98107
|
"- Old apps keep calling: ctx.version says which, so a change can answer them the old way.",
|
|
98108
98108
|
"",
|
|
@@ -98122,13 +98122,13 @@ var ONLINE = () => [
|
|
|
98122
98122
|
" person turns on, and where.",
|
|
98123
98123
|
"- app online_logs reads the server's logs, stage test or live.",
|
|
98124
98124
|
"- app online_query reads the game's data with one read-only SQL statement: here, where playtests write it, unless",
|
|
98125
|
-
" stage says test, or live once the person allows it
|
|
98125
|
+
" stage says test, or live once the person allows it on the Server page.",
|
|
98126
98126
|
"",
|
|
98127
98127
|
"## What it costs",
|
|
98128
98128
|
"",
|
|
98129
98129
|
`- Online is paid by use from the workspace's wallet, apart from its plan: the person adds credit on the Plan page, and`,
|
|
98130
98130
|
` it turns on with $10 or more there. A small game's month (about 10,000 calls a day, its database awake two hours a`,
|
|
98131
|
-
` day) costs about ${dollars(priceOf(TYPICAL_MONTH))}; the
|
|
98131
|
+
` day) costs about ${dollars(priceOf(TYPICAL_MONTH))}; the Server page and the Plan page give every price.`,
|
|
98132
98132
|
"- Each size holds its ceilings (calls a second, players at once, work a call, the database's compute and data); a call",
|
|
98133
98133
|
" past them answers busy.",
|
|
98134
98134
|
"- When the wallet runs out, the game's server pauses: the game keeps running, its calls answer resting, and nothing",
|
|
@@ -98136,8 +98136,8 @@ var ONLINE = () => [
|
|
|
98136
98136
|
"",
|
|
98137
98137
|
"## Games made for children",
|
|
98138
98138
|
"",
|
|
98139
|
-
"- A game made for children (
|
|
98140
|
-
"
|
|
98139
|
+
"- A game made for children (Upload \u203A App asks) keeps its live rooms, player files and push off until the person confirms,",
|
|
98140
|
+
" on the Server page, that each is designed to be safe. Design them so: no free text between players, only preset",
|
|
98141
98141
|
" phrases, emotes and moves; no name a player types; nothing that says who or where a player is; files only the game",
|
|
98142
98142
|
" makes, like a level; notifications that never sell and never reward.",
|
|
98143
98143
|
"- Keep no more of anyone than the game needs.",
|
|
@@ -98243,10 +98243,10 @@ var PURCHASES = [
|
|
|
98243
98243
|
"- Google's check uses the workspace's Play key file, the one that uploads: its account needs View financial data and",
|
|
98244
98244
|
" Manage orders in Play Console \u203A Users and permissions. Until the workspace's owner adds it under Store access,",
|
|
98245
98245
|
" Google's purchases are checked on the phone.",
|
|
98246
|
-
"- Refunds and renewals reach the game's own addresses, which the
|
|
98246
|
+
"- Refunds and renewals reach the game's own addresses, which the Server page gives. Apple's: the App Store wizard's",
|
|
98247
98247
|
" Purchases step sets it. Google's: the person makes a Pub/Sub topic in their Google Cloud project, names it in Play",
|
|
98248
98248
|
" Console \u203A Monetize \u203A Monetization setup, and adds a push subscription to the game's address, authenticating as the",
|
|
98249
|
-
" service account of the workspace's Play key file; a notice sent as anyone else is refused. The
|
|
98249
|
+
" service account of the workspace's Play key file; a notice sent as anyone else is refused. The Server page says each",
|
|
98250
98250
|
" step.",
|
|
98251
98251
|
"",
|
|
98252
98252
|
"## Checked rewards",
|
|
@@ -98256,7 +98256,7 @@ var PURCHASES = [
|
|
|
98256
98256
|
" reward, ctx) hears it once: { placement, amount, item, transaction }. Give the reward there; the game asks its",
|
|
98257
98257
|
" server for it.",
|
|
98258
98258
|
"- The person pastes that address into each rewarded ad unit in AdMob, under Advanced settings \u203A Server-side",
|
|
98259
|
-
" verification; the
|
|
98259
|
+
" verification; the Server page gives it. Only rewards from the game's own rewarded ad units (Upload \u203A Ads) count: AdMob",
|
|
98260
98260
|
" signs every account's alike. AdMob's call can come a few seconds after the ad ends: ask when the ad says rewarded,",
|
|
98261
98261
|
" and once more a moment later.",
|
|
98262
98262
|
"- The server keeps each reward under the player's id, which Delete my online data deletes too.",
|
|
@@ -98273,7 +98273,7 @@ var PURCHASES = [
|
|
|
98273
98273
|
"",
|
|
98274
98274
|
"## Testing",
|
|
98275
98275
|
"",
|
|
98276
|
-
"-
|
|
98276
|
+
"- When the person plays in the editor, each buy is a card they answer: Bought, Cancelled or Pending. With the test server",
|
|
98277
98277
|
" granting purchases, Bought reaches its onPurchase as a test purchase, and a reward watched reaches its onReward.",
|
|
98278
98278
|
"- A playtest buys by itself, granted as a test purchase, and a restore finds nothing owned; with onPurchase, the buy",
|
|
98279
98279
|
" goes to the server on this machine first. The answer lists each, like purchases: buy coins_100 granted at 1.2 s,",
|
|
@@ -98488,7 +98488,7 @@ var GUIDE_TOPICS = [
|
|
|
98488
98488
|
{ name: "model", about: "art model: 3D art Blender builds from a script, when asked for 3D", text: () => MODEL.join("\n") },
|
|
98489
98489
|
{ name: "team", about: "working beside teammates and their agents: board, parts, claims, notes", text: () => TEAM.join("\n") },
|
|
98490
98490
|
{ name: "film", about: "films: levels that play themselves, cutscenes, chapters, render to MP4", text: () => FILM.join("\n") },
|
|
98491
|
-
{ name: "publish", about: "the Upload
|
|
98491
|
+
{ name: "publish", about: "the Upload page: export, GitHub, app builds, stores, privacy, ads", text: () => PUBLISH.join("\n") },
|
|
98492
98492
|
{ name: "online", about: "a game's own server: code, database, live rooms, test and live, costs", text: () => ONLINE().join("\n") },
|
|
98493
98493
|
{ name: "purchases", about: "selling in store apps, and ad rewards the server verifies", text: () => PURCHASES.join("\n") }
|
|
98494
98494
|
];
|
|
@@ -106796,7 +106796,7 @@ function readArt2(args2, ctx) {
|
|
|
106796
106796
|
|
|
106797
106797
|
${painting}` : modelled !== null ? `${head}, modelled in 3D.${earlier} its model, to change with art model's edit or give whole again, or to render again with art model and its name alone:
|
|
106798
106798
|
|
|
106799
|
-
${modelled}` : by === "code" ? `${head}, made with code in the Art
|
|
106799
|
+
${modelled}` : by === "code" ? `${head}, made with code in the Art view (the person's Make with code).${earlier} paint it with art paint for a finished look` : `${head}, imported, not painted: the person's own, never replaced`;
|
|
106800
106800
|
const shown3 = answer(body3, { name: `own:${name2}`, kind: entry.kind, painted: painting !== null, modelled: modelled !== null });
|
|
106801
106801
|
return { ...shown3, content: [...shown3.content, ...picture5] };
|
|
106802
106802
|
}
|
|
@@ -107254,7 +107254,7 @@ var APP_TOOLS = [
|
|
|
107254
107254
|
},
|
|
107255
107255
|
{
|
|
107256
107256
|
name: "app_online",
|
|
107257
|
-
description: "the game's own server (guide online): sizes, today's calls and errors, cost and wallet, versions in use; what the person turns on
|
|
107257
|
+
description: "the game's own server (guide online): sizes, today's calls and errors, cost and wallet, versions in use; what the person turns on, on the editor's Server page.",
|
|
107258
107258
|
inputSchema: object2({ game: GAME }, ["game"]),
|
|
107259
107259
|
handle: (args2, ctx) => sent(args2, ctx, { action: "online" }, ONLINE2)
|
|
107260
107260
|
},
|
|
@@ -107266,7 +107266,7 @@ var APP_TOOLS = [
|
|
|
107266
107266
|
},
|
|
107267
107267
|
{
|
|
107268
107268
|
name: "app_online_query",
|
|
107269
|
-
description: "one read-only SQL statement on its data: here (playtests'), test, or live once the person allows it in the
|
|
107269
|
+
description: "one read-only SQL statement on its data: here (playtests'), test, or live once the person allows it in the Server page.",
|
|
107270
107270
|
inputSchema: object2({
|
|
107271
107271
|
game: GAME,
|
|
107272
107272
|
sql: { type: "string", minLength: 1, maxLength: 4e3, description: "one statement, like select player_id, score from scores" },
|
|
@@ -107337,7 +107337,7 @@ var GITHUB_TOOLS = [
|
|
|
107337
107337
|
// ../agent/dist/film-tools.js
|
|
107338
107338
|
var NEEDS_PROGRAM8 = "rendering video runs in the Gamesteel program on your machine, which asks the editor with the game open: add it to your agent with npx gamesteel@latest connect <agent>";
|
|
107339
107339
|
var STARTED = "Rendering started in the editor where the game is open. It renders each film with no video, or an old one, once for each of the game's languages, and keeps the videos with the game; each answer about the game says how it goes, and how it ended.";
|
|
107340
|
-
var NONE = "No editor has this game open: ask the person to open it in the editor and press Render video in
|
|
107340
|
+
var NONE = "No editor has this game open: ask the person to open it in the editor and press Render video in Upload \u203A Films, or to keep it open and ask you again.";
|
|
107341
107341
|
var FILM_TOOLS = [
|
|
107342
107342
|
{
|
|
107343
107343
|
name: "render_video",
|
|
@@ -112085,6 +112085,110 @@ function studioBackend(run3, session, headers = {}, timings = {}) {
|
|
|
112085
112085
|
|
|
112086
112086
|
// ../studio/dist/server/http.js
|
|
112087
112087
|
import { randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
112088
|
+
|
|
112089
|
+
// ../design/dist/tokens.js
|
|
112090
|
+
var PALETTE2 = {
|
|
112091
|
+
"bg": "#050607",
|
|
112092
|
+
/** A panel's solid stand-in, and what the scene canvas reads. */
|
|
112093
|
+
"surface": "#101216",
|
|
112094
|
+
/** Raised, solid: banners, and floating glass where blur is missing. */
|
|
112095
|
+
"surface-2": "#181B20",
|
|
112096
|
+
/** The code editor's well. */
|
|
112097
|
+
"field": "#08090B",
|
|
112098
|
+
/** The scene canvas's grid and frame; glass draws its hairlines in white. */
|
|
112099
|
+
"line": "#232833",
|
|
112100
|
+
"line-strong": "#2E3440",
|
|
112101
|
+
"text": "#F3F5F8",
|
|
112102
|
+
"text-2": "#B0B6C0",
|
|
112103
|
+
"text-3": "#999FAA",
|
|
112104
|
+
/** The editor's mint. The renderer's palette and the chrome still agree. */
|
|
112105
|
+
"accent": "#5ED3A0",
|
|
112106
|
+
/** The top of the primary button's gradient, where light catches it. */
|
|
112107
|
+
"accent-hi": "#8EF2C7",
|
|
112108
|
+
/** Words on an accent fill. */
|
|
112109
|
+
"accent-ink": "#06281A",
|
|
112110
|
+
/** Accent chips, solid. */
|
|
112111
|
+
"accent-soft": "#163A2C",
|
|
112112
|
+
/** The AI's colour in the editor: its edits, its outlines, its entries in
|
|
112113
|
+
* the console. Reserved for it, so a person always knows which changes
|
|
112114
|
+
* were not theirs. */
|
|
112115
|
+
"ai": "#8EAEFF",
|
|
112116
|
+
/** The forge's red: the landing page's room glows in it (the owner, 2026-10-12: "put the glow on red"), and it is
|
|
112117
|
+
* the site's accent where the editor has its mint. */
|
|
112118
|
+
"forge": "#EC1B1C",
|
|
112119
|
+
/** The forge's red, light enough to read as words: the site's code and its accent text. */
|
|
112120
|
+
"forge-hi": "#FF6B5E",
|
|
112121
|
+
/** Words on a forge fill. */
|
|
112122
|
+
"forge-ink": "#2B0705",
|
|
112123
|
+
/** The forge's red as the editor's words: lighter than forge-hi, so it reads at 4.5:1 on the editor's lightest
|
|
112124
|
+
* grounds (a hovered row, the selection and the glass over a picture, in a lit room). */
|
|
112125
|
+
"forge-text": "#FF867B",
|
|
112126
|
+
/** What is well and live: the editor's Server with a version live, a teammate in the editor (2026-10-27). */
|
|
112127
|
+
"good": "#6EDB9A",
|
|
112128
|
+
"warn": "#F2B65E",
|
|
112129
|
+
"warn-soft": "#3A2C14",
|
|
112130
|
+
"bad": "#F28A92",
|
|
112131
|
+
"bad-soft": "#3D1D22"
|
|
112132
|
+
};
|
|
112133
|
+
var SPARK = {
|
|
112134
|
+
white: "#FFF3D6",
|
|
112135
|
+
yellow: "#FFC24A",
|
|
112136
|
+
orange: "#FF7A1C",
|
|
112137
|
+
red: "#B8320F"
|
|
112138
|
+
};
|
|
112139
|
+
var CHROME = [
|
|
112140
|
+
[0, "#FFFFFF"],
|
|
112141
|
+
[14, "#FFFFFF"],
|
|
112142
|
+
[30, "#E6E9EE"],
|
|
112143
|
+
[44, "#A9AFB9"],
|
|
112144
|
+
[50, "#3D4148"],
|
|
112145
|
+
[56, "#5D636C"],
|
|
112146
|
+
[66, "#C7CCD4"],
|
|
112147
|
+
[74, "#F0D2B6"],
|
|
112148
|
+
[84, "#FF9A4A"],
|
|
112149
|
+
[100, SPARK.orange]
|
|
112150
|
+
];
|
|
112151
|
+
var GRAIN_ALPHA = 0.06;
|
|
112152
|
+
var GRAIN = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 ${String(GRAIN_ALPHA)} 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E")`;
|
|
112153
|
+
var ICON = (paths) => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E${paths}%3C/svg%3E")`;
|
|
112154
|
+
var ICON_ALERT = ICON("%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E");
|
|
112155
|
+
var ICON_CHECK = ICON("%3Cpath d='M20 6 9 17l-5-5'/%3E");
|
|
112156
|
+
var CHEVRON = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23${PALETTE2["text-2"].slice(1)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`;
|
|
112157
|
+
|
|
112158
|
+
// ../studio/dist/loading-mark.js
|
|
112159
|
+
var LOADING_MARK = ["svg", { class: "ed-loading-mark", viewBox: "0 0 160 100", width: "160", height: "100", "aria-hidden": "true", focusable: "false" }, [
|
|
112160
|
+
["defs", {}, [
|
|
112161
|
+
[
|
|
112162
|
+
"linearGradient",
|
|
112163
|
+
{ id: "ed-loading-chrome", gradientUnits: "userSpaceOnUse", x1: "0", y1: "28", x2: "0", y2: "72" },
|
|
112164
|
+
CHROME.map(([at5, hex5]) => ["stop", { offset: `${String(at5)}%`, "stop-color": hex5 }])
|
|
112165
|
+
],
|
|
112166
|
+
["radialGradient", { id: "ed-loading-heat" }, [
|
|
112167
|
+
["stop", { offset: "0%", "stop-color": SPARK.orange, "stop-opacity": "0.75" }],
|
|
112168
|
+
["stop", { offset: "55%", "stop-color": SPARK.red, "stop-opacity": "0.25" }],
|
|
112169
|
+
["stop", { offset: "100%", "stop-color": SPARK.red, "stop-opacity": "0" }]
|
|
112170
|
+
]],
|
|
112171
|
+
["filter", { id: "ed-loading-melt", filterUnits: "userSpaceOnUse", x: "0", y: "0", width: "160", height: "100" }, [
|
|
112172
|
+
["feGaussianBlur", { in: "SourceGraphic", stdDeviation: "4.5" }],
|
|
112173
|
+
["feColorMatrix", { values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -8" }]
|
|
112174
|
+
]]
|
|
112175
|
+
]],
|
|
112176
|
+
["ellipse", { class: "ed-loading-heat", cx: "80", cy: "78", rx: "58", ry: "12", fill: "url(#ed-loading-heat)" }],
|
|
112177
|
+
["g", { filter: "url(#ed-loading-melt)", fill: "url(#ed-loading-chrome)" }, [
|
|
112178
|
+
["circle", { class: "ed-loading-drop is-left", cx: "67", cy: "50", r: "20" }],
|
|
112179
|
+
["circle", { class: "ed-loading-drop is-right", cx: "95", cy: "51", r: "16" }]
|
|
112180
|
+
]],
|
|
112181
|
+
["ellipse", { class: "ed-loading-shine is-left", cx: "60", cy: "39", rx: "7", ry: "2.6", fill: "#FFFFFF" }],
|
|
112182
|
+
["ellipse", { class: "ed-loading-shine is-right", cx: "90", cy: "42", rx: "5", ry: "2", fill: "#FFFFFF" }]
|
|
112183
|
+
]];
|
|
112184
|
+
var attribute = (text3) => text3.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
112185
|
+
function markupOf([tag, attributes, inside2 = []]) {
|
|
112186
|
+
const named3 = Object.entries(attributes).map(([name2, value]) => ` ${name2}="${attribute(value)}"`).join("");
|
|
112187
|
+
return inside2.length === 0 ? `<${tag}${named3}/>` : `<${tag}${named3}>${inside2.map(markupOf).join("")}</${tag}>`;
|
|
112188
|
+
}
|
|
112189
|
+
var LOADING_PAGE = `<div class="ed-loading"><span class="ed-loading-art">${markupOf(LOADING_MARK)}</span><div class="ed-loading-words"></div><div class="ed-loading-bar"></div><div class="ed-loading-slow"></div></div>`;
|
|
112190
|
+
|
|
112191
|
+
// ../studio/dist/server/http.js
|
|
112088
112192
|
import { createServer as createServer2 } from "node:http";
|
|
112089
112193
|
function paramsOf(pattern, path) {
|
|
112090
112194
|
if (!pattern.includes(":") && !pattern.endsWith("/*"))
|
|
@@ -113317,7 +113421,7 @@ function editRoutes(manager, options = {}) {
|
|
|
113317
113421
|
method: "GET",
|
|
113318
113422
|
path: `${base}/scripts`,
|
|
113319
113423
|
// The game's scripts as files (the free engine spec, §2.5), each with
|
|
113320
|
-
// what the type checker found in it, for the Code
|
|
113424
|
+
// what the type checker found in it, for the Code view and the Console.
|
|
113321
113425
|
handle: (_req, res, _body, params) => {
|
|
113322
113426
|
game(res, params["slug"], (s5) => {
|
|
113323
113427
|
const version2 = s5.version;
|
|
@@ -114942,7 +115046,7 @@ ${said4}` };
|
|
|
114942
115046
|
}
|
|
114943
115047
|
/** What a frame of this game is drawn with: the library and the game's
|
|
114944
115048
|
* own art, each image by its file, and the HUD in the game's style, as
|
|
114945
|
-
* the
|
|
115049
|
+
* the editor's run draws it. */
|
|
114946
115050
|
artOf(slug) {
|
|
114947
115051
|
const { store } = this.options.manager;
|
|
114948
115052
|
this.library ??= libraryCatalogue().map((entry) => ({ ...entry, src: join18(LIBRARY_ROOT2, entry.src) }));
|
|
@@ -116141,73 +116245,6 @@ import { dirname as dirname8, join as join20, resolve as resolve3 } from "node:p
|
|
|
116141
116245
|
import { promisify } from "node:util";
|
|
116142
116246
|
import { gzip } from "node:zlib";
|
|
116143
116247
|
|
|
116144
|
-
// ../design/dist/tokens.js
|
|
116145
|
-
var PALETTE2 = {
|
|
116146
|
-
"bg": "#050607",
|
|
116147
|
-
/** A panel's solid stand-in, and what the scene canvas reads. */
|
|
116148
|
-
"surface": "#101216",
|
|
116149
|
-
/** Raised, solid: banners, and floating glass where blur is missing. */
|
|
116150
|
-
"surface-2": "#181B20",
|
|
116151
|
-
/** The code editor's well. */
|
|
116152
|
-
"field": "#08090B",
|
|
116153
|
-
/** The scene canvas's grid and frame; glass draws its hairlines in white. */
|
|
116154
|
-
"line": "#232833",
|
|
116155
|
-
"line-strong": "#2E3440",
|
|
116156
|
-
"text": "#F3F5F8",
|
|
116157
|
-
"text-2": "#B0B6C0",
|
|
116158
|
-
"text-3": "#999FAA",
|
|
116159
|
-
/** The editor's mint. The renderer's palette and the chrome still agree. */
|
|
116160
|
-
"accent": "#5ED3A0",
|
|
116161
|
-
/** The top of the primary button's gradient, where light catches it. */
|
|
116162
|
-
"accent-hi": "#8EF2C7",
|
|
116163
|
-
/** Words on an accent fill. */
|
|
116164
|
-
"accent-ink": "#06281A",
|
|
116165
|
-
/** Accent chips, solid. */
|
|
116166
|
-
"accent-soft": "#163A2C",
|
|
116167
|
-
/** The AI's colour in the editor: its edits, its outlines, its entries in
|
|
116168
|
-
* the console. Reserved for it, so a person always knows which changes
|
|
116169
|
-
* were not theirs. */
|
|
116170
|
-
"ai": "#8EAEFF",
|
|
116171
|
-
/** The forge's red: the landing page's room glows in it (the owner, 2026-10-12: "put the glow on red"), and it is
|
|
116172
|
-
* the site's accent where the editor has its mint. */
|
|
116173
|
-
"forge": "#EC1B1C",
|
|
116174
|
-
/** The forge's red, light enough to read as words: the site's code and its accent text. */
|
|
116175
|
-
"forge-hi": "#FF6B5E",
|
|
116176
|
-
/** Words on a forge fill. */
|
|
116177
|
-
"forge-ink": "#2B0705",
|
|
116178
|
-
/** The forge's red as the editor's words: lighter than forge-hi, so it reads at 4.5:1 on the editor's lightest
|
|
116179
|
-
* grounds (a hovered row, the selection and the glass over a picture, in a lit room). */
|
|
116180
|
-
"forge-text": "#FF867B",
|
|
116181
|
-
"warn": "#F2B65E",
|
|
116182
|
-
"warn-soft": "#3A2C14",
|
|
116183
|
-
"bad": "#F28A92",
|
|
116184
|
-
"bad-soft": "#3D1D22"
|
|
116185
|
-
};
|
|
116186
|
-
var SPARK = {
|
|
116187
|
-
white: "#FFF3D6",
|
|
116188
|
-
yellow: "#FFC24A",
|
|
116189
|
-
orange: "#FF7A1C",
|
|
116190
|
-
red: "#B8320F"
|
|
116191
|
-
};
|
|
116192
|
-
var CHROME = [
|
|
116193
|
-
[0, "#FFFFFF"],
|
|
116194
|
-
[14, "#FFFFFF"],
|
|
116195
|
-
[30, "#E6E9EE"],
|
|
116196
|
-
[44, "#A9AFB9"],
|
|
116197
|
-
[50, "#3D4148"],
|
|
116198
|
-
[56, "#5D636C"],
|
|
116199
|
-
[66, "#C7CCD4"],
|
|
116200
|
-
[74, "#F0D2B6"],
|
|
116201
|
-
[84, "#FF9A4A"],
|
|
116202
|
-
[100, SPARK.orange]
|
|
116203
|
-
];
|
|
116204
|
-
var GRAIN_ALPHA = 0.06;
|
|
116205
|
-
var GRAIN = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 ${String(GRAIN_ALPHA)} 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E")`;
|
|
116206
|
-
var ICON = (paths) => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E${paths}%3C/svg%3E")`;
|
|
116207
|
-
var ICON_ALERT = ICON("%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E");
|
|
116208
|
-
var ICON_CHECK = ICON("%3Cpath d='M20 6 9 17l-5-5'/%3E");
|
|
116209
|
-
var CHEVRON = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23${PALETTE2["text-2"].slice(1)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`;
|
|
116210
|
-
|
|
116211
116248
|
// ../design/dist/fonts.js
|
|
116212
116249
|
var resolve2 = __gamesteelResolve;
|
|
116213
116250
|
var FONT_FILES = {
|
|
@@ -119649,7 +119686,7 @@ function pushFileProblem(platform, id, text3) {
|
|
|
119649
119686
|
const file = PUSH_FILES[platform];
|
|
119650
119687
|
const app = platform === "android" ? "the Android app" : "the Apple app";
|
|
119651
119688
|
if (text3 === null)
|
|
119652
|
-
return `${file} is missing: download it from the game's Firebase project (Project settings, Your apps, ${app} ${id}) and add it
|
|
119689
|
+
return `${file} is missing: download it from the game's Firebase project (Project settings, Your apps, ${app} ${id}) and add it on the Server page`;
|
|
119653
119690
|
const ids = firebaseIds(platform, text3);
|
|
119654
119691
|
if (ids === null)
|
|
119655
119692
|
return `${file} is not the file Firebase gives: download it again`;
|
|
@@ -121904,7 +121941,7 @@ function settingsWords(state, builder, releases, icon) {
|
|
|
121904
121941
|
`- icon: ${s5.icon === "title" ? "the title icon (the first letter on the HUD plate)" : s5.icon}${icon === null ? "" : `, but ${icon}`}`,
|
|
121905
121942
|
`- splash: ${s5.splash}`,
|
|
121906
121943
|
`- audience: ${s5.children === true ? "includes children under 13, so it shows no ads" : "does not include children under 13"}`,
|
|
121907
|
-
state.keyHidden === true ? "Upload key: read by the owner's or an admin's program for a release build." : state.key === null ? "Upload key: none yet; a release build makes one, and Gamesteel keeps it sealed." : "Upload key: kept by Gamesteel, sealed; the workspace's owner and admins download a copy from
|
|
121944
|
+
state.keyHidden === true ? "Upload key: read by the owner's or an admin's program for a release build." : state.key === null ? "Upload key: none yet; a release build makes one, and Gamesteel keeps it sealed." : "Upload key: kept by Gamesteel, sealed; the workspace's owner and admins download a copy from Upload \u203A App.",
|
|
121908
121945
|
builder,
|
|
121909
121946
|
...release,
|
|
121910
121947
|
`Build targets: ${TARGETS2}.`
|
|
@@ -122209,7 +122246,7 @@ function hostedAppRoutes(options) {
|
|
|
122209
122246
|
store.saveStoreListing(slug, listing);
|
|
122210
122247
|
const problems = listingProblems(listing);
|
|
122211
122248
|
const kept = `${slug}'s listing in ${language} is kept with the game.`;
|
|
122212
|
-
const pictures = problems.some((one2) => PICTURE_PROBLEMS.has(one2)) ? "; the person makes the pictures in the editor's App and
|
|
122249
|
+
const pictures = problems.some((one2) => PICTURE_PROBLEMS.has(one2)) ? "; the person makes the pictures in the editor's Upload \u203A App, and with Store screenshot while the game plays" : "";
|
|
122213
122250
|
const play2 = problems.length === 0 ? " It is finished." : ` It still needs ${listingNeeds(problems)}${pictures}.`;
|
|
122214
122251
|
const appleSet = APPLE_TEXT2.some((key) => set2[key] !== void 0);
|
|
122215
122252
|
const update = appleSet && options.stores !== void 0 ? await options.stores.appleUpdate(slug).catch(() => false) : false;
|
|
@@ -128203,7 +128240,7 @@ Team since your last call: ${news.map((event) => event.words).join("; ")}.`;
|
|
|
128203
128240
|
own3.delete(listener);
|
|
128204
128241
|
};
|
|
128205
128242
|
}
|
|
128206
|
-
/** The editor's Team
|
|
128243
|
+
/** The editor's Team page: the board as the person sees it, and their actions on it. */
|
|
128207
128244
|
routes() {
|
|
128208
128245
|
const personOf = (req2) => {
|
|
128209
128246
|
const id = requestPlaces.get(req2)?.person;
|
|
@@ -128254,7 +128291,7 @@ Team since your last call: ${news.map((event) => event.words).join("; ")}.`;
|
|
|
128254
128291
|
}
|
|
128255
128292
|
];
|
|
128256
128293
|
}
|
|
128257
|
-
/** The board as the Team
|
|
128294
|
+
/** The board as the Team page shows it to `viewer`. */
|
|
128258
128295
|
async view(slug, viewer) {
|
|
128259
128296
|
const board = await this.load(slug);
|
|
128260
128297
|
const doc = this.docOf(slug);
|
|
@@ -135277,7 +135314,7 @@ var DeviceSite = class {
|
|
|
135277
135314
|
const game = answer3.endpoint?.game;
|
|
135278
135315
|
return answer3.open && answer3.server != null && typeof host === "string" && typeof game === "string" ? { host, game, push: answer3.push === true, purchases: answer3.purchases === true, rewards: answer3.rewards === true } : null;
|
|
135279
135316
|
}
|
|
135280
|
-
/** The game's online settings whole, as the
|
|
135317
|
+
/** The game's online settings whole, as the Server page reads them (the online services spec), for its agent's words. */
|
|
135281
135318
|
async onlineState(slug) {
|
|
135282
135319
|
return this.answer(await this.request("GET", `games/${slug}/online`), [200]);
|
|
135283
135320
|
}
|
|
@@ -135346,7 +135383,7 @@ var DeviceSite = class {
|
|
|
135346
135383
|
};
|
|
135347
135384
|
|
|
135348
135385
|
// src/program/online-agent.ts
|
|
135349
|
-
var TAB = "the editor's
|
|
135386
|
+
var TAB = "the editor's Server page";
|
|
135350
135387
|
var MOST_CHARS = 7e3;
|
|
135351
135388
|
var REGIONS = {
|
|
135352
135389
|
"aws-us-east-2": "Ohio",
|
|
@@ -135405,20 +135442,20 @@ var AgentOnline = class {
|
|
|
135405
135442
|
const days = online.wallet?.daysLeft ?? null;
|
|
135406
135443
|
return [
|
|
135407
135444
|
`- server: ${server}: ${String(ceilings.eventsPerSecond)} calls a second, ${String(ceilings.connected)} players at once, ${String(ceilings.cpuMs)} ms of work a call`,
|
|
135408
|
-
database === null ? "- database: off: the person turns it on
|
|
135409
|
-
online.rooms === true ? "- live rooms: on" : "- live rooms: off: a game made for children turns them on once the person confirms,
|
|
135445
|
+
database === null ? "- database: off: the person turns it on, on the Server page" : `- database: ${database}: up to ${String(DATABASE_SIZES[database].maxCu)} compute units and ${String(DATABASE_SIZES[database].dataBytes / 1e9)} GB of data${online.region == null ? "" : `, in ${REGIONS[online.region] ?? online.region}`}` + (online.databaseUse == null ? "" : `; data kept: ${String(Math.round(online.databaseUse.bytes / 1e6))} MB, compute this month: ${String(Math.round(online.databaseUse.cuHours * 10) / 10)} compute-unit hours`),
|
|
135446
|
+
online.rooms === true ? "- live rooms: on" : "- live rooms: off: a game made for children turns them on once the person confirms, on the Server page, they are designed to be safe",
|
|
135410
135447
|
`- test: ${versions?.test == null ? "no code deployed yet: each save of server/main.ts goes to test a moment later" : `version ${String(versions.test)}`}`,
|
|
135411
|
-
`- live: ${versions?.live == null ? "nothing published yet: the person sends test's code to players with Publish to players,
|
|
135448
|
+
`- live: ${versions?.live == null ? "nothing published yet: the person sends test's code to players with Publish to players, on the Server page" : `version ${String(versions.live)}; the person publishes test's code to players with Publish to players`}`,
|
|
135412
135449
|
`- players this week: ${players.length === 0 ? "no live calls" : players.map((one2) => `${one2.version} (${count2(one2.calls, "call")})`).join(", ")}`,
|
|
135413
135450
|
`- today: ${count2(online.use?.events ?? 0, "event")}, ${count2(online.use?.calls ?? 0, "call")}; errors: ${count2(online.errors?.calls ?? 0, "call")}, ${count2(online.errors?.runs ?? 0, "run")}`,
|
|
135414
|
-
...(online.errors?.busy ?? 0) > 0 ? [`- ${count2(online.errors?.busy ?? 0, "call")} answered busy today: the game reaches its size's ceilings; a bigger size, which the person chooses
|
|
135451
|
+
...(online.errors?.busy ?? 0) > 0 ? [`- ${count2(online.errors?.busy ?? 0, "call")} answered busy today: the game reaches its size's ceilings; a bigger size, which the person chooses on the Server page, lets it do more`] : [],
|
|
135415
135452
|
...online.errors?.deploy == null ? [] : [`- not deployed: ${online.errors.deploy}`],
|
|
135416
135453
|
`- this month: ${dollars(online.cost ?? 0)}${wallet === null ? "" : `; ${wallet}${days === null ? "" : `, about ${count2(days, "day")} at this rate`}`}`,
|
|
135417
135454
|
...online.notice == null ? [] : [`- paused since ${online.notice.pausedAt.slice(0, 10)}: the wallet ran out, its calls answer resting${online.notice.deleteOn === null ? "" : `, and its data goes on ${online.notice.deleteOn.slice(0, 10)} unless what is owed is paid`}; the person adds credit on the Plan page`],
|
|
135418
135455
|
// Part 2: players' files and push, each on the database.
|
|
135419
|
-
online.files === true ? `- player files: on: a file is at most ${grouped(FILE_CAPS[server])} bytes on this server size; kept: ${String(Math.round((online.filesUse?.bytes ?? 0) / 1e5) / 10)} MB, ` + (online.filesUse?.measured == null ? "measured each day" : `measured ${online.filesUse.measured.slice(0, 10)}`) : "- player files: off: the person turns them on
|
|
135420
|
-
online.push === true ? "- push: on, through the game's own Firebase project: each push costs an event; never needed to play, never a reward for saying yes" : "- push: off: the person turns it on
|
|
135421
|
-
`- live players' data: ${online.liveForAgent === true ? "the person lets the agent read it" : "read only once the person lets the agent,
|
|
135456
|
+
online.files === true ? `- player files: on: a file is at most ${grouped(FILE_CAPS[server])} bytes on this server size; kept: ${String(Math.round((online.filesUse?.bytes ?? 0) / 1e5) / 10)} MB, ` + (online.filesUse?.measured == null ? "measured each day" : `measured ${online.filesUse.measured.slice(0, 10)}`) : "- player files: off: the person turns them on, on the Server page, with the database",
|
|
135457
|
+
online.push === true ? "- push: on, through the game's own Firebase project: each push costs an event; never needed to play, never a reward for saying yes" : "- push: off: the person turns it on, on the Server page, with the game's own Firebase project and the database",
|
|
135458
|
+
`- live players' data: ${online.liveForAgent === true ? "the person lets the agent read it" : "read only once the person lets the agent, on the Server page"}`
|
|
135422
135459
|
];
|
|
135423
135460
|
}
|
|
135424
135461
|
async logs(slug, stage) {
|
|
@@ -136282,7 +136319,7 @@ var KEEP = 5;
|
|
|
136282
136319
|
var LOG_MOST = 200;
|
|
136283
136320
|
var TARGETS3 = ["android-debug", "android-release", "ios-project", "ios-release"];
|
|
136284
136321
|
var CLOSED = "the program closed while this was being built";
|
|
136285
|
-
var KEY_NOTICE = "Gamesteel keeps this app's upload key on the website, sealed. The workspace's owner and admins download a copy from
|
|
136322
|
+
var KEY_NOTICE = "Gamesteel keeps this app's upload key on the website, sealed. The workspace's owner and admins download a copy from Upload \u203A App: keep it safe, as Google Play takes updates to the app only when they are signed with it.";
|
|
136286
136323
|
var MEMBERS_RELEASE = "release builds run from the workspace owner's or an admin's program, which signs them with the key the website keeps: ask one of them to build android-release";
|
|
136287
136324
|
var timeWords = (ms) => ms < 6e4 ? `${String(ms / 1e3)} seconds` : `${String(Math.round(ms / 6e4))} minutes`;
|
|
136288
136325
|
var ranPast = (ms) => `the build ran longer than ${timeWords(ms)} and was stopped: try again`;
|
|
@@ -136402,7 +136439,7 @@ var DeviceApps = class {
|
|
|
136402
136439
|
const path = join34(this.folder, slug, name2);
|
|
136403
136440
|
return existsSync22(path) ? new Uint8Array(readFileSync30(path)) : null;
|
|
136404
136441
|
}
|
|
136405
|
-
/** The key never leaves the website through the program: its owner and admins download it from
|
|
136442
|
+
/** The key never leaves the website through the program: its owner and admins download it from Upload › App. */
|
|
136406
136443
|
async keyZip() {
|
|
136407
136444
|
return null;
|
|
136408
136445
|
}
|
|
@@ -136542,7 +136579,7 @@ var DeviceApps = class {
|
|
|
136542
136579
|
const made3 = await this.options.site.storeState(slug, store).then((state) => state.products?.fingerprint ?? null, () => null);
|
|
136543
136580
|
if (made3 !== purchasesFingerprint(sold)) {
|
|
136544
136581
|
const count3 = sold.products.length;
|
|
136545
|
-
noSale = `this release sells ${String(count3)} product${count3 === 1 ? "" : "s"} not yet made in ${appStore ? "the App Store" : "Google Play"}: make them in the editor's Upload \u203A ${appStore ? "App Store" : "Play
|
|
136582
|
+
noSale = `this release sells ${String(count3)} product${count3 === 1 ? "" : "s"} not yet made in ${appStore ? "the App Store" : "Google Play"}: make them in the editor's Upload \u203A ${appStore ? "App Store" : "Google Play"}, in its Purchases step`;
|
|
136546
136583
|
}
|
|
136547
136584
|
}
|
|
136548
136585
|
const checkedAds = server?.rewards === true && ads.exported !== void 0 ? { ...ads, exported: { ...ads.exported, rewardsChecked: true } } : ads;
|
|
@@ -138079,7 +138116,7 @@ function appleNextStep(wizard, facts) {
|
|
|
138079
138116
|
const { state } = facts;
|
|
138080
138117
|
if (!facts.connected) return `the person gives their App Store Connect key (an Admin team key) in ${wizard}`;
|
|
138081
138118
|
if (state.app === null) return `the person creates the app in App Store Connect (New App, iOS, the bundle id ${facts.bundleId}); app store_check then finds it`;
|
|
138082
|
-
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its App Store screenshots in the editor
|
|
138119
|
+
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its App Store screenshots with Store screenshot while the game plays in the editor and gives App Review's contact in the wizard";
|
|
138083
138120
|
if (!facts.confirmed) return `the person reads, edits and confirms the privacy policy as theirs in ${wizard}`;
|
|
138084
138121
|
if (!facts.opens) return "add a small Privacy button that calls this.game.openPrivacy() (the playbook's privacy button)";
|
|
138085
138122
|
if (!answersComplete(state.answers)) return `the person answers Apple's age rating questions in ${wizard}`;
|
|
@@ -138186,7 +138223,7 @@ var AppleWork = class {
|
|
|
138186
138223
|
}
|
|
138187
138224
|
async settingsOf(slug) {
|
|
138188
138225
|
const settings = await this.kit.settings(slug);
|
|
138189
|
-
if (settings === null) throw new Refused3(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138226
|
+
if (settings === null) throw new Refused3(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138190
138227
|
return settings;
|
|
138191
138228
|
}
|
|
138192
138229
|
/** The person's app on App Store Connect, recorded once found; null before they made it. */
|
|
@@ -138432,7 +138469,7 @@ var ADS_TXT_KEPT_MS = 3e4;
|
|
|
138432
138469
|
function nextStep(wizard, facts, now) {
|
|
138433
138470
|
const { state } = facts;
|
|
138434
138471
|
if (!facts.connected) return `the person signs in with Google, or the workspace's owner gives a key file, in ${wizard}`;
|
|
138435
|
-
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its pictures in the editor's App and
|
|
138472
|
+
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its pictures in the editor's Upload \u203A App, and with Store screenshot while the game plays";
|
|
138436
138473
|
if (!facts.confirmed) return `the person reads, edits and confirms the privacy policy as theirs in ${wizard}`;
|
|
138437
138474
|
if (!facts.opens) return "add a small Privacy button that calls this.game.openPrivacy() (the playbook's privacy button)";
|
|
138438
138475
|
if (state.upload === null) return "upload it to testing with app store_upload";
|
|
@@ -138493,7 +138530,7 @@ var DeviceStores = class _DeviceStores {
|
|
|
138493
138530
|
return this.options.clock?.() ?? /* @__PURE__ */ new Date();
|
|
138494
138531
|
}
|
|
138495
138532
|
wizard(slug) {
|
|
138496
|
-
return `the editor's Upload \u203A Play
|
|
138533
|
+
return `the editor's Upload \u203A Google Play at ${this.siteUrl}/editor?game=${slug}`;
|
|
138497
138534
|
}
|
|
138498
138535
|
/** What the store ask does, in words; `from` the agent or the person's click in the editor. */
|
|
138499
138536
|
async act(slug, ask, from, report) {
|
|
@@ -138657,7 +138694,7 @@ ${await this.apple.state(slug)}`;
|
|
|
138657
138694
|
/** What every upload and submission needs first: the listing, the privacy policy, and the game opening it. */
|
|
138658
138695
|
async ready(slug) {
|
|
138659
138696
|
const settings = await this.options.apps.settingsOf(this.options.workspaceId(), slug, this.game(slug));
|
|
138660
|
-
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138697
|
+
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138661
138698
|
this.appIds.set(slug, settings.id);
|
|
138662
138699
|
const listing = this.listingOf(slug);
|
|
138663
138700
|
const problems = listingProblems(listing);
|
|
@@ -138760,7 +138797,7 @@ Warning: ${checked3.words}`;
|
|
|
138760
138797
|
async products(slug) {
|
|
138761
138798
|
const { site, apps } = this.options;
|
|
138762
138799
|
const settings = await apps.settingsOf(this.options.workspaceId(), slug, this.game(slug));
|
|
138763
|
-
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138800
|
+
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138764
138801
|
this.appIds.set(slug, settings.id);
|
|
138765
138802
|
const kept = await sellable(slug, this.options.sessions().store.loadPurchases(slug), settings, site);
|
|
138766
138803
|
if (typeof kept === "string") throw new Refused4(kept);
|
|
@@ -138858,7 +138895,7 @@ Warning: ${checked3.words}`;
|
|
|
138858
138895
|
async check(slug) {
|
|
138859
138896
|
const { site } = this.options;
|
|
138860
138897
|
const settings = await this.options.apps.settingsOf(this.options.workspaceId(), slug, this.game(slug));
|
|
138861
|
-
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138898
|
+
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138862
138899
|
this.appIds.set(slug, settings.id);
|
|
138863
138900
|
const { client } = await this.access(slug);
|
|
138864
138901
|
if (!await client.exists(settings.id)) {
|
|
@@ -139471,7 +139508,7 @@ async function startProgram(options) {
|
|
|
139471
139508
|
...known2.team === null ? {} : { team: { board: remoteBoard(site), names: remoteNames(site) } },
|
|
139472
139509
|
orderer: (slug) => siteOrderer({ site, slug, grantId }),
|
|
139473
139510
|
models: machineModels(options.models),
|
|
139474
|
-
// The plan's rule for an app build is the website's, read here; the key is downloaded only from
|
|
139511
|
+
// The plan's rule for an app build is the website's, read here; the key is downloaded only from Upload › App.
|
|
139475
139512
|
apps,
|
|
139476
139513
|
appOptions: {
|
|
139477
139514
|
mayExport: async () => (await plan()).limits.export,
|