{"version":3,"file":"dfc7a76691fc581685ee.chunk.js","mappings":"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","sources":["webpack://ch.enlightware.gamecreator/./src/apps/common/analytics.ts","webpack://ch.enlightware.gamecreator/./src/apps/common/login-manager.ts","webpack://ch.enlightware.gamecreator/./src/apps/full/backlog-executor.ts","webpack://ch.enlightware.gamecreator/./src/blocks/actions/speed/brake-block.ts","webpack://ch.enlightware.gamecreator/./src/game/game-blocks.ts","webpack://ch.enlightware.gamecreator/./src/game/game.ts","webpack://ch.enlightware.gamecreator/./src/level/level-editor-interface.ts","webpack://ch.enlightware.gamecreator/./src/runtime/variable.ts","webpack://ch.enlightware.gamecreator/./src/storage/backlog.ts","webpack://ch.enlightware.gamecreator/./src/storage/storage.ts","webpack://ch.enlightware.gamecreator/./src/utils/assert.ts","webpack://ch.enlightware.gamecreator/./src/utils/serialization.ts"],"sourcesContent":["/**\n * @module utils\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport Bowser from 'bowser';\n\nimport { BuildFlags, CodeVersion, RustVersion } from './version';\nimport { Features, Parameters } from './parameters';\nimport { getJsRuntimeId } from './js-runtime-id';\nimport { setAssertionListener } from 'utils/assert';\nimport * as Api from 'network/api';\nimport { JSONValue } from 'utils/json-value';\nimport { toStr } from 'utils/types';\n\nfunction calcBrowserVersionString() {\n\tconst { name, version } = Bowser.getParser(window.navigator.userAgent).getBrowser();\n\treturn `${name}-${version}`;\n}\n\nfunction calcSystemString() {\n\tconst bowserParser = Bowser.getParser(window.navigator.userAgent);\n\tconst osName = bowserParser.getOSName();\n\tconst version = bowserParser.getOSVersion();\n\tconst platform = bowserParser.getPlatformType();\n\treturn `${platform}-${osName}-${version}`;\n}\n\nfunction isLoggingDisabled() {\n\t// we don't want logs from acceptance tests ATM, and we honour disabling logging\n\tconst InNode = (typeof process !== 'undefined') && (process.release.name === 'node');\n\treturn (!InNode && !Features.logOnLocalhost && window.location.hostname === 'localhost') ||\n\t\tParameters.isUnderTest ||\n\t\t!Features.analyticsEnabled\n\t;\n}\n\nexport function getStackTrace() {\n\treturn new Error().stack;\n}\n\nexport class Analytics {\n\tstatic readonly browser = calcBrowserVersionString();\n\tstatic readonly systemString = calcSystemString();\n\tstatic appName = 'UNKNOWN!';\n\tstatic serverVersion = 'UNKNOWN!';\n\tstatic loggingDisabled = isLoggingDisabled();\n\n\tstatic get appInfo() {\n\t\treturn {\n\t\t\tname: Analytics.appName,\n\t\t\tcode: CodeVersion,\n\t\t\tsystem: Analytics.systemString,\n\t\t\tbrowser: Analytics.browser,\n\t\t\trustc: RustVersion,\n\t\t\tbuildFlags: BuildFlags\n\t\t}\n\t}\n}\n\nexport function reportLog(id: string, data: any = null) {\n\tconsole.debug(`Reporting log: ${id}`, data);\n\treport('log', id, data);\n}\n\nexport function reportWarning(id: string, data: any = null) {\n\tconsole.warn(`Reporting warning: ${id}`, data);\n\treport('warn', id, data);\n}\n\nlet lastScreenReport: string | null = null;\nlet screenReportTimeout: number | null = null;\n\nexport function reportScreenSize() {\n\tif (screenReportTimeout !== null) {\n\t\twindow.clearInterval(screenReportTimeout);\n\t}\n\tscreenReportTimeout = window.setTimeout(doReportScreenSize, 1000);\n}\n\nfunction doReportScreenSize() {\n\tscreenReportTimeout = null;\n\tconst screenReport: JSONValue = {\n\t\tdpr: window.devicePixelRatio,\n\t\tiw: window.innerWidth,\n\t\tih: window.innerHeight,\n\t\tow: window.outerWidth,\n\t\toh: window.outerHeight\n\t}\n\tconst first = lastScreenReport === null;\n\tif (first) {\n\t\twindow.addEventListener('resize', () => reportScreenSize());\n\t}\n\tconst stringScreenReport = JSON.stringify(screenReport);\n\tif (stringScreenReport !== lastScreenReport) {\n\t\tlastScreenReport = stringScreenReport;\n\t\tscreenReport.first = first;\n\t\treportLog('resize', screenReport);\n\t}\n}\n\ninterface ErrorData {\n\tmsg: string,\n\terror?: string,\n\tcodeVersion: string,\n\tsourceUrl?: string,\n\tlineNo?: number,\n\tcolumnNo?: number,\n\tstack?: string\n}\nexport function reportError(id: string, msg: string, error?: any) {\n\tconsole.warn(`Reporting error: ${id}: ${msg}`, error);\n\treportErrorInternal(id, {\n\t\tmsg,\n\t\tcodeVersion: CodeVersion,\n\t\terror: error?.toString(),\n\t\tstack: (error? error.stack : new Error().stack)\n\t});\n}\n\nfunction reportErrorInternal(id: string, data: ErrorData) {\n\tif (ignoreErrors) {\n\t\treturn;\n\t}\n\treport('err', id, data);\n}\n\nfunction getHeapSize() {\n\tconst p = performance as any as { memory?: { usedJSHeapSize?: number } };\n\treturn p?.memory?.usedJSHeapSize || null;\n}\n\n\nlet reportCounter = 0;\nlet errorReportCounter = 0;\nconst MaxErrorReportCount = 100;\nconst MaxReportCount = 500;\n\nfunction report(kind: 'log' | 'err' | 'warn', id: string, data: any) {\n\tif (Analytics.loggingDisabled) {\n\t\tconsole.info('Logging disabled, not sending:', kind, id, data);\n\t\treturn;\n\t}\n\tconst record = [\n\t\tid,\n\t\tDate.now(),\n\t\tgetJsRuntimeId(),\n\t\treportCounter,\n\t\tgetHeapSize(),\n\t\tdata,\n\t];\n\n\tif (kind === 'err') {\n\t\tif (errorReportCounter >= MaxErrorReportCount) {\n\t\t\tconsole.error(`Error count limit (${MaxErrorReportCount}) reached, not reporting:`, record);\n\t\t\treturn;\n\t\t}\n\t\terrorReportCounter++;\n\t} else {\n\t\tif (reportCounter >= MaxReportCount) {\n\t\t\tconsole.error(`Report count limit (${MaxReportCount}) reached, not reporting:`, record);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (Features.devMode) {\n\t\t(() => {\n\t\t\tswitch (kind) {\n\t\t\t\tcase 'err': return console.error;\n\t\t\t\tcase 'warn': return console.warn;\n\t\t\t\tcase 'log': return console.info;\n\t\t\t}\n\t\t})()(kind.toUpperCase() + ' report:', record);\n\t} else {\n\t\t// TODO: when there is server support for warnings, change this (see issue #1889)\n\t\tconst serverKind = kind === 'warn' ? 'log' : kind;\n\t\tconst blob = new Blob([JSON.stringify(record)], {type : 'application/json'});\n\t\tnavigator.sendBeacon(Api.getHttpBaseUrl() + '/analytics/v0/' + serverKind,\n\t\t\tblob\n\t\t);\n\t}\n\treportCounter++;\n}\n\nlet ignoreErrors = false;\n\nexport function disableErrorReporting() {\n\tignoreErrors = true;\n}\n\nfunction onError(msgAny: string | Event, sourceUrl?: string, lineNo?: number, columnNo?: number, error?: Error) {\n\treportErrorInternal('onError', {\n\t\tcodeVersion: CodeVersion,\n\t\tmsg: toStr(msgAny),\n\t\terror: error?.toString(),\n\t\tsourceUrl,\n\t\tlineNo,\n\t\tcolumnNo,\n\t\tstack: error?.stack\n\t});\n}\n\nfunction onUnhandledReject({reason}: { reason: any }) {\n\treportErrorInternal('rejectedAsync', {\n\t\tcodeVersion: CodeVersion,\n\t\tmsg: reason?.message ?? toStr(reason),\n\t\terror: toStr(reason),\n\t\tsourceUrl: reason.fileName,\n\t\tlineNo: reason.lineNumber,\n\t\tcolumnNo: reason.columnNumber,\n\t\tstack: reason.stack\n\t});\n}\n\nfunction assertionListener(msg: string, recoverable: boolean) {\n\tif (recoverable) {\n\t\treportError('recoverableAssertionFailed', msg);\n\t}\n}\n\nif (!Features.devMode) {\n\twindow.onerror = onError;\n\twindow.addEventListener('unhandledrejection', onUnhandledReject);\n\tsetAssertionListener(assertionListener);\n}\n","/**\n * @module utils\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nconst RememberMeKey = 'gc-rememberMe';\n\nexport interface Login {\n\tid: string;\n\tsecret: string;\n}\nexport class LoginManager {\n\tprivate static readonly LoginKey = 'gc-login-secret';\n\n\tprivate static instance: LoginManager | null = null;\n\n\tprotected constructor() { }\n\n\tstatic getInstance() {\n\t\tif (this.instance === null) {\n\t\t\tthis.instance = new LoginManager();\n\t\t}\n\t\treturn this.instance;\n\t}\n\n\tgetLogin() {\n\t\tconst loginString = localStorage.getItem(LoginManager.LoginKey);\n\t\treturn loginString ? JSON.parse(loginString) as Login : null;\n\t}\n\tsetLogin(login: Login) {\n\t\tlocalStorage.setItem(LoginManager.LoginKey, JSON.stringify(login));\n\t}\n\tremoveLogin() {\n\t\tLoginManager.forgetAll();\n\t}\n\tstatic forgetAll() {\n\t\tlocalStorage.removeItem(LoginManager.LoginKey);\n\t}\n}\n\nexport class OTPLoginManager extends LoginManager {\n\tconstructor(private readonly id: string, private readonly otp: string) {\n\t\tsuper();\n\t}\n\tgetLogin() {\n\t\treturn { id: this.id, secret: this.otp };\n\t}\n}\n\n// Remember me settings\n\nexport function setRememberMeInLocalStorage(enabled: boolean) {\n\tif (!enabled) {\n\t\tLoginManager.forgetAll();\n\t}\n\treturn localStorage.setItem(RememberMeKey, String(enabled));\n}\n\nexport function rememberMe() {\n\treturn localStorage.getItem(RememberMeKey) === 'true';\n}\n","/**\n * @module full-app\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport { reportError, reportWarning } from 'apps/common/analytics';\nimport { BacklogInterface, assetsBacklogsWithContentTypes, createdGamesBacklog, gameNamesBacklog, gamesBacklog, miniaturesBacklog } from 'storage/backlog';\nimport { KeyValueMap, Workspace } from 'storage/db';\nimport { ApiError, BlobWithContentType } from 'network/api-types';\nimport { dynamicCast } from 'utils/types';\nimport { encodePng } from 'utils/image';\n\nconst backlogBlacklist: Set = new Set();\n\nlet isClosing = false;\nwindow.addEventListener('beforeunload', () => isClosing = true);\n\nexport function setupBacklogExecutor(\n\tassetDb: KeyValueMap,\n\tgameDb: KeyValueMap,\n\tgameMiniaturesDb: KeyValueMap,\n\tgameNamesDb: KeyValueMap,\n\townWorkspace: Workspace\n) {\n\tlet first = true;\n\tlet executing = false;\n\tconst forbiddenKeys: Set = new Set();\n\tconst executor = async () => {\n\t\tif (!navigator.onLine || isClosing) {\n\t\t\treturn;\n\t\t}\n\t\texecuting = true;\n\t\ttry {\n\t\t\tif (first) {\n\t\t\t\tconsole.info('Running backlog executor first time:');\n\t\t\t}\n\t\t\tconst filterKey = (key: string) => !backlogBlacklist.has(key) && !forbiddenKeys.has(key);\n\t\t\tlet anyFailed = false;\n\t\t\tconst handleError = (e: any, key: string, ctx: string) => {\n\t\t\t\tif (e instanceof ApiError && e.statusCode === 403) {\n\t\t\t\t\tforbiddenKeys.add(key);\n\t\t\t\t\tconsole.debug(`Forbidden to store ${key} in ${ctx}, blacklisting.`);\n\t\t\t\t} else {\n\t\t\t\t\tanyFailed = true;\n\t\t\t\t\t// TODO: in the future, whitelist typical errors\n\t\t\t\t\treportError('backlogStoreFailed', `Failed to store ${key} in ${ctx}.`, e);\n\t\t\t\t}\n\t\t\t};\n\t\t\t// save assets\n\t\t\tfor (const [backlog, contentType] of assetsBacklogsWithContentTypes) {\n\t\t\t\tfor (const [key, value] of await backlog.list(filterKey)) {\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\tconsole.info(`- ${backlog.kind} ${key}`);\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait assetDb.set(key, { value, contentType });\n\t\t\t\t\t\tawait backlog.remove(key);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\thandleError(e, key, assetDb.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// if any asset failed, we do not even attempt to save the rest\n\t\t\tif (anyFailed) {\n\t\t\t\tconsole.warn('Backlog executor will not attempt to save games because saving some asset failed');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// save games\n\t\t\tfor (const [key, game] of await gamesBacklog.list(filterKey)) {\n\t\t\t\tif (first) {\n\t\t\t\t\tconsole.info(`- Game ${key}`);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait gameDb.set(key, game);\n\t\t\t\t\tawait gamesBacklog.remove(key);\n\t\t\t\t} catch (e) {\n\t\t\t\t\thandleError(e, key, gameDb.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t\t// save miniatures\n\t\t\tfor (const [key, imageData] of await miniaturesBacklog.list(filterKey)) {\n\t\t\t\tif (first) {\n\t\t\t\t\tconsole.info(`- Miniature ${key}`);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait gameMiniaturesDb.set(key, encodePng(imageData));\n\t\t\t\t\tawait miniaturesBacklog.remove(key);\n\t\t\t\t} catch (e) {\n\t\t\t\t\thandleError(e, key, gameMiniaturesDb.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t\t// save game names\n\t\t\tfor (const [key, gameName] of await gameNamesBacklog.list(filterKey)) {\n\t\t\t\tif (first) {\n\t\t\t\t\tconsole.info(`- Name '${gameName}' of game ${key}`);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait gameNamesDb.set(key, gameName);\n\t\t\t\t\tawait gameNamesBacklog.remove(key);\n\t\t\t\t} catch (e) {\n\t\t\t\t\thandleError(e, key, gameNamesDb.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t\t// if any games or miniature failed, we do not even attempt to push games to workspaces\n\t\t\tif (anyFailed) {\n\t\t\t\tconsole.warn('Backlog executor will not attempt to push games to workspaces because saving some games or miniature failed');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// add to workspace\n\t\t\tfor (const [gameKey, workspace] of await createdGamesBacklog.list(filterKey)) {\n\t\t\t\tif (workspace !== ownWorkspace.key || forbiddenKeys.has(workspace)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (first) {\n\t\t\t\t\tconsole.info(`- Game ${gameKey} to workspace ${workspace}`);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait ownWorkspace.addFront(gameKey);\n\t\t\t\t\tawait createdGamesBacklog.remove(gameKey);\n\t\t\t\t} catch (e) {\n\t\t\t\t\thandleError(e, gameKey, `workspace ${workspace}`);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tfirst = false;\n\t\t\texecuting = false;\n\t\t}\n\t};\n\tvoid executor();\n\twindow.setInterval(() => {\n\t\tif (!executing) {\n\t\t\tvoid executor();\n\t\t}\n\t}, 10000);\n}\n\nexport async function storeWithBacklog(\n\tkey: string,\n\tdata: T,\n\tdbSetter: (key: string, data: T) => Promise,\n\tbacklog: BacklogInterface,\n\tforceBacklog = false\n): Promise {\n\t// check online status\n\tif (forceBacklog || !navigator.onLine) {\n\t\tif (forceBacklog) {\n\t\t\tconsole.debug('Force storing to backlog');\n\t\t} else {\n\t\t\tconsole.debug('Offline, storing to backlog instead');\n\t\t}\n\t\tawait backlog.insert(key, data);\n\t\treturn;\n\t}\n\t// if online, try to save to the server and handle errors\n\t// insert what we are about to save from backlog blacklist\n\tbacklogBlacklist.add(key);\n\ttry {\n\t\t// remove what we are about to save from backlog\n\t\tawait backlog.remove(key);\n\t\t// attempt to save what we want to save\n\t\tawait dbSetter(key, data);\n\t\tconsole.debug(`Successfully saved data ${key}!`);\n\t} catch (e) {\n\t\tconsole.warn(`Data ${key} cannot be saved, storing to backlog instead`);\n\t\tawait backlog.insert(key, data);\n\t\tif (e instanceof ApiError && e.statusCode === 403) {\n\t\t\treturn;\n\t\t\t// TODO: in the future, handle locked (423)\n\t\t}\n\t\t// notify the user their are offline or logged out (403) or locked (423)\n\t\t// somewhere else: detect online attempt to write back journal, detect need to re-auth\n\t\t// TODO: in the future, whitelist typical errors\n\t\tconst error = dynamicCast(Error, e) ?? undefined;\n\t\treportWarning('storeFailed', `Failed to store ${key}: ${error?.toString() ?? e}`);\n\t} finally {\n\t\t// delete what we were saving from backlog blacklist\n\t\tbacklogBlacklist.delete(key);\n\t}\n}\n","/**\n * @module block-speed\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport { CandliBlock } from 'game/game-block-definition';\nimport * as flatbuffers from 'flatbuffers';\nimport * as CandliFB from 'flatbuffers/all';\nimport { assertNumber } from 'utils/assert';\nimport { Fixed88ToFloat, FloatToFixed88 } from 'flatbuffers/flatbuffers-cast';\nimport { BlockKind } from 'vpl/block';\nimport { qsT, qsTr } from 'translator/translator';\n\nexport const BrakeMaxIntensity = 10;\n\nexport function intensityToUserUnit(intensity: number) {\n\treturn Math.sqrt(intensity / BrakeMaxIntensity);\n}\n\nexport function userUnitToIntensity(userUnit: number) {\n\treturn BrakeMaxIntensity * userUnit * userUnit;\n}\n\nexport class BrakeBlock extends CandliBlock {\n\tintensity: number;\n\n\tconstructor(\n\t\tintensityOrFbBrake: number | CandliFB.Brake = BrakeMaxIntensity * 0.25,\n\t\tlockState?: CandliBlock\n\t) {\n\t\tsuper('Brake', lockState);\n\t\tif (intensityOrFbBrake instanceof CandliFB.Brake) {\n\t\t\tthis.intensity = intensityOrFbBrake.intensity() * Fixed88ToFloat;\n\t\t} else {\n\t\t\tthis.intensity = intensityOrFbBrake;\n\t\t}\n\t\tthis.validate();\n\t}\n\n\tclone(): BrakeBlock {\n\t\treturn new BrakeBlock(this.intensity, this);\n\t}\n\n\tget name() {\n\t\treturn qsT('Brake');\n\t}\n\n\tsummaryText() {\n\t\treturn qsT('brake');\n\t}\n\n\tdetailedTextIfConfigured() {\n\t\tconst hl = Math.log(2) / this.intensity;\n\t\treturn qsTr('brake to half speed in %1 s').args(hl.toFixed(1));\n\t}\n\n\tserialise(builder: flatbuffers.Builder) {\n\t\treturn CandliFB.Brake.createBrake(builder, this.intensity * FloatToFixed88);\n\t}\n\n\tget fbActionType() {\n\t\treturn CandliFB.AnyAction.Brake;\n\t}\n\n\tget kind() {\n\t\treturn BlockKind.ContinuousAction;\n\t}\n\n\tvalidate() {\n\t\tsuper.validate();\n\t\tassertNumber(this.intensity, 'intensity');\n\t}\n\n\tgetMiniatureKey() {\n\t\treturn `brake ${this.intensity}`;\n\t}\n\n\tactionEffects() {\n\t\treturn {\n\t\t\tmodifies: ['LINEAR_VELOCITY', 'ANGULAR_VELOCITY'] as const\n\t\t}\n\t}\n}\n","/**\n * @module game\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport * as CandliFB from 'flatbuffers/all';\nimport { nonnull } from 'utils/types';\nimport { assertFalse } from 'utils/assert';\nimport { CoreGame, ObjectType } from './game-core';\nimport { InitBlock } from 'blocks/conditions/init/init-block';\nimport { InputBlock } from 'blocks/conditions/input/input-block';\nimport { TouchBlock } from 'blocks/conditions/touch/touch-block';\nimport { CollisionBlock } from 'blocks/conditions/collision/collision-block';\nimport { StopBlock } from 'blocks/actions/speed/stop-block';\nimport { SpeedBlock } from 'blocks/actions/speed/speed-block';\nimport { BrakeBlock } from 'blocks/actions/speed/brake-block';\nimport { FlipBlock } from 'blocks/actions/flip/flip-block';\nimport { DeleteBlock } from 'blocks/actions/delete/delete-block';\nimport { ChangeVariableBlock } from 'blocks/actions/variable/change-variable-block';\nimport { SpawnBlock } from 'blocks/actions/spawn/spawn-block';\nimport { VibrateBlock } from 'blocks/actions/vibrate/vibrate-block';\nimport { TeleportBlock } from 'blocks/actions/teleport/teleport-block';\nimport { ShowDialogBlock } from 'blocks/actions/dialog/dialog-block';\nimport { MusicBlock } from 'blocks/actions/music/music-block';\nimport { AngularSpeedBlock } from 'blocks/actions/speed/angular-speed-block';\nimport { TimerBlock } from 'blocks/conditions/timer/timer-block';\nimport { RestartTimerBlock } from 'blocks/actions/timer/restart-timer-block';\nimport { Action, Condition } from 'vpl/row';\nimport { CandliBlock } from './game-block-definition';\nimport { ColorTheme } from 'vpl/color-theme';\nimport { AnyCanvasRenderingContext2D } from 'imgui/imgui-types';\nimport { BlockKind } from 'vpl/block';\nimport { ComparisonBlock } from 'blocks/conditions/comparison/comparison-block';\nimport { ReadVariableBlock } from 'blocks/generic/read-variable/read-variable-block';\nimport { SoundBlock } from 'blocks/actions/sound/sound-block';\nimport { PlayAnimationBlock } from 'blocks/actions/animation/animation-block';\nimport { AnimationIsBlock } from 'blocks/conditions/animation/animation-is-block';\nimport { BinaryArithmeticBlock } from 'blocks/generic/binary-arithmetic/binary-arithmetic-block';\nimport { RandomValueBlock } from 'blocks/generic/random-value/random-value-block';\nimport { ScriptGenericBlock } from 'blocks/generic/script-generic/script-generic-block';\nimport { ScriptActionBlock } from 'blocks/actions/script/script-action-block';\nimport { ScriptConditionBlock } from 'blocks/conditions/script/script-condition-block';\n\nfunction decodeFbGenericBlock(fbGenericBlock: CandliFB.GenericBlockContainer, game: CoreGame) {\n\tconst blockType = fbGenericBlock.genericBlockType();\n\tconst block = (() => {\n\t\tswitch (blockType) {\n\t\t\tcase CandliFB.AnyGenericBlock.ReadVariable:\n\t\t\t\treturn new ReadVariableBlock(nonnull(fbGenericBlock.genericBlock(new CandliFB.ReadVariable())) as CandliFB.ReadVariable);\n\t\t\tcase CandliFB.AnyGenericBlock.BinaryArithmetic:\n\t\t\t\treturn new BinaryArithmeticBlock(nonnull(fbGenericBlock.genericBlock(new CandliFB.BinaryArithmetic())) as CandliFB.BinaryArithmetic);\n\t\t\tcase CandliFB.AnyGenericBlock.RandomValue:\n\t\t\t\treturn new RandomValueBlock(nonnull(fbGenericBlock.genericBlock(new CandliFB.RandomValue())) as CandliFB.RandomValue);\n\t\t\tcase CandliFB.AnyGenericBlock.ScriptGeneric:\n\t\t\t\treturn new ScriptGenericBlock(nonnull(fbGenericBlock.genericBlock(new CandliFB.ScriptGeneric())) as CandliFB.ScriptGeneric, game.scriptCtx);\n\t\t\tdefault:\n\t\t\t\tassertFalse(`Unknown generic block ${blockType}`);\n\t\t}\n\t})();\n\treturn block;\n}\n\n/** Resolve a Flatbuffers condition into the real block */\nexport function decodeFbCondition(fbCondition: CandliFB.Condition, game: CoreGame, fromVersion: number) {\n\tconst conditionType = fbCondition.conditionType();\n\tconst inverted = fbCondition.inverted();\n\tconst AnyCondition = CandliFB.AnyCondition;\n\tconst condition = (() => {\n\t\tswitch (conditionType) {\n\t\t\tcase AnyCondition.Init:\n\t\t\t\treturn new Condition(new InitBlock(), inverted);\n\t\t\tcase AnyCondition.InputButton:\n\t\t\t\treturn new Condition(new InputBlock(nonnull(fbCondition.condition(new CandliFB.InputButton())) as CandliFB.InputButton), inverted);\n\t\t\tcase AnyCondition.Touch:\n\t\t\t\treturn new Condition(new TouchBlock(), inverted);\n\t\t\tcase AnyCondition.Contact:\n\t\t\t\treturn new Condition(new CollisionBlock(nonnull(fbCondition.condition(new CandliFB.Contact())) as CandliFB.Contact, game.objects), inverted);\n\t\t\tcase AnyCondition.Timer:\n\t\t\t\treturn new Condition(new TimerBlock(nonnull(fbCondition.condition(new CandliFB.Timer())) as CandliFB.Timer), inverted);\n\t\t\tcase AnyCondition.Comparison:\n\t\t\t\treturn new Condition(new ComparisonBlock(nonnull(fbCondition.condition(new CandliFB.Comparison())) as CandliFB.Comparison, fromVersion), inverted);\n\t\t\tcase AnyCondition.GenericBlock_ReadVariable:\n\t\t\t\treturn new Condition(new ReadVariableBlock(nonnull(fbCondition.condition(new CandliFB.ReadVariable())) as CandliFB.ReadVariable))\n\t\t\tcase AnyCondition.AnimationIs:\n\t\t\t\treturn new Condition(new AnimationIsBlock(nonnull(fbCondition.condition(new CandliFB.AnimationIs())) as CandliFB.AnimationIs), inverted);\n\t\t\tcase AnyCondition.GenericBlock_GenericBlockContainer:\n\t\t\t\treturn new Condition(decodeFbGenericBlock(nonnull(fbCondition.condition(new CandliFB.GenericBlockContainer())) as CandliFB.GenericBlockContainer, game));\n\t\t\tcase AnyCondition.ScriptCondition:\n\t\t\t\treturn new Condition(new ScriptConditionBlock(nonnull(fbCondition.condition(new CandliFB.ScriptCondition())) as CandliFB.ScriptCondition, game.scriptCtx));\n\t\t\tdefault:\n\t\t\t\tassertFalse(`Unknown condition ${conditionType}`);\n\t\t}\n\t})();\n\tcondition.block.movementLocked = fbCondition.movementLocked();\n\tcondition.block.parametersLocked = fbCondition.parametersLocked();\n\treturn condition;\n}\n\n/** Resolve a Flatbuffers action into the real block */\nexport function decodeFbAction(fbAction: CandliFB.Action, game: CoreGame, fromVersion: number) {\n\tconst actionType = fbAction.actionType();\n\tconst AnyAction = CandliFB.AnyAction;\n\tconst action = (() => {\n\t\tswitch (actionType) {\n\t\t\tcase AnyAction.AddVelocity:\n\t\t\t\treturn new Action(new SpeedBlock(nonnull(fbAction.action(new CandliFB.AddVelocity())) as CandliFB.AddVelocity, fromVersion));\n\t\t\tcase AnyAction.AddAcceleration:\n\t\t\t\treturn new Action(new SpeedBlock(nonnull(fbAction.action(new CandliFB.AddAcceleration())) as CandliFB.AddAcceleration, fromVersion));\n\t\t\tcase AnyAction.AddAngularVelocity:\n\t\t\t\treturn new Action(new AngularSpeedBlock(nonnull(fbAction.action(new CandliFB.AddAngularVelocity())) as CandliFB.AddAngularVelocity));\n\t\t\tcase AnyAction.AddAngularAcceleration:\n\t\t\t\treturn new Action(new AngularSpeedBlock(nonnull(fbAction.action(new CandliFB.AddAngularAcceleration())) as CandliFB.AddAngularAcceleration));\n\t\t\tcase AnyAction.Stop:\n\t\t\t\treturn new Action(new StopBlock());\n\t\t\tcase AnyAction.Brake:\n\t\t\t\treturn new Action(new BrakeBlock(nonnull(fbAction.action(new CandliFB.Brake())) as CandliFB.Brake));\n\t\t\tcase AnyAction.Flip:\n\t\t\t\treturn new Action(new FlipBlock(nonnull(fbAction.action(new CandliFB.Flip())) as CandliFB.Flip));\n\t\t\tcase AnyAction.Delete:\n\t\t\t\treturn new Action(new DeleteBlock());\n\t\t\tcase AnyAction.ChangeVariable:\n\t\t\t\treturn new Action(new ChangeVariableBlock(nonnull(fbAction.action(new CandliFB.ChangeVariable())) as CandliFB.ChangeVariable, fromVersion));\n\t\t\tcase AnyAction.Spawn:\n\t\t\t\treturn new Action(new SpawnBlock(nonnull(fbAction.action(new CandliFB.Spawn())) as CandliFB.Spawn, game.objects, fromVersion));\n\t\t\tcase AnyAction.Teleport:\n\t\t\t\treturn new Action(new TeleportBlock(nonnull(fbAction.action(new CandliFB.Teleport())) as CandliFB.Teleport, game.objects));\n\t\t\tcase AnyAction.ShowDialog:\n\t\t\t\treturn new Action(new ShowDialogBlock(nonnull(fbAction.action(new CandliFB.ShowDialog())) as CandliFB.ShowDialog));\n\t\t\tcase AnyAction.Vibrate:\n\t\t\t\treturn new Action(new VibrateBlock());\n\t\t\tcase AnyAction.SetMusic:\n\t\t\t\treturn new Action(new MusicBlock(nonnull(fbAction.action(new CandliFB.SetMusic())) as CandliFB.SetMusic));\n\t\t\tcase AnyAction.RestartTimer:\n\t\t\t\treturn new Action(new RestartTimerBlock());\n\t\t\tcase AnyAction.PlaySound:\n\t\t\t\treturn new Action(new SoundBlock(nonnull(fbAction.action(new CandliFB.PlaySound())) as CandliFB.PlaySound));\n\t\t\tcase AnyAction.PlayAnimation:\n\t\t\t\treturn new Action(new PlayAnimationBlock(nonnull(fbAction.action(new CandliFB.PlayAnimation())) as CandliFB.PlayAnimation));\n\t\t\tcase AnyAction.GenericBlock_GenericBlockContainer:\n\t\t\t\treturn new Action(decodeFbGenericBlock(nonnull(fbAction.action(new CandliFB.GenericBlockContainer())) as CandliFB.GenericBlockContainer, game));\n\t\t\tcase AnyAction.ScriptAction:\n\t\t\t\treturn new Action(new ScriptActionBlock(nonnull(fbAction.action(new CandliFB.ScriptAction())) as CandliFB.ScriptAction, game.scriptCtx));\n\t\t\tdefault:\n\t\t\t\tassertFalse(`Unknown action ${actionType}`);\n\t\t}\n\t})();\n\taction.block.movementLocked = fbAction.movementLocked();\n\taction.block.parametersLocked = fbAction.parametersLocked();\n\treturn action;\n}\n\nexport function renderActionBlockPreview(ctx: AnyCanvasRenderingContext2D, block: CandliBlock) {\n\tctx.shadowColor = 'rgba(0,0,0,0.48)';\n\tctx.shadowBlur = 2;\n\tctx.shadowOffsetY = 1;\n\tif (block.kind === BlockKind.InstantaneousAction) {\n\t\tctx.fillStyle = ColorTheme.action;\n\t\tctx.beginPath();\n\t\tctx.arc(4.5, 57.5, 2, 0, 2 * Math.PI);\n\t\tctx.fill();\n\t} else {\n\t\tconst deg2rad = Math.PI / 180;\n\t\tctx.strokeStyle = ColorTheme.action;\n\t\tctx.lineWidth = 3;\n\t\tctx.beginPath();\n\t\tctx.arc(32, 32, 37, 124 * deg2rad, 148 * deg2rad);\n\t\tctx.stroke();\n\t}\n\tctx.shadowColor = 'transparent';\n}","/**\n * @module game\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport { Vector2, SimilarityTransform } from 'math/linalg';\nimport { drawLevelObjects } from './render';\nimport { Drawable, drawCanvasRotatedInBox } from 'utils/render';\nimport { ColorTheme } from 'vpl/color-theme';\nimport { Level, CoreGame, ImageDecoder, ImageIndices, ScriptPackage, BlockWithContext } from './game-core';\nimport { assert, assertString, assertStringOrNull } from 'utils/assert';\nimport * as flatbuffers from 'flatbuffers';\nimport * as CandliFB from 'flatbuffers/all';\nimport { bufferToHex } from 'utils/buffers';\nimport { createOffscreenCanvasAnd2DContext } from 'utils/canvas';\nimport { imageDataToCanvas, canvasToImageData } from 'utils/conversion';\nimport { AnyCanvas, isCanvas } from 'imgui/imgui-types';\nimport { createUUID, AssetNamespace, GameNamespace, createUUIDString } from 'network/api-uuid';\nimport { Sound } from 'blocks/actions/sound/sound-interface';\nimport { encodePng } from 'utils/image';\nimport { WebAudioSound, packWav } from 'blocks/actions/sound/sound';\nimport { defined, downcast, toStr } from 'utils/types';\nimport { GameAndItsBinary, storage } from 'storage/storage';\nimport { loadGameFromBinary } from './game-loader';\nimport { cloneDeepWith } from 'lodash-es';\n\n// \"extends\" ImageIndices\nexport type CanvasWithKeyIndices = Map;\n\n/// A UUID-based key\nexport type AssetKey = ArrayBuffer;\n\n/**\n * A base class for assets.\n */\nexport abstract class Asset {\n\t/** the asset UUID for this canvas */\n\tprivate readonly _key: AssetKey;\n\n\t/**\n\t * Constructor.\n\t * @param key either the UUID for this asset, or null if it must be generated\n\t */\n\tconstructor(key: AssetKey | null = null) {\n\t\t// do we need to compute key?\n\t\tif (key === null) {\n\t\t\t// generate game id based on host + timestamp (V1) and the AssetNamespace (V5)\n\t\t\tthis._key = createUUID(AssetNamespace);\n\t\t} else {\n\t\t\tthis._key = key;\n\t\t}\n\t}\n\n\t/** the UUID of this asset */\n\tget rawKey() { return this._key; }\n\n\t/** the UUID of this asset, in text */\n\tget textKey() { return bufferToHex(this.rawKey); }\n\n\t/** the a version of this asset for storage on the server */\n\tabstract get toStored(): ArrayBuffer;\n\n\t/** Ensure data integrity by checking that they are of the right type and valid. */\n\tvalidate() {\n\t\tassert(this.rawKey.byteLength === 16, `UUID key has wrong length ${this.rawKey.byteLength}`);\n\t}\n}\n\n/**\n * A canvas that has a key corresponding to its content.\n */\nexport class CanvasWithKey extends Asset implements Drawable {\n\t/**\n\t * Constructor.\n\t * @param canvasOrImageData the canvas or the image data of this sprite\n\t * @param key either the UUID for this asset, or null if it must be generated\n\t */\n\tconstructor(private canvasOrImageData: AnyCanvas | ImageData, key: AssetKey | null = null) {\n\t\tsuper(key);\n\t}\n\n\tget toStored() {\n\t\treturn encodePng(this.imageData);\n\t}\n\n\tget canvas() {\n\t\tif (!isCanvas(this.canvasOrImageData)) {\n\t\t\tthis.canvasOrImageData = imageDataToCanvas(this.canvasOrImageData);\n\t\t}\n\t\treturn this.canvasOrImageData;\n\t}\n\n\tget imageData() {\n\t\treturn this.canvasOrImageData instanceof ImageData ?\n\t\t\tthis.canvasOrImageData :\n\t\t\tcanvasToImageData(this.canvasOrImageData)\n\t\t;\n\t}\n\n\t/**\n\t * Shadow-clone this sprite\n\t */\n\tclone() {\n\t\treturn new CanvasWithKey(this.canvasOrImageData, this.rawKey);\n\t}\n\n\t/** the size of canvas */\n\tget size() {\n\t\treturn new Vector2(this.canvasOrImageData.width, this.canvasOrImageData.height);\n\t}\n\n\tvalidate() {\n\t\tsuper.validate();\n\t\tassert(isCanvas(this.canvasOrImageData) || this.canvasOrImageData instanceof ImageData, `canvasOrImageData (${toStr(this.canvasOrImageData)}) is neither a HTMLCanvasElement, nor an OffscreenCanvas, nor an ImageData`);\n\t}\n\n\tdraw(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number) {\n\t\tctx.drawImage(this.canvas, x, y, width, height);\n\t}\n}\n\nexport function newCanvasWithKeyMaxSize(canvas: AnyCanvas, maxSize: number) {\n\tconst scaling = Math.min(maxSize / canvas.width, maxSize / canvas.height, 1.0);\n\tif (scaling < 1.0) {\n\t\tconst nw = Math.floor(canvas.width * scaling);\n\t\tconst nh = Math.floor(canvas.height * scaling);\n\t\tconst { canvas: scaled, ctx } = createOffscreenCanvasAnd2DContext(nw, nh);\n\t\tctx.drawImage(canvas, 0, 0, nw, nh);\n\t\treturn [new CanvasWithKey(scaled), scaling] as [CanvasWithKey, number];\n\t} else {\n\t\treturn [new CanvasWithKey(canvas), 1.0] as [CanvasWithKey, number];\n\t}\n}\n\n/**\n * A sound that has a key corresponding to its content.\n */\nexport class SoundWithKey extends Asset implements Sound {\n\tconstructor(\n\t\tpublic readonly sound: Sound,\n\t\tkey: AssetKey | null = null\n\t) {\n\t\tsuper(key);\n\t}\n\n\tget toStored() {\n\t\treturn packWav(...this.sound.getSamplesAndRate());\n\t}\n\n\tgetSamplesAndRate(): [Float32Array, number] {\n\t\treturn this.sound.getSamplesAndRate();\n\t}\n\n\tconnectToAudioNodeAndPlay(ctx: AudioContext, node: AudioNode): void {\n\t\tthis.sound.connectToAudioNodeAndPlay(ctx, node);\n\t}\n}\n\nexport function generateGameStringId() {\n\t// generate game id based on host + timestamp (V1) and the GameNamespace (V5)\n\treturn createUUIDString(GameNamespace);\n}\n\nexport type DrawableToUUID = (drawable: Drawable) => Uint8Array;\n\nexport type SoundToUUID = (sound: Sound) => Uint8Array;\n\n\nexport class InMemoryStoredGame {\n\tconstructor(\n\t\tpublic readonly id: string,\n\t\tpublic readonly name: string | null,\n\t\tpublic readonly binaryGame: Uint8Array,\n\t\tpublic readonly sprites: Map,\n\t\tpublic readonly sounds: Sound[],\n\t\tpublic readonly scripts: ScriptPackage[]\n\t) {\n\t}\n\n\ttoGame(): Game {\n\t\tconst buffer = new flatbuffers.ByteBuffer(this.binaryGame);\n\t\tconst fbGame = CandliFB.Game.getRootAsGame(buffer);\n\t\tconst imageDecoder = (index: number) => defined(this.sprites.get(index));\n\t\treturn new Game(\n\t\t\tfbGame,\timageDecoder, this.sounds, this.scripts, this.id, this.name\n\t\t);\n\t}\n}\n\nexport class Game extends CoreGame {\n\tpublic id: string;\n\tpublic _name: string | null;\n\n\tconstructor();\n\tconstructor(fbGame: CandliFB.Game, imageDecoder: ImageDecoder, sounds: Sound[], scripts: ScriptPackage[], id: string, name: string | null);\n\tconstructor(fbGame?: CandliFB.Game, imageDecoder?: ImageDecoder, sounds?: Sound[], scripts?: ScriptPackage[], id?: string, name?: string | null) {\n\t\tif (fbGame !== undefined) {\n\t\t\tsuper(fbGame, imageDecoder!, sounds!, scripts!);\n\t\t\tthis.id = id!;\n\t\t\tthis._name = name!;\n\t\t} else {\n\t\t\tsuper();\n\t\t\tthis.id = generateGameStringId();\n\t\t\tthis._name = null;\n\t\t}\n\t}\n\n\tstatic async load(key: string) {\n\t\treturn loadGameFromBinary(key, storage())\n\t}\n\n\tregenerateId() {\n\t\tthis.id = generateGameStringId();\n\t}\n\n\tget name() {\n\t\treturn this._name;\n\t}\n\n\tset name(name: string | null) {\n\t\tif (name !== this._name) {\n\t\t\tthis._name = name;\n\t\t\tvoid storage().setGameName(this.id, name);\n\t\t}\n\t}\n\n\treplaceWith(that: Game) {\n\t\tsuper.replaceWith(that);\n\t\tthis.id = that.id;\n\t\tthis._name = that.name;\n\t}\n\n\t/**\n\t * Clone this game.\n\t * Duplicates everything, except sound and asset data, as these are read-only and shared.\n\t */\n\tclone() {\n\t\treturn cloneDeepWith(this, (value) => {\n\t\t\tif (value instanceof CanvasWithKey) {\n\t\t\t\treturn value.clone();\n\t\t\t} else if (value instanceof WebAudioSound) {\n\t\t\t\treturn value.clone();\n\t\t\t} else {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}) as Game;\n\t}\n\n\tserialise(builder: flatbuffers.Builder, drawableToUUID: DrawableToUUID, soundToUUID: SoundToUUID, keepLocking: boolean): [number, ImageIndices] {\n\t\tconst imageIndices = this.collectImageIndices();\n\t\tconst imageKeysOffsets: number[] = [];\n\t\tfor (const [drawable] of imageIndices) {\n\t\t\timageKeysOffsets.push(CandliFB.UUID.createUUID(builder,\n\t\t\t\tCandliFB.UUID.createValueVector(builder,\n\t\t\t\t\tdrawableToUUID(drawable)\n\t\t\t\t)\n\t\t\t));\n\t\t}\n\t\tconst imageKeysOffset = CandliFB.Game.createImageKeysVector(builder, imageKeysOffsets);\n\t\tconst soundKeysOffsets = this.sounds.map(sound =>\n\t\t\tCandliFB.UUID.createUUID(builder,\n\t\t\t\tCandliFB.UUID.createValueVector(builder,\n\t\t\t\t\tsoundToUUID(sound)\n\t\t\t\t)\n\t\t\t)\n\t\t);\n\t\tconst soundKeysOffset = CandliFB.Game.createSoundKeysVector(builder, soundKeysOffsets);\n\t\tconst childrenOffsets = super.serialiseChildren(builder, imageIndices, keepLocking);\n\t\tCandliFB.Game.startGame(builder);\n\t\tsuper.serialiseSelf(builder, childrenOffsets);\n\t\tCandliFB.Game.addImageKeys(builder, imageKeysOffset);\n\t\tCandliFB.Game.addSoundKeys(builder, soundKeysOffset);\n\t\treturn [CandliFB.Game.endGame(builder), imageIndices];\n\t}\n\n\t/** Get the serialised game as a byte array from Flatbuffers, plus the image index map */\n\tasUint8Array(drawableToUUID: DrawableToUUID, soundToUUID: SoundToUUID, keepLocking = false): [Uint8Array, ImageIndices] {\n\t\tconst BuilderBufferSize = 65536;\n\t\tconst builder = new flatbuffers.Builder(BuilderBufferSize);\n\t\tconst [gameIndex, imageIndices] = this.serialise(builder, drawableToUUID, soundToUUID, keepLocking);\n\t\tbuilder.finish(gameIndex);\n\t\treturn [builder.asUint8Array(), imageIndices];\n\t}\n\n\t/** Get the serialised game as a byte array from Flatbuffers, plus the image index map, using asset UUID */\n\tasUint8ArrayWithAssetUUID(): [Uint8Array, ImageIndices] {\n\t\treturn this.asUint8Array(\n\t\t\t(drawable: Drawable) => new Uint8Array(downcast(CanvasWithKey, drawable).rawKey),\n\t\t\t(sound: Sound) => new Uint8Array(downcast(SoundWithKey, sound).rawKey)\n\t\t)\n\t}\n\n\t/** Get an in-memory stored representation of this game */\n\ttoInMemoryStored(): InMemoryStoredGame {\n\t\tconst [data, imageIndices] = this.asUint8ArrayWithAssetUUID();\n\t\tconst sprites = new Map();\n\t\tfor (const [drawable, index] of imageIndices) {\n\t\t\tsprites.set(index, drawable);\n\t\t}\n\t\tconst sounds = [...this.sounds];\n\t\tconst scripts = [...this.scriptPackages];\n\t\treturn new InMemoryStoredGame(this.id, this.name, data, sprites, sounds, scripts);\n\t}\n\n\tsave() {\n\t\treturn storage().saveGame(this);\n\t}\n\n\tvalidate(acceptLocking = false) {\n\t\tsuper.validate(acceptLocking);\n\t\tassertString(this.id, 'id');\n\t\tassertStringOrNull(this.name, 'name');\n\t}\n\n\t/**\n\t * Return a square miniature of the game\n\t * @param size the size of the square in pixels\n\t */\n\tgetMiniature(size: number) {\n\t\tconst { canvas, ctx } = this.getLevelMiniatureCanvas(this.level, size);\n\t\tconst margin = 4;\n\t\tconst miniSize = 16;\n\t\tconst y = size - margin - miniSize;\n\t\tctx.fillStyle = ColorTheme.surface;\n\t\tctx.globalAlpha = 0.8;\n\t\tctx.fillRect(0, y - margin, size, miniSize + 2 * margin);\n\t\tctx.globalAlpha = 1.0;\n\t\tthis.objects.some((objectType, index) => {\n\t\t\tconst x = margin + index * (miniSize + margin);\n\t\t\tif (x > size) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst angle = objectType.firstFrame.partFromPixelTransform.rotationAngle;\n\t\t\tdrawCanvasRotatedInBox(ctx, objectType.sprite, angle, miniSize, new Vector2(x, y));\n\t\t\treturn false;\n\t\t});\n\t\treturn canvas;\n\t}\n\n\t/**\n\t * Return a square miniature of a level\n\t * @param size the size of the square in pixels\n\t * @param level the level to use, must be part of [[this]]\n\t */\n\tprivate getLevelMiniatureCanvas(level: Level, size: number) {\n\t\tconst miniatureCenter = new Vector2(size * 0.5, size * 0.5);\n\t\tconst camera = level.camera;\n\t\tconst invScaleFactor = camera.extent.maximum() / size;\n\t\t// note: we use temporary space X that is miniature scale but not centered\n\t\tconst worldFromX = SimilarityTransform.scalingTranslation(invScaleFactor, camera.position);\n\t\tconst miniatureFromX = SimilarityTransform.translation(miniatureCenter);\n\t\tconst miniatureToWorld = worldFromX.after(miniatureFromX.inverse());\n\t\tconst worldToMiniature = miniatureToWorld.inverse();\n\t\tconst { canvas, ctx } = createOffscreenCanvasAnd2DContext(size, size);\n\t\tctx.fillStyle = level.camera.backgroundColorRgbHexString;\n\t\tctx.fillRect(0, 0, size, size);\n\t\tdrawLevelObjects(ctx, worldToMiniature, level, null, null);\n\t\tif (this.twoPlayers) {\n\t\t\tctx.save();\n\t\t\tctx.translate(size - 32, 8);\n\t\t\tctx.fillStyle = 'black';\n\t\t\tconst path = new Path2D('m12 0a12 12 0 0 0-12 12 12 12 0 0 0 12 12 12 12 0 0 0 12-12 12 12 0 0 0-12-12zm-4.4707 6.7109c1.0449 0 1.8558 0.25039 2.4316 0.75195 0.58051 0.50156 0.86914 1.2101 0.86914 2.125 0 0.50156-0.1286 1.0111-0.38867 1.5313s-0.70629 1.1264-1.3379 1.8184l-2.3066 2.4316h4.3613v1.623h-6.9531v-1.3789l3.2812-3.498c0.45048-0.49228 0.78246-0.92218 0.99609-1.2891 0.21827-0.36688 0.32812-0.71519 0.32812-1.0449 0-0.45048-0.11424-0.80317-0.3418-1.0586-0.22756-0.26007-0.552-0.38867-0.97461-0.38867-0.45512 0-0.81536 0.15686-1.0801 0.47266-0.26007 0.31116-0.39062 0.72157-0.39062 1.2324h-2.0195c0-0.61767 0.14688-1.1806 0.43945-1.6914 0.29722-0.51085 0.71519-0.91128 1.2539-1.1992 0.53872-0.29258 1.1493-0.4375 1.832-0.4375zm5.4277 0.13867h3.957c0.76164 0 1.43 0.13932 2.0059 0.41797 0.58051 0.27865 1.0267 0.67591 1.3379 1.1914 0.31116 0.51085 0.4668 1.0932 0.4668 1.748 0 0.99384-0.34075 1.7796-1.0234 2.3555-0.67804 0.57122-1.6194 0.85547-2.8223 0.85547h-1.832v3.5742h-2.0898zm2.0898 1.6934v3.1836h1.8672c0.55265 1e-6 0.97378-0.13055 1.2617-0.39062 0.29258-0.26007 0.43945-0.63225 0.43945-1.1152 0-0.49692-0.14687-0.89857-0.43945-1.2051-0.29258-0.30651-0.69739-0.46337-1.2129-0.47266z');\n\t\t\tctx.fill(path);\n\t\t\tctx.restore();\n\t\t}\n\t\treturn { canvas, ctx };\n\t}\n}\n\nexport function blockPath(game: Game, blockWithCtx: BlockWithContext) {\n\tconst objectTypeName = blockWithCtx.objectType.nameOrIndex(game);\n\treturn `${game.id}/${objectTypeName}/${blockWithCtx.ruleIndex}/${blockWithCtx.category}/${blockWithCtx.blockIndex}`;\n}\n","/**\n * @module level\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport { Camera, Level, ObjectInstance, ObjectType } from '../game/game-core';\nimport { ObjectInstancesArtist } from './sprite-artist';\nimport { SimilarityTransform, Vector2 } from '../math/linalg';\nimport { Snappable, SnapConstraints, SnapResult } from './snapping';\nimport { DragContentArtist, DragFrom, GUI, OptionalUserActions, UserAction } from 'imgui/imgui';\nimport { ColorTheme } from 'vpl/color-theme';\nimport { FixedSizeArray } from 'utils/array-types';\nimport { Category as BlockCategory } from 'vpl/block';\nimport { Constructor, defined, dynamicCast, nonnull } from 'utils/types';\nimport { Rect } from 'math/geometry';\nimport { stringUnionCast } from 'utils/union-types';\n\nexport function setCandliColorTheme() {\n\tColorTheme.condition = '#651fff';\n\tColorTheme.action = '#ff6d00';\n}\n\nexport const CameraGreen = '#009600';\n\nexport type SnappingFunction = (toSnap: Snappable, snapConstraints: SnapConstraints, level: Level, objectInstances: ObjectInstance[], worldToScreen: SimilarityTransform, screenToWorld: SimilarityTransform, scalingActive: boolean) => SnapResult;\n\nexport type EditableLayers = FixedSizeArray;\nexport const LayersAllEditable: EditableLayers = [true, true, true, true];\n\nexport interface LevelEditorLayout {\n\twidgetRect: Rect;\n\tscaleIndicatorAtRight: boolean;\n\tscreenToWorld: SimilarityTransform;\n\tworldToScreen: SimilarityTransform;\n}\n\nexport const enum DraggableItemState {\n\tNotDragging,\n\tDraggingThis,\n\tDraggingOtherSameSystem,\n\tDraggingOtherSystem,\n}\n\nexport abstract class DraggableItem {\n\tabstract contains(screenPos: Vector2): boolean;\n\tabstract draw(ui: GUI, draggingState: DraggableItemState): void;\n\tabstract get artist(): DragContentArtist;\n\n\tpressed(_screenPos: Vector2): OptionalUserActions { return null; }\n\tdragging(_screenPos: Vector2, _drag: DragFrom): OptionalUserActions { return null; }\n\tclicked(_screenPos: Vector2): OptionalUserActions { return null; }\n\tendDrag(_screenPos: Vector2): OptionalUserActions { return null; }\n}\n\nexport abstract class DraggableSystem {\n\tprotected _selected = false;\n\tprotected editorLayout: LevelEditorLayout | null = null;\n\n\tselect() {\n\t\tthis._selected = true;\n\t}\n\tdeselect() {\n\t\tthis._selected = false;\n\t}\n\tget selected() {\n\t\treturn this._selected;\n\t}\n\n\t/// Called every time the layout is changed, system is supposed to cache\n\t/// to avoid re-computing transforms all the time.\n\tupdateEditorLayout(layout: LevelEditorLayout) {\n\t\tthis.editorLayout = layout;\n\t}\n\n\tget layout() {\n\t\treturn nonnull(this.editorLayout);\n\t}\n\n\tabstract items(): IterableIterator;\n\n\tdrawBeforeItems(_ui: GUI) {}\n\tdrawAfterItems(_ui: GUI) {}\n}\n\nexport class EditingData {\n\t// editing state\n\tprivate selectedElements: Set = new Set();\n\taccumulatedInputTrafoBefore = new SimilarityTransform();\n\taccumulatedInputTrafoAfter = new SimilarityTransform();\n\tpossibleDoubleClick = false;\n\tlastSelectionTime = 0;\n\tscalingActive = false;\n\tdraggableSystems: DraggableSystem[] = [];\n\trotateKey = false;\n\tscaleKey = false;\n\tdragSelectStartPos: Vector2 | null = null;\n\n\t// editor settings\n\tbringObjectToFrontOnDrag = true;\n\tallowPanAndZoom = true;\n\tshowGridAndBounds = true;\n\ttransformComponents = {\n\t\ttranslation: true,\n\t\trotation: true,\n\t\tscaling: true\n\t};\n\teditableLayers = LayersAllEditable;\n\tsnappingEnabled = true;\n\tcanDuplicate: ((object: ObjectInstance) => boolean) | null = null;\n\n\t// overrides\n\toutlinedElement: null | ObjectInstance = null;\n\n\tconstructor(\n\t\tpublic levelGetter: () => Level,\n\t\tpublic spriteLevelArtist: ObjectInstancesArtist,\n\t\tpublic snappingFunction: SnappingFunction\n\t) {\n\t}\n\n\tresetDefaultSettings() {\n\t\tthis.bringObjectToFrontOnDrag = true;\n\t\tthis.allowPanAndZoom = true;\n\t\tthis.draggableSystems = [];\n\t\tthis.showGridAndBounds = true;\n\t}\n\n\tsystemOfType(type: Constructor): T | null {\n\t\tfor (const system of this.draggableSystems) {\n\t\t\tconst systemOfTypeT = dynamicCast(type, system);\n\t\t\tif (systemOfTypeT !== null) {\n\t\t\t\treturn systemOfTypeT;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tremoveSystemOfType(type: Constructor) {\n\t\tthis.draggableSystems = this.draggableSystems.filter(\n\t\t\t(system) => dynamicCast(type, system) === null\n\t\t);\n\t}\n\n\tprocessRotateAndShiftKeys(event: KeyboardEvent, press: boolean) {\n\t\tif (event.which === 82) { // 'r'\n\t\t\tthis.rotateKey = press;\n\t\t\treturn true;\n\t\t}\n\t\tif (event.which === 83) { // 's'\n\t\t\tthis.scaleKey = press;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tget selection() {\n\t\treturn this.selectedElements;\n\t}\n\n\tget singleSelection() {\n\t\treturn this.selectedElements.size === 1 ?\n\t\t\tdefined(this.selectedElements.values().next().value) :\n\t\t\tnull;\n\t}\n\n\thasSelection() {\n\t\treturn this.selectedElements.size > 0;\n\t}\n\n\tclearSelection() {\n\t\tthis.selectedElements.clear();\n\t}\n\n\tsetSelection(objects: ObjectInstance[]) {\n\t\tthis.selectedElements = new Set(objects);\n\t}\n\n\tsetSelectionSet(objects: Set) {\n\t\tthis.selectedElements = objects;\n\t}\n\n\tsetSingleSelection(object: ObjectInstance) {\n\t\tthis.selectedElements = new Set([object]);\n\t}\n\n\taddToSelection(object: ObjectInstance) {\n\t\tthis.selectedElements.add(object);\n\t}\n\n\tremoveFromSelection(object: ObjectInstance) {\n\t\tthis.selectedElements.delete(object);\n\t}\n\n\tcanDuplicateOptSelection() {\n\t\treturn this.canDuplicate !== null &&\n\t\t\t[...this.selectedElements].every(this.canDuplicate);\n\t}\n\n\thasMoveableSelection() {\n\t\treturn this.hasSelection() &&\n\t\t\t[...this.selectedElements].every((obj) => !obj.movementLocked);\n\t}\n\n\thasDeletableSelection() {\n\t\treturn this.hasSelection() &&\n\t\t\t[...this.selectedElements].every((obj) => !obj.deleteLocked);\n\t}\n\n\thasCopyableSelection() {\n\t\treturn this.hasSelection() &&\n\t\t\t[...this.selectedElements].every((obj) => !obj.copyLocked);\n\t}\n\n\thasInactiveSelection() {\n\t\treturn [...this.selectedElements].some((obj) => !this.editableLayers[obj.type.kind]);\n\t}\n}\n\n// user actions generated by interacting with the level editor\nexport class InstanceClicked extends UserAction {\n\tconstructor(public object: ObjectInstance) {\n\t\tsuper();\n\t}\n}\nexport class InstanceDoubleClicked extends UserAction {\n\tconstructor(public object: ObjectInstance) {\n\t\tsuper();\n\t}\n}\nexport class InstanceAdded extends UserAction {\n\tconstructor(public object: ObjectInstance) {\n\t\tsuper();\n\t}\n}\nexport class InstanceRemoved extends UserAction {\n\tconstructor(public object: ObjectInstance) {\n\t\tsuper();\n\t}\n}\nexport class InstanceEdited extends UserAction {\n\tconstructor(public object: ObjectInstance) {\n\t\tsuper();\n\t}\n}\nexport class InstancePoseChanged extends UserAction {\n\tconstructor(public object: ObjectInstance) {\n\t\tsuper();\n\t}\n}\nexport class CameraZoomed extends UserAction {\n\tconstructor(public camera: Camera) {\n\t\tsuper();\n\t}\n}\nexport class CameraMoved extends UserAction {\n\tconstructor(public camera: Camera) {\n\t\tsuper();\n\t}\n}\nexport class PanAndZoom extends UserAction {\n\tconstructor() {\n\t\tsuper();\n\t}\n}\n\nexport namespace Activity {\n\texport type Kind = ObjectType.KindString;\n\texport type Mean = 'arithmetic' | 'geometric' | 'harmonic';\n\n\texport interface VarDescription {\n\t\tid: number,\n\t\tname: string,\n\t\tdesc?: string,\n\t\troles: string[],\n\t\tkind?: Kind,\n\t\tsize?: [number, Mean]\n\t\tachieves?: string[],\n\t}\n\n\texport type ConfigurationChangeOp = '@' | '+' | '-' | '>';\n\n\texport interface ConfigurationChangeDescription {\n\t\top: ConfigurationChangeOp,\n\t\tpath: Array,\n\t\tvalue?: number\n\t}\n\n\texport type StatementDescription = {\n\t\tdesc: string,\n\t\tachieves?: string[],\n\t};\n\n\texport type RunDescription = {\n\t\tdesc?: string,\n\t\tplayed: string,\n\t\tcompleted?: [string, string],\n\t\tachieves?: string[],\n\t};\n\n\texport interface StepDescription {\n\t\ttitle: string,\n\t\timage?: string,\n\t\tdesc: string,\n\t\tconf?: Array,\n\t\tinit?: VarDescription,\n\t\tstmt: Array,\n\t\trun?: RunDescription\n\t}\n\n\texport type ActivityDescription = Array;\n\n\texport type StepValidationResult = Array;\n\texport type ValidationResult = Array;\n\n\texport type HintPath = string[];\n\texport type Hint = [HintPath, number];\n\n\texport const HintBlockCategories = ['condition', 'action'] as const;\n\texport type HintBlockCategoryType = typeof HintBlockCategories[number];\n\texport const HintBlockOps = ['insert', 'remove', 'modify', 'invert'] as const;\n\texport type HintBlockOpType = typeof HintBlockOps[number];\n\n\texport function castHintBlockCategory(value: string) {\n\t\tconst cat = stringUnionCast(Activity.HintBlockCategories, value);\n\t\tswitch (cat) {\n\t\t\tcase 'condition': return BlockCategory.Condition;\n\t\t\tcase 'action': return BlockCategory.Action;\n\t\t}\n\t}\n}\n","/**\n * @module runtime\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport { assertFalse, assertIntegerRange, assertNonNegativeInteger } from 'utils/assert';\n\nexport const enum VariableScope {\n\tGame = 0,\n\tLevel = 1, // currently not used\n\tObjectType = 2,\n\tObjectInstance = 3\n}\nconst VariableScopeShift = 12;\nconst VariableIndexMask = 0xff;\n\nexport function visualIndexToScope(index: 0 | 1 | 2) {\n\tswitch (index) {\n\t\tcase 0: return VariableScope.Game;\n\t\tcase 1: return VariableScope.ObjectType;\n\t\tcase 2: return VariableScope.ObjectInstance;\n\t}\n}\n\nexport function scopeAsVisualIndex(scope: VariableScope) {\n\tswitch (scope) {\n\t\tcase VariableScope.Game: return 0;\n\t\tcase VariableScope.ObjectType: return 1;\n\t\tcase VariableScope.ObjectInstance: return 2;\n\t\tdefault: assertFalse(`Invalid scope: ${scope}`);\n\t}\n}\n\nexport function variableScope(address: number): VariableScope {\n\treturn address >> VariableScopeShift;\n}\n\nexport function variableIndex(address: number) {\n\treturn address & VariableIndexMask;\n}\n\nexport function variableScopeAndIndex(address: number): [VariableScope, number] {\n\treturn [ variableScope(address), variableIndex(address) ];\n}\n\nexport function variableAddress(scope: VariableScope, index: number) {\n\treturn (scope << VariableScopeShift) | index;\n}\n\nexport function validateAddress(address: number) {\n\tassertNonNegativeInteger(address, 'variable address');\n\tconst [scope, index] = variableScopeAndIndex(address);\n\tswitch (scope) {\n\t\tcase VariableScope.Game:\n\t\tcase VariableScope.ObjectType:\n\t\tcase VariableScope.ObjectInstance:\n\t\t\tassertIntegerRange(index, 0, 255, 'variable index out of range');\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassertFalse(`invalid variable scope ${scope}`)\n\t}\n}","/**\n * @module storage\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\n// cSpell: words IDBPDatabase ittr\nimport { showError } from 'error/error-screen';\nimport { openDB, IDBPDatabase, DBSchema } from 'idb/with-async-ittr.js';\nimport { ContentTypes } from 'network/api-types';\nimport { qsT } from 'translator/translator';\nimport { assertString } from 'utils/assert';\nimport { sleep } from 'utils/async';\nimport { KnownKeys, nonnull } from 'utils/types';\n\ninterface BacklogDB extends DBSchema {\n\tgames: {\n\t\tkey: string;\n\t\tvalue: ArrayBuffer;\n\t};\n\t// The following backlog is deprecated, it is use only for migration.\n\timages: {\n\t\tkey: string;\n\t\tvalue: ImageData;\n\t};\n\t// The following backlog is deprecated, it is use only for migration.\n\tassetFiles: {\n\t\tkey: string; // asset UUID as hex string\n\t\tvalue: ArrayBuffer; // file content\n\t};\n\timagePngAssets: {\n\t\tkey: string; // asset UUID as hex string\n\t\tvalue: ArrayBuffer; // file content\n\t};\n\taudioWavAssets: {\n\t\tkey: string; // asset UUID as hex string\n\t\tvalue: ArrayBuffer; // file content\n\t};\n\tminiatures: {\n\t\tkey: string; // game key\n\t\tvalue: ImageData;\n\t};\n\tgameNames: {\n\t\tkey: string; // game key\n\t\tvalue: string | null;\n\t};\n\tcreatedGames: {\n\t\tkey: string; // game key\n\t\tvalue: string; // workspace\n\t};\n}\n\nexport type BacklogKinds = KnownKeys;\n\ntype BacklogIDB = IDBPDatabase;\nlet currentDb: BacklogIDB | null = null;\nlet getDbPromise: Promise> | null = null;\n\nasync function openBacklogDB() {\n\tconst targetVersion = 4;\n\tconsole.info('Opening backlog database' + (targetVersion ? ` (target version ${targetVersion})` : ''));\n\treturn openDB('backlog', targetVersion, {\n\t\tupgrade(db, oldVersion, newVersion, _transaction) {\n\t\t\tconsole.info('Upgrade event for backlog object store', oldVersion, newVersion);\n\t\t\tfor (let version = oldVersion + 1; version <= targetVersion; version++) {\n\t\t\t\tconsole.debug('Creating backlog object stores version', version);\n\t\t\t\tswitch (version) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tdb.createObjectStore('games');\n\t\t\t\t\t\tdb.createObjectStore('images');\n\t\t\t\t\t\tdb.createObjectStore('miniatures');\n\t\t\t\t\t\tdb.createObjectStore('createdGames');\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tdb.createObjectStore('gameNames');\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tdb.createObjectStore('assetFiles');\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\tdb.createObjectStore('imagePngAssets');\n\t\t\t\t\t\tdb.createObjectStore('audioWavAssets');\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n}\n\nasync function openBacklogDBRetry() {\n\tconst RetryCount = 3;\n\tfor (let i = 0; i < RetryCount; i++) {\n\t\ttry {\n\t\t\tconst db = await openBacklogDB();\n\t\t\tif (await isDBWorking(db)) {\n\t\t\t\treturn db;\n\t\t\t}\n\t\t\tdb.close();\n\t\t} catch (e) {\n\t\t\tconsole.error('idb open failed, retrying...', e);\n\t\t}\n\t\tawait sleep(1);\n\t}\n\tthrow Error(`idb open failed after ${RetryCount} retries`);\n}\n\nasync function isDBWorking(db: IDBPDatabase) {\n\ttry {\n\t\tconst tx = db.transaction('games');\n\t\tawait tx.done;\n\t\treturn true;\n\t} catch (e) {\n\t\tconsole.error('idb create transaction failed, close and reopen...', e, db);\n\t\treturn false;\n\t}\n}\n\nasync function dbOperationWithErrorReporting(f: () => Promise) {\n\ttry {\n\t\treturn await f();\n\t} catch (e) {\n\t\tshowError(qsT('You are offline and I cannot save your work locally due to a browser bug. Please make sure your internet connection works and restart your device to continue working.'));\n\t\tthrow e;\n\t}\n}\n\nfunction getDb() {\n\tconst curDb = currentDb;\n\tif (curDb === null) {\n\t\t// if no db is open, open it if no one else is doing that opening already\n\t\tif (getDbPromise === null) {\n\t\t\tgetDbPromise = dbOperationWithErrorReporting(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tcurrentDb = await openBacklogDBRetry();\n\t\t\t\t\treturn currentDb;\n\t\t\t\t} finally {\n\t\t\t\t\t// we completed the open, so we can clear the promise\n\t\t\t\t\tgetDbPromise = null;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t} else {\n\t\t// if db is open, check if it is working if no one else is doing that check already\n\t\tif (getDbPromise === null) {\n\t\t\tgetDbPromise = dbOperationWithErrorReporting(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tif (await isDBWorking(curDb)) {\n\t\t\t\t\t\treturn curDb;\n\t\t\t\t\t}\n\t\t\t\t\tcurDb.close();\n\t\t\t\t\t// Note: the ordering of the following two lines is critical for correctness\n\t\t\t\t\t// re-open\n\t\t\t\t\tcurrentDb = await openBacklogDBRetry();\n\t\t\t\t\treturn currentDb;\n\t\t\t\t} finally {\n\t\t\t\t\t// we completed the check, so we can clear the promise\n\t\t\t\t\tgetDbPromise = null;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\treturn getDbPromise;\n}\n\nexport interface BacklogInterface {\n\tinsert(key: string, data: D): Promise;\n\tremove(key: string): Promise;\n\tget(key: string): Promise;\n\tempty(): Promise;\n};\n\nexport class Backlog implements BacklogInterface {\n\tconstructor(public readonly kind: K) {}\n\n\tasync insert(key: string, data: D) {\n\t\tconst db = await getDb();\n\t\tconst tx = db.transaction(this.kind, 'readwrite');\n\t\tconst store = tx.objectStore(this.kind);\n\t\tawait store.put(data, key);\n\t\tawait tx.done;\n\t}\n\n\tasync remove(key: string) {\n\t\tconst db = await getDb();\n\t\tconst tx = db.transaction(this.kind, 'readwrite');\n\t\tconst store = tx.objectStore(this.kind);\n\t\tawait store.delete(key);\n\t\tawait tx.done;\n\t}\n\n\tasync get(key: string) {\n\t\tconst db = await getDb();\n\t\tconst tx = db.transaction(this.kind);\n\t\tconst store = tx.objectStore(this.kind);\n\t\treturn store.get(key);\n\t}\n\n\tasync list(keyFilter = (_: string) => true) {\n\t\tconst output: [BacklogDB[K]['key'], BacklogDB[K]['value']][] = [];\n\t\tconst db = await getDb();\n\t\tconst tx = db.transaction(this.kind);\n\t\tconst store = tx.objectStore(this.kind);\n\t\tfor await (const cursor of store) {\n\t\t\tconst ks = cursor.key as string;\n\t\t\tassertString(ks, 'cursor.key');\n\t\t\tif (keyFilter(ks)) {\n\t\t\t\toutput.push([ks, cursor.value]);\n\t\t\t}\n\t\t}\n\t\treturn output;\n\t}\n\n\tasync empty() {\n\t\tconst db = await getDb();\n\t\tconst tx = db.transaction(this.kind);\n\t\tconst store = tx.objectStore(this.kind);\n\t\treturn await store.count() === 0;\n\t}\n}\n\nexport const gamesBacklog = new Backlog('games');\nexport const imagePngAssetsBacklog = new Backlog('imagePngAssets');\nexport const audioWavAssetsBacklog = new Backlog('audioWavAssets');\nexport const miniaturesBacklog = new Backlog('miniatures');\nexport const gameNamesBacklog = new Backlog('gameNames');\nexport const createdGamesBacklog = new Backlog('createdGames');\n\nexport const assetsBacklogsWithContentTypes = [\n\t[imagePngAssetsBacklog, ContentTypes.ImagePng],\n\t[audioWavAssetsBacklog, ContentTypes.AudioWav]\n] as const;","/**\n * @module storage\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport { Game, CanvasWithKey, SoundWithKey } from 'game/game';\nimport { Counter, Workspace } from './db';\nimport { AssetManager } from './asset-manager';\nimport { nonnull } from 'utils/types';\nimport { assertNull } from 'utils/assert';\n\nexport type GameAndItsBinary = [Game, Uint8Array];\n\nexport interface GameSaveResult {\n\tserialisedGame: Uint8Array;\n\tgameSavePromise: Promise;\n}\n\n/// The main interface to the underlying storage.\nexport interface Storage {\n\t// Attributes\n\tgetAttribute(name: string): Promise;\n\tsetAttribute(name: string, value: string): Promise;\n\n\t// Workspaces\n\tgetWorkspace(name: string, insecure: boolean): Workspace;\n\n\t// Counters\n\tgetCounter(name: string): Counter;\n\n\t// Games\n\t/**\n\t * Attempts to load a game's binary.\n\t * This method is expected to call loadBinaryGame to do the actual load.\n\t * @param gameKey the key of the game to load\n\t * @returns a promise to the game if load was successful, throws an exception if it fails\n\t */\n\tloadGameBinary(gameKey: string): Promise;\n\n\t/**\n\t * Get the URL of a game miniature, for reading.\n\t * @param gameKey game key\n\t * @returns a promise to the miniature URI\n\t */\n\tminiatureReadURI(gameKey: string): Promise;\n\n\t/**\n\t * Get the name of a game.\n\t * @param gameKey game key\n\t * @returns a promise to the game name\n\t */\n\tgetGameName(gameKey: string): Promise;\n\n\t/**\n\t * Save a game. First make a synced copy, then store async.\n\t * The caller is free to modify the game after this call.\n\t * @param game the game to save\n\t * @returns a class containing the serialised game and a promise that resolves after the game is saved (or that throws an exception if it fails)\n\t */\n\tsaveGame(game: Game): GameSaveResult;\n\n\t/**\n\t * Save a new binary game. Miniature will be a placeholder.\n\t * @param binaryGame the game to save\n\t * @returns a class containing the serialised game and a promise that resolves after the game is saved (or that throws an exception if it fails)\n\t */\n\tsaveNewBinaryGame(binaryGame: Uint8Array): GameSaveResult;\n\n\t/**\n\t * Set the name of a game.\n\t * @param gameKey the key of the game to set the name for\n\t * @param gameName the name of the game, or null to remove the name\n\t * @returns a promise that resolves after the game name is set (or that throws an exception if it fails)\n\t */\n\tsetGameName(gameKey: string, gameName: string | null): Promise;\n\n\t// Assets\n\n\t/// Access image assets.\n\tget imageAssets(): AssetManager;\n\n\t/// Access sound assets.\n\tget soundAssets(): AssetManager;\n\n\t/// Attempt to store pending assets and returns whether they were all stored successfully.\n\tstorePendingAssetsAndReturnSuccess(): Promise;\n\n\t/// For debugging only, prints a list of all assets to the console.\n\tlistAssets(): void;\n}\n\nlet storageImpl: Storage | null = null;\n\nexport function storageOpt() {\n\treturn storageImpl;\n}\n\nexport function storage() {\n\treturn nonnull(storageImpl, 'storage is not set');\n}\n\nexport function setStorage(storage: Storage) {\n\tassertNull(storageImpl, 'storage is already set');\n\tstorageImpl = storage;\n}","/**\n * @module utils\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\nimport { Constructor, TypeOfReturnValues, toStr } from './types';\nimport { robustStringify } from './text';\nimport { approximateEqual } from 'math/helpers';\n\n// eslint-disable-next-line no-underscore-dangle\nlet _devMode = false;\n\nexport function setDevMode(devMode: boolean) {\n\t_devMode = devMode;\n}\nexport function getDevMode() {\n\treturn _devMode;\n}\n\nexport class AssertionError extends Error {\n\ttoString() {\n\t\treturn 'Assertion' + super.toString();\n\t}\n}\n\n/**\n * Assert that [[condition]] is true.\n * @param condition - If not true, throw or console.trace if recoverable is true as well.\n * @param message - An optional message to send with the exception.\n */\nexport function assert(condition: boolean, message: string | null = null): asserts condition {\n\tif (!condition) {\n\t\tmessage = message !== null ? `Assertion failed: ${message}` : 'Assertion failed!';\n\t\tif (assertionListener) {\n\t\t\tassertionListener(message, false);\n\t\t}\n\t\tthrow new AssertionError(message);\n\t}\n}\n\n/**\n * Assert that [[condition]] is true.\n * @param condition - If not true, throw or console.trace if recoverable is true as well.\n * @param messageGenerator - A function that will generate the message if condition is false.\n */\nexport function assertL(condition: boolean, messageGenerator: () => string): asserts condition {\n\tif (!condition) {\n\t\tconst message = `Assertion failed: ${messageGenerator()}`;\n\t\tif (assertionListener) {\n\t\t\tassertionListener(message, false);\n\t\t}\n\t\tthrow new AssertionError(message);\n\t}\n}\n\n/**\n * Assert that [[condition]] is true.\n * In production, do not throw but console.trace(); in dev mode, behaves like assert.\n * @param condition - If not true, throw or console.trace if recoverable is true as well.\n * @param message - An optional message to send with the exception.\n */\nexport function assertRecoverable(condition: boolean, message: string | null = null) {\n\tif (!condition) {\n\t\tconst recoverable = !getDevMode();\n\t\tmessage = message !== null ? `Assertion failed: ${message}` : 'Assertion failed!';\n\t\tif (assertionListener) {\n\t\t\tassertionListener(message, recoverable);\n\t\t}\n\t\tif (recoverable) {\n\t\t\tconsole.trace(message);\n\t\t} else {\n\t\t\tthrow new AssertionError(message);\n\t\t}\n\t}\n}\n\n/**\n * Always throw, has the never type to be used in if and case blocks\n * @param message - An optional message to send with the exception.\n */\nexport function assertFalse(message: string | null = null): never {\n\tmessage = message !== null ? `Assertion failed: ${message}` : 'Assertion failed!';\n\tif (assertionListener) {\n\t\tassertionListener(message, false);\n\t}\n\tthrow new AssertionError(message);\n}\n\n/** Throws unimplemented, never returns */\nexport function unimplemented(): never {\n\tassertFalse('unimplemented!');\n}\n\n/**\n * Fail with a message.\n * In production, do not throw but console.trace(); in dev mode, behaves like assert.\n * @param message - An optional message to send with the exception.\n */\nexport function assertFalseRecoverable(message: string | null = null) {\n\tassertRecoverable(false, message);\n}\n\n/**\n * Asserts that [[object]] is a Javascript object.\n * @param value value to test.\n * @param name user-readable name of the object.\n */\nexport function assertObject(value: unknown, name: string): asserts value is object {\n\tif (typeof value === 'object') {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${robustStringify(value)}) is not an object, it is ${typeof value}`);\n}\n\n/**\n * Assert that [[object]] is an instance of [[type]].\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param object object to test.\n * @param name user-readable name of the object.\n */\nexport function assertType(type: Constructor, object: T, name: string): asserts object is U {\n\tassertTypeAny(type, object, name);\n}\n\n/**\n * Assert that [[object]] is an instance of [[type]].\n * In contrast to assertType the compiler will not check that [[object]] actually extends [[type]] (prefer the former if it also works)\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param object object to test.\n * @param name user-readable name of the object.\n */\nexport function assertTypeAny(type: Constructor, object: any, name: string): asserts object is U {\n\tif (object instanceof type) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${robustStringify(object)}) is not a ${type.name}`);\n}\n\nexport function assertInStringUnion(unionValues: T, value: string, name: string): asserts value is T[number] {\n\tif (unionValues.includes(value)) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} is not a member of ${unionValues}`);\n}\n\n/** An object that has a validate function, which might throw if object is invalid */\nexport interface Validable {\n\tvalidate(): void;\n}\n\n/**\n * Assert that [[object]] is an instance of [[type]] and its validate() function does not throw.\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param object object to test.\n * @param name user-readable name of the object.\n */\nexport function assertValidType(type: Constructor, object: T, name: string): asserts object is U {\n\tassertType(type, object, name);\n\tobject.validate();\n}\n\n/**\n * Assert that [[object]] is an array of instances of [[type]] and their validate() functions do not throw.\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param objects array to test.\n * @param name user-readable name of the array.\n */\nexport function assertValidArrayType(type: Constructor, objects: T[], name: string): asserts objects is U[] {\n\tassertType(Array, objects, name);\n\tlet i = 0;\n\tfor (const object of objects) {\n\t\tassertValidType(type, object, `${name}[${i}]`);\n\t\ti += 1;\n\t}\n}\n\n/**\n * Assert that [[object]] is an array of instances of [[type]].\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param objects array to test.\n * @param name user-readable name of the array.\n */\nexport function assertArrayType(type: Constructor, objects: T[], name: string): asserts objects is T[] {\n\tassertType(Array, objects, name);\n\tlet i = 0;\n\tfor (const object of objects) {\n\t\tassert(object instanceof type, `${name}[${i}] is not an instance of ${type}`);\n\t\ti += 1;\n\t}\n}\n\n/**\n * Assert that [[objects]] is an array of instances of type.\n * @param type The type to test to, a primitive type string returned by the typeof operator.\n * @param objects array to test.\n * @param name user-readable name of the array.\n */\nexport function assertValidArrayPrimitive(type: TypeOfReturnValues, objects: unknown, name: string) {\n\tassertType(Array, objects, name);\n\tlet i = 0;\n\tfor (const object of objects) {\n\t\tassert(typeof object === type, `${name}[${i}] is of type ${typeof object} instead of ${type}`);\n\t\ti += 1;\n\t}\n}\n\n/**\n* Assert that [[objects]] is an array of number.\n* @param objects array to test.\n* @param name user-readable name of the array.\n*/\nexport function assertValidArrayNumber(objects: unknown, name: string): asserts objects is number[] {\n\tassertValidArrayPrimitive('number', objects, name);\n}\n\n/**\n * Assert that [[objects]] is an array of string.\n * @param objects array to test.\n * @param name user-readable name of the array.\n */\nexport function assertValidArrayString(objects: unknown, name: string): asserts objects is string[] {\n\tassertValidArrayPrimitive('string', objects, name);\n}\n\n/**\n * Assert that [[object]] is a set of instances of [[type]] and their validate() functions do not throw.\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param objects set to test.\n * @param name user-readable name of the set.\n */\nexport function assertValidSetType(type: Constructor, objects: Set, name: string): asserts objects is Set {\n\tassertType(Set, objects, name);\n\tconst array = [...objects];\n\treturn assertValidArrayType(type, array, name);\n}\n\n/**\n * Assert that [[object]] is a set of instances of [[type]].\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param objects set to test.\n * @param name user-readable name of the set.\n */\nexport function assertSetType(type: Constructor, objects: Set, name: string): asserts objects is Set {\n\tassertType(Set, objects, name);\n\tconst array = [...objects];\n\treturn assertArrayType(type, array, name);\n}\n\n/**\n * Assert that [[objects]] is a set of instances of type.\n * @param type The type to test to, a primitive type string returned by the typeof operator.\n * @param objects set to test.\n * @param name user-readable name of the set.\n */\nexport function assertValidSetPrimitive(type: TypeOfReturnValues, objects: Set, name: string) {\n\tassertType(Set, objects, name);\n\tconst array = [...objects];\n\treturn assertValidArrayPrimitive(type, array, name);\n}\n\n/**\n *Assert that [[map]] is a Map of primitive types .\n * @param keyType The type to test to, a primitive type string returned by the typeof operator.\n * @param valueType The type to test to, a primitive type string returned by the typeof operator.\n * @param map map to test.\n * @param name user-readable name of the map.\n */\nexport function assertValidMapPrimitive(keyType: TypeOfReturnValues, valueType: TypeOfReturnValues, map: Map, name: string) {\n\tassertType(Map, map, name);\n\tfor (const [key, value] of map) {\n\t\tassert(typeof key === keyType, `Key ${name}[${key}] is of type ${typeof key} instead of ${keyType}`);\n\t\tassert(typeof value === valueType, `Value ${name}[${key}] is of type ${typeof value} instead of ${valueType}`);\n\t}\n}\n\n/**\n * Assert that [[value]] is null.\n * @param value value to test.\n * @param name user-readable name of the object.\n */\nexport function assertNull(value: T | null, name: string): asserts value is null {\n\tif (value === null) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not null`);\n}\n\n/**\n * Assert that [[value]] is non null.\n * @param value value to test.\n * @param name user-readable name of the object.\n */\nexport function assertNonNull(value: T | null, name: string): asserts value is T {\n\tif (value !== null) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is null`);\n}\n\n/**\n * Assert that [[value]] is undefined.\n * @param value value to test.\n * @param name user-readable name of the object.\n */\nexport function assertUndefined(value: T | undefined, name: string): asserts value is undefined {\n\tif (value === undefined) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not undefined`);\n}\n\n/**\n * Assert that [[value]] is not undefined.\n * @param value value to test.\n * @param name user-readable name of the object.\n */\nexport function assertDefined(value: T | undefined, name: string): asserts value is T {\n\tif (value !== undefined) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is undefined`);\n}\n\n/**\n * Assert that [[object]] is null or an instance of [[type]].\n * @param type The type to test to, this allows to pass abstract classes using [[Constructor]] type.\n * @param object object to test.\n * @param name user-readable name of the object.\n */\nexport function assertTypeOrNull(type: Constructor, object: T | null, name: string): asserts object is U | null {\n\tif (object === null || object instanceof type) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${object}) is non-null and not a ${type.name}`);\n}\n\n/**\n * Assert that [[value]] is a string.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertString(value: unknown, name: string): asserts value is string {\n\tif (typeof value === 'string') {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not a string`);\n}\n\n/**\n * Assert that [[value]] is a non-empty string.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertNonEmptyString(value: unknown, name: string): asserts value is string {\n\tassertString(value, name);\n\tif (value.length > 0) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is an empty string`);\n}\n\n/**\n * Assert that [[value]] is null or a string.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertStringOrNull(value: unknown, name: string): asserts value is string | null {\n\tif (value === null || typeof value === 'string') {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is non-null and not a string`);\n}\n\n/**\n * Assert that [[value]] is a valid boolean.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertBoolean(value: unknown, name: string): asserts value is boolean {\n\tif (typeof value === 'boolean') {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not a boolean`);\n}\n\n/**\n * Assert that [[value]] is a valid integer.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertInteger(value: unknown, name: string): asserts value is number {\n\tif (Number.isInteger(value)) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not an integer`);\n}\n\n/**\n * Assert that [[value]] is an integer within a given range.\n * @param value The value to test.\n * @param min inclusive lower-bound of the valid range.\n * @param max inclusive higher-bound of the valid range.\n * @param name user-readable name of the value.\n */\nexport function assertIntegerRange(value: unknown, min: number, max: number, name: string): asserts value is number {\n\tassertInteger(value, name);\n\tif (value >= min && value <= max) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) has value out of range [${min}:${max}]`);\n}\n\n/**\n * Assert that [[value]] is a valid and greater than or equal to zero.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertNonNegativeInteger(value: unknown, name: string): asserts value is number {\n\tassertInteger(value, name);\n\tif (value >= 0) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not a non-negative integer`);\n}\n\n/**\n * Assert that [[value]] is a valid and greater than zero.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertPositiveInteger(value: unknown, name: string): asserts value is number {\n\tassertInteger(value, name);\n\tif (value > 0) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not a positive integer`);\n}\n\n/**\n * Assert that [[value]] is a finite number.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertNumber(value: unknown, name: string): asserts value is number {\n\tif (Number.isFinite(value)) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not a finite number`);\n}\n\n/**\n * Assert that [[value]] is null or a finite number.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertNumberOrNull(value: unknown, name: string): asserts value is number | null {\n\tif (value === null || Number.isFinite(value)) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is non-null and not finite`);\n}\n\n/**\n * Assert that [[value]] is a finite positive number.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertPositiveNumber(value: unknown, name: string): asserts value is number {\n\tassertNumber(value, name);\n\tif (value >= 0) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not a positive number`);\n}\n\n/**\n * Assert that [[value]] is a finite strictly positive number.\n * @param value The value to test.\n * @param name user-readable name of the value.\n */\nexport function assertStrictlyPositiveNumber(value: unknown, name: string): asserts value is number {\n\tassertNumber(value, name);\n\tif (value > 0) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is not a strictly positive number`);\n}\n\n/**\n * Assert that [[value]] is real within a given range.\n * @param value The value to test.\n * @param min inclusive lower-bound of the valid range.\n * @param max inclusive higher-bound of the valid range.\n * @param name user-readable name of the value.\n */\nexport function assertNumberRange(value: unknown, min: number, max: number, name: string): asserts value is number {\n\tassertNumber(value, name);\n\tif (value >= min && value <= max) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) has value out of range [${min}:${max}]`);\n}\n\n/**\n * Assuming that value is an array of T, asserts that it has at least one element.\n * @param value The array to test\n * @param name user-readable name of the value\n */\nexport function assertNonEmptyArray(value: T[], name: string): asserts value is [T, ...T[]] {\n\tif (value.length > 0) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) is empty`);\n}\n\n/**\n * Assuming that value is an array of T, asserts that it has at least two elements.\n * @param value The array to test\n * @param name user-readable name of the value\n */\nexport function assertMinLength2Array(value: T[], name: string): asserts value is [T, T, ...T[]] {\n\tif (value.length > 1) {\n\t\treturn;\n\t}\n\tassertFalse(`${name} (${value}) has length (${value.length}) < 2`);\n}\n\n/**\n * Assert that [[lhs]] and [[rhs]] have same value, using operator !==.\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param ctx debug context\n * @param name optional name for the value, default none\n */\nexport function assertValueEqual(lhs: T, rhs: T, ctx: string, name = '') {\n\tif (lhs === rhs) {\n\t\treturn;\n\t}\n\tif (Array.isArray(lhs) && Array.isArray(rhs)) {\n\t\tassertArrayEqual(lhs, rhs, assertValueEqual, ctx);\n\t\treturn;\n\t}\n\tconst nameS = name !== '' ? name + ' ' : '';\n\tassertFalse(`${ctx}: different ${nameS}value between lhs (${lhs}) and rhs (${rhs})`);\n}\n\n/**\n * Assert that [[lhs]] and [[rhs]] have same value, using operator !==.\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param ctx debug context\n * @param f comparator function, should return true if values are equal\n * @param name optional name for the value, default none\n */\nexport function assertValueEqualF(lhs: T, rhs: T, ctx: string, f: (lhs: T, rhs: T) => boolean, name = '') {\n\tif (f(lhs, rhs)) {\n\t\treturn;\n\t}\n\tconst nameS = name !== '' ? name + ' ' : '';\n\tassertFalse(`${ctx}: different ${nameS}value between lhs (${lhs}) and rhs (${rhs})`);\n}\n\n/**\n * Assert that [[lhs]] and [[rhs]] have same actual type, using operator typeof.\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param ctx debug context\n * @param name optional name for the value, default none\n */\nexport function assertTypeEqual(lhs: T, rhs: T, ctx: string, name = '') {\n\tif (typeof lhs === typeof rhs) {\n\t\treturn;\n\t}\n\tconst nameS = name !== '' ? name + ' ' : '';\n\tassertFalse(`${ctx}: different ${nameS}types between lhs (${typeof lhs}, value: ${lhs}) and rhs (${typeof rhs}, value: ${rhs})`);\n}\n\n/**\n * Assert that [[lhs]] and [[rhs]] have same numeric value, up to tolerance\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param ctx debug context\n * @param name optional name for the value, default none\n * @param relTolerance optional numeric relative tolerance for equality comparison, default: 1e-6\n * @param absTolerance optional numeric absolute tolerance for equality comparison, default: 1e-10\n */\nexport function assertNumberEqual(lhs: number, rhs: number, ctx: string, name = '', relTolerance = 1e-6, absTolerance = 1e-10) {\n\tif (!approximateEqual(lhs, rhs, relTolerance, absTolerance)) {\n\t\tconst nameS = name !== '' ? name + ' ' : '';\n\t\tassertFalse(`${ctx}: different ${nameS}value between lhs (${lhs}) and rhs (${rhs}); diff. is larger than abs. tolerance ${absTolerance} or rel. tolerance ${relTolerance}`);\n\t}\n}\n\n/**\n * Assert that [[lhs]] and [[rhs]] have same actual class, not only superclass.\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param ctx debug context\n */\nexport function assertClassEqual(lhs: T, rhs: T, ctx: string) {\n\tconst lClass = lhs.constructor.name;\n\tconst rClass = rhs.constructor.name;\n\tif (lClass !== rClass) {\n\t\tassertFalse(`${ctx}: different between lhs (${lClass}) and rhs (${rClass})`);\n\t}\n}\n\n/** All keys of type V from T, or null */\nexport type EntriesOfTypeOrNull = {\n\t[K in keyof T]-?: T[K] extends V | null ? K : (T[K] extends V ? K : never);\n}[keyof T];\n\n/**\n * Assert that member [[key]] is the same in [[lhs]] and [[rhs]].\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param key key whose value to test\n * @param ctx debug context\n */\nexport function assertMemberEqual>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberEqualF(lhs, rhs, key, assertValueEqual, ctx);\n}\n\n/**\n * Assert that member [[key]] has same actual type, using operator typeof, in [[lhs]] and [[rhs]].\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key key whose type to test\n * @param ctx debug context\n */\nexport function assertMemberTypeEqual>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberEqualF(lhs, rhs, key, assertTypeEqual, ctx);\n}\n\n/**\n * Assert that member [[key]] has the same numeric value in [[lhs]] and [[rhs]].\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param key key whose value to test\n * @param ctx debug context\n */\nexport function assertMemberNumberEqual>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberEqualF(lhs, rhs, key, assertNumberEqual, ctx);\n}\n\n/**\n * Assert that member [[key]] is the same in [[lhs]] and [[rhs]].\n * @param lhs left-hand-side object in equality\n * @param rhs right-hand-side object in equality\n * @param key key whose value to test\n * @param f user-given function that asserts equality of member\n * @param ctx debug context\n */\nexport function assertMemberEqualF>\n(lhs: T, rhs: T, key: K, f: (lhs: V, rhs: V, ctx: string) => void, ctx: string) {\n\t// FIXME: SM: I did not manage to let Typescript accept this by itself\n\tconst lV = lhs[key] as unknown as V | null;\n\tconst rV = rhs[key] as unknown as V | null;\n\tif (lV === null && rV === null) {\n\t\treturn;\n\t}\n\tconst localCtx = `${ctx}.${String(key)}`;\n\tif (lV === null) {\n\t\tassertFalse(`${localCtx}: member is null in lhs but has value in rhs (${rV})`);\n\t}\n\tif (rV === null) {\n\t\tassertFalse(`${localCtx}: member has value in lhs (${lV}) but is null in rhs`);\n\t}\n\tf(lV, rV, localCtx);\n}\n\n/**\n * Assert that [[lArray]] and [[rArray]] have the same length and values.\n * @param lArray left-hand-side array\n * @param rArray right-hand-side array\n * @param f user-given function that asserts equality of members\n * @param ctx debug context\n */\nexport function assertArrayEqual(lArray: ArrayLike, rArray: ArrayLike, f: (lhs: V, rhs: V, ctx: string) => void, ctx: string) {\n\tif (lArray.length !== rArray.length) {\n\t\tassertFalse(`${ctx}.length: different element count between lhs (${lArray.length}) and rhs (${rArray.length})`);\n\t}\n\tfor (let i = 0; i < lArray.length; ++i) {\n\t\tconst elementCtx = `${ctx}[${i}]`;\n\t\tf(lArray[i]!, rArray[i]!, elementCtx);\n\t}\n}\n\n/**\n * Assert that [[lSet]] and [[rSet]] are equal.\n * First convert to arrays, sort them, and then compare them.\n * @param lSet left-hand-side set\n * @param rSet right-hand-side set\n * @param f user-given function that asserts equality of members\n * @param ctx debug context\n */\nexport function assertSetEqual(lSet: Set, rSet: Set, f: (lhs: V, rhs: V, ctx: string) => void, ctx: string) {\n\tconst lArray = [...lSet];\n\tlArray.sort();\n\tconst rArray = [...rSet];\n\trArray.sort();\n\treturn assertArrayEqual(lArray, rArray, f, ctx);\n}\n\n/**\n * Assert that [[lMap]] and [[rMap]] are equal.\n * First convert to arrays, sort them, and then compare them.\n * @param lMap left-hand-side map\n * @param rMap right-hand-side map\n * @param f user-given function that asserts equality of members\n * @param ctx debug context\n */\nexport function assertMapEqual(lMap: Map, rMap: Map, f: (lhs: [K, V], rhs: [K, V], ctx: string) => void, ctx: string) {\n\tconst lArray = [...lMap];\n\tlArray.sort();\n\tconst rArray = [...rMap];\n\trArray.sort();\n\treturn assertArrayEqual(lArray, rArray, f, ctx);\n}\n\n/**\n * Assert that array [[key]] are the same in [[lhs]] and [[rhs]].\n * The type of [[lhs]] and [[rhs]] are compared with `assertValueEqual(l, h, ctx)`.\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key array to be tested for equality\n * @param ctx debug context\n */\nexport function assertMemberArrayEqual>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberArrayEqualF(lhs, rhs, key, assertValueEqual, ctx);\n}\n\n/**\n * Assert that set [[key]] are the same in [[lhs]] and [[rhs]].\n * The type of [[lhs]] and [[rhs]] are compared with `assertValueEqual(l, h, ctx)`.\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key set to be tested for equality\n * @param ctx debug context\n */\nexport function assertMemberSetEqual>>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberSetEqualF(lhs, rhs, key, assertValueEqual, ctx);\n}\n\n/**\n * Assert that [[lhs]] and [[rhs]] have the same keys and values in [[key]].\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key map to be tested for equality\n * @param ctx debug context\n */\nexport function assertMemberMapEqual>>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberMapEqualF(lhs, rhs, key, assertValueEqual, ctx);\n}\n\n/**\n * Assert that array [[key]] are the same in [[lhs]] and [[rhs]], using user-provided [[comp]] function.\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key array to be tested for equality\n * @param f user-given function that asserts equality of members\n * @param ctx debug context\n */\nexport function assertMemberArrayEqualF>\n(lhs: T, rhs: T, key: K, f: (lhs: V, rhs: V, ctx: string) => void, ctx: string) {\n\tconst lArray = lhs[key] as unknown as V[] | null;\n\tconst rArray = rhs[key] as unknown as V[] | null;\n\tif (lArray === null && rArray === null) {\n\t\treturn;\n\t}\n\tif (lArray === null) {\n\t\tassertFalse(`${ctx}: member is null in lhs but has value in rhs (${rArray})`);\n\t}\n\tif (rArray === null) {\n\t\tassertFalse(`${ctx}: member has value in lhs (${lArray}) but is null in rhs`);\n\t}\n\tassertArrayEqual(lArray, rArray, f, `${ctx}.${String(key)}`);\n}\n\n/**\n * Assert that set [[key]] are the same in [[lhs]] and [[rhs]], using user-provided [[comp]] function.\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key set to be tested for equality\n * @param f user-given function that asserts equality of members\n * @param ctx debug context\n */\nexport function assertMemberSetEqualF>>\n(lhs: T, rhs: T, key: K, f: (lhs: V, rhs: V, ctx: string) => void, ctx: string) {\n\tconst lSet = lhs[key] as unknown as Set | null;\n\tconst rSet = rhs[key] as unknown as Set | null;\n\tif (lSet === null && rSet === null) {\n\t\treturn;\n\t}\n\tif (lSet === null) {\n\t\tassertFalse(`${ctx}: member is null in lhs but has value in rhs (${toStr(rSet)})`);\n\t}\n\tif (rSet === null) {\n\t\tassertFalse(`${ctx}: member has value in lhs (${toStr(lSet)}) but is null in rhs`);\n\t}\n\tassertSetEqual(lSet, rSet, f, `${ctx}.${String(key)}`);\n}\n\n/**\n * Assert that [[lhs]] and [[rhs]] have the same keys and values in [[key]].\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key map to be tested for equality\n * @param f user-given function that asserts equality of members\n * @param ctx debug context\n */\nexport function assertMemberMapEqualF>>\n(lhs: T, rhs: T, key: K, f: (lhs: [MK, MV], rhs: [MK, MV], ctx: string) => void, ctx: string) {\n\tconst lMap = lhs[key] as unknown as Map | null;\n\tconst rMap = rhs[key] as unknown as Map | null;\n\tif (lMap === null && rMap === null) {\n\t\treturn;\n\t}\n\tif (lMap === null) {\n\t\tassertFalse(`${ctx}: member is null in lhs but has value in rhs (${toStr(rMap)})`);\n\t}\n\tif (rMap === null) {\n\t\tassertFalse(`${ctx}: member has value in lhs (${toStr(lMap)}) but is null in rhs`);\n\t}\n\tassertMapEqual(lMap, rMap, f, `${ctx}.${String(key)}`);\n}\n\n/** Array that have an assertEqual member */\ninterface HasAssertEqual {\n\tassertEqual(that: HasAssertEqual, ctx: string): void;\n}\n\n/**\n * Assert that array [[key]] are the same in [[lhs]] and [[rhs]].\n * The type of [[lhs]] and [[rhs]] must have an `assertEqual(element, ctx)` member.\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key array to be tested for equality\n * @param ctx debug context\n */\nexport function assertMemberArrayEqualM>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberArrayEqualF(lhs, rhs, key, (l: V, r: V, c: string) => l.assertEqual(r, c), ctx);\n}\n\n/**\n * Assert that member [[key]] are the same in [[lhs]] and [[rhs]].\n * The type of [[lhs]] and [[rhs]] must have an `assertEqual(element, ctx)` member.\n * @param lhs left-hand-side object\n * @param rhs right-hand-side object\n * @param key member to be tested for equality\n * @param ctx debug context\n */\nexport function assertMemberEqualM>\n(lhs: T, rhs: T, key: K, ctx: string) {\n\tassertMemberEqualF(lhs, rhs, key, (l: V, r: V, c: string) => l.assertEqual(r, c), ctx);\n}\n\n\ntype AssertionListener = (msg: string, recoverable: boolean) => void;\n\nlet assertionListener: AssertionListener| null;\n\nexport function setAssertionListener(handler: AssertionListener) {\n\tassertionListener = handler;\n}\n","/**\n * @module utils\n */\n/** comment to work-around limitation of typedoc module plugin */\n\n// Copyright 2018-2024 Enlightware GmbH, Switzerland\n\n// cSpell: disable-next-line\n// eslint-disable-next-line camelcase\nimport { encode_png_base64, decodePngBase64 } from './image';\nimport { canvasToImageData, imageDataToCanvas } from './conversion';\nimport { dataURI } from './uri';\nimport { AnyCanvas } from 'imgui/imgui-types';\n\n// image data <-> string conversion\n\nexport function imageDataToString(imageData: ImageData) {\n\treturn encode_png_base64(imageData);\n}\n\nexport function imageDataToURIString(imageData: ImageData) {\n\treturn dataURI('image/png', imageDataToString(imageData));\n}\n\nexport function stringToImageData(data: string) {\n\treturn decodePngBase64(data);\n}\n\n// canvas <-> string conversion\n\nexport function canvasDataToString(canvas: AnyCanvas): string {\n\treturn imageDataToString(canvasToImageData(canvas));\n}\n\nexport function canvasDataToURIString(canvas: AnyCanvas): string {\n\treturn dataURI('image/png', canvasDataToString(canvas));\n}\n\nexport function stringToCanvasData(data: string) {\n\treturn imageDataToCanvas(stringToImageData(data));\n}\n\n// map serialization\n\nexport function serialiseMap(map: Map) {\n\treturn JSON.stringify([...map]);\n}\n\nexport function deserialiseMap(json: string) {\n\treturn new Map(JSON.parse(json) as [K, V][]);\n}\n"],"names":["Analytics","static","name","version","window","navigator","userAgent","getBrowser","calcBrowserVersionString","bowserParser","osName","getOSName","getOSVersion","getPlatformType","calcSystemString","process","release","logOnLocalhost","location","hostname","isUnderTest","analyticsEnabled","isLoggingDisabled","appInfo","appName","code","system","systemString","browser","rustc","buildFlags","reportLog","id","data","console","debug","report","reportWarning","warn","lastScreenReport","screenReportTimeout","reportScreenSize","clearInterval","setTimeout","doReportScreenSize","screenReport","dpr","devicePixelRatio","iw","innerWidth","ih","innerHeight","ow","outerWidth","oh","outerHeight","first","addEventListener","stringScreenReport","JSON","stringify","reportError","msg","error","reportErrorInternal","codeVersion","toString","stack","Error","ignoreErrors","getHeapSize","p","performance","memory","usedJSHeapSize","reportCounter","errorReportCounter","MaxErrorReportCount","MaxReportCount","kind","loggingDisabled","info","record","Date","now","devMode","toUpperCase","serverKind","blob","Blob","type","sendBeacon","disableErrorReporting","onerror","msgAny","sourceUrl","lineNo","columnNo","reason","message","fileName","lineNumber","columnNumber","recoverable","RememberMeKey","LoginManager","getInstance","this","instance","getLogin","loginString","localStorage","getItem","LoginKey","parse","setLogin","login","setItem","removeLogin","forgetAll","removeItem","rememberMe","backlogBlacklist","Set","isClosing","async","storeWithBacklog","key","dbSetter","backlog","forceBacklog","onLine","insert","add","remove","e","statusCode","undefined","delete","BrakeBlock","intensity","constructor","intensityOrFbBrake","BrakeMaxIntensity","lockState","super","validate","clone","summaryText","detailedTextIfConfigured","hl","Math","log","args","toFixed","serialise","builder","createBrake","fbActionType","Brake","getMiniatureKey","actionEffects","modifies","decodeFbGenericBlock","fbGenericBlock","game","blockType","genericBlockType","ReadVariable","genericBlock","BinaryArithmetic","RandomValue","ScriptGeneric","scriptCtx","decodeFbCondition","fbCondition","fromVersion","conditionType","inverted","AnyCondition","condition","Init","InputButton","Touch","Contact","C","objects","Timer","Comparison","GenericBlock_ReadVariable","AnimationIs","GenericBlock_GenericBlockContainer","ScriptCondition","block","movementLocked","parametersLocked","decodeFbAction","fbAction","actionType","AnyAction","action","AddVelocity","AddAcceleration","AddAngularVelocity","AddAngularAcceleration","Stop","Flip","Delete","ChangeVariable","Spawn","Teleport","ShowDialog","Vibrate","SetMusic","RestartTimer","PlaySound","PlayAnimation","ScriptAction","Asset","_key","rawKey","textKey","byteLength","CanvasWithKey","canvasOrImageData","toStored","imageData","canvas","ImageData","size","width","height","draw","ctx","x","y","drawImage","SoundWithKey","sound","getSamplesAndRate","connectToAudioNodeAndPlay","node","generateGameStringId","InMemoryStoredGame","binaryGame","sprites","sounds","scripts","toGame","buffer","fbGame","getRootAsGame","Game","index","get","_name","imageDecoder","load","regenerateId","setGameName","replaceWith","that","value","drawableToUUID","soundToUUID","keepLocking","imageIndices","collectImageIndices","imageKeysOffsets","drawable","push","createUUID","createValueVector","imageKeysOffset","createImageKeysVector","soundKeysOffsets","map","soundKeysOffset","createSoundKeysVector","childrenOffsets","serialiseChildren","startGame","serialiseSelf","addImageKeys","addSoundKeys","endGame","asUint8Array","gameIndex","finish","asUint8ArrayWithAssetUUID","Uint8Array","toInMemoryStored","Map","set","scriptPackages","save","saveGame","acceptLocking","getMiniature","getLevelMiniatureCanvas","level","fillStyle","surface","globalAlpha","fillRect","miniSize","some","objectType","angle","firstFrame","partFromPixelTransform","rotationAngle","sprite","miniatureCenter","camera","invScaleFactor","extent","maximum","worldFromX","scalingTranslation","position","miniatureFromX","translation","worldToMiniature","after","inverse","backgroundColorRgbHexString","twoPlayers","translate","path","Path2D","fill","restore","LayersAllEditable","Activity","HintBlockCategories","HintBlockOps","castHintBlockCategory","VariableScopeShift","VariableIndexMask","variableScope","address","variableIndex","variableScopeAndIndex","variableAddress","scope","validateAddress","currentDb","getDbPromise","openBacklogDB","upgrade","db","oldVersion","newVersion","_transaction","createObjectStore","openBacklogDBRetry","i","isDBWorking","close","tx","transaction","done","dbOperationWithErrorReporting","f","getDb","curDb","Backlog","store","objectStore","put","list","keyFilter","_","output","cursor","ks","empty","count","gamesBacklog","imagePngAssetsBacklog","audioWavAssetsBacklog","miniaturesBacklog","gameNamesBacklog","ImagePng","AudioWav","storageImpl","storage","setStorage","assertionListener","_devMode","setDevMode","AssertionError","assert","assertL","messageGenerator","assertRecoverable","trace","assertFalse","assertType","object","assertTypeAny","assertValidType","assertValidArrayType","Array","assertValidArrayPrimitive","assertValidArrayNumber","assertSetType","assertArrayType","assertValidSetPrimitive","assertValidMapPrimitive","keyType","valueType","assertNull","assertNonNull","assertTypeOrNull","assertString","assertStringOrNull","assertBoolean","assertInteger","Number","isInteger","assertIntegerRange","min","max","assertNonNegativeInteger","assertPositiveInteger","assertNumber","isFinite","assertNumberOrNull","assertPositiveNumber","assertStrictlyPositiveNumber","assertNumberRange","assertNonEmptyArray","length","assertValueEqual","lhs","rhs","isArray","assertArrayEqual","assertTypeEqual","assertNumberEqual","relTolerance","absTolerance","assertClassEqual","lClass","rClass","assertMemberEqual","assertMemberEqualF","assertMemberNumberEqual","lV","rV","localCtx","String","lArray","rArray","elementCtx","assertMemberSetEqual","lSet","rSet","sort","assertSetEqual","assertMemberSetEqualF","assertMemberMapEqual","lMap","rMap","assertMapEqual","assertMemberMapEqualF","assertMemberArrayEqualF","assertMemberArrayEqualM","l","r","c","assertEqual","assertMemberEqualM","setAssertionListener","handler","imageDataToString","imageDataToURIString","deserialiseMap","json"],"sourceRoot":""}