@underpostnet/cyberia 3.3.73 → 3.3.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +17 -5
- package/.github/workflows/coverall.cyberia.ci.yml +10 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
- package/.github/workflows/engine-cyberia.ci.yml +1 -1
- package/.github/workflows/publish.cyberia.ci.yml +44 -0
- package/AGENTS.md +61 -12
- package/CHANGELOG.md +159 -161
- package/CLI-HELP.md +20 -13
- package/Dockerfile +1 -1
- package/Dockerfile.dev +1 -1
- package/Dockerfile.test +2 -2
- package/README.md +28 -28
- package/bin/build.js +96 -21
- package/bin/cyberia.js +1247 -1373
- package/bin/index.js +1247 -1373
- package/compose.env +17 -5
- package/conf.js +7 -4
- package/deploy/cyberia-client/deploy.sh +10 -11
- package/deploy/cyberia-client/package.sh +0 -1
- package/deploy/cyberia-client/state.sh +3 -2
- package/deploy/cyberia-server/deploy.sh +10 -11
- package/deploy/cyberia-server/package.sh +0 -1
- package/deploy/cyberia-server/state.sh +3 -2
- package/deploy/dd-cyberia/deploy.sh +5 -5
- package/deploy/dd-cyberia/init.sh +15 -14
- package/deploy/dd-cyberia/package.sh +0 -1
- package/deploy/dd-cyberia/state.sh +2 -2
- package/deploy/dd-cyberia/sync-deploy.sh +139 -75
- package/deploy/lib/config.sh +29 -0
- package/deploy/lib/github-actions-logging.sh +92 -76
- package/deploy/lib/host.sh +28 -19
- package/deploy/lib/state.sh +4 -1
- package/deploy/pwa-microservices-template/deploy.sh +10 -8
- package/deploy/release/deploy.sh +0 -2
- package/deployment.yaml +1 -1
- package/docker-compose.yml +23 -17
- package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
- package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
- package/docs/coverage/cyberia/base.css +362 -0
- package/docs/coverage/cyberia/block-navigation.js +82 -0
- package/docs/coverage/cyberia/favicon.png +0 -0
- package/docs/coverage/cyberia/index.html +161 -0
- package/docs/coverage/cyberia/prettify.css +101 -0
- package/docs/coverage/cyberia/prettify.js +937 -0
- package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
- package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
- package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
- package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
- package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
- package/docs/coverage/cyberia/sorter.js +205 -0
- package/hardhat/package-lock.json +2 -2
- package/hardhat/package.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +46 -27
- package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
- package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
- package/manifests/deployment/mongo-express/service.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
- package/package.json +19 -17
- package/scripts/test-monitor.sh +3 -3
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
- package/src/api/cyberia-action/cyberia-action.router.js +2 -2
- package/src/api/cyberia-action/cyberia-action.service.js +3 -4
- package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
- package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
- package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
- package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
- package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
- package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
- package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
- package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
- package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
- package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
- package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
- package/src/api/cyberia-map/cyberia-map.service.js +6 -2
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
- package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
- package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
- package/src/api/file/file.ref.js +153 -0
- package/src/api/file/file.ref.json +11 -2
- package/src/api/object-layer/object-layer.model.js +47 -12
- package/src/api/object-layer/object-layer.router.js +10 -3
- package/src/api/object-layer/object-layer.service.js +113 -120
- package/src/cli/app.js +12 -1
- package/src/cli/client.js +2 -2
- package/src/cli/cluster.js +80 -18
- package/src/cli/deploy.js +215 -34
- package/src/cli/docker-compose.js +1 -1
- package/src/cli/fs-selection.js +115 -0
- package/src/cli/fs.js +158 -342
- package/src/cli/index.js +22 -10
- package/src/cli/release.js +3 -1
- package/src/cli/repository.js +35 -9
- package/src/cli/run.js +80 -199
- package/src/cli/test.js +4 -0
- package/src/cli/wireguard.js +32 -31
- package/src/client/components/core/Badge.js +0 -1
- package/src/client/components/core/CssCore.js +8 -1
- package/src/client/components/core/Docs.js +77 -100
- package/src/client/components/core/Modal.js +77 -23
- package/src/client/components/core/ToolTip.js +15 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
- package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
- package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
- package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
- package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
- package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -4
- package/src/client/components/itemledger/AppShellItemledger.js +0 -2
- package/src/client/components/underpost/AppShellUnderpost.js +1 -1
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
- package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
- package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
- package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
- package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -10
- package/src/client/services/object-layer/object-layer.service.js +3 -1
- package/src/client/ssr/views/Cyberia404.js +3 -3
- package/src/client-builder/client-build-docs.js +52 -52
- package/src/client-builder/client-build.js +67 -44
- package/src/client-builder/client-bundle.js +347 -0
- package/src/db/mongo/MongoExpress.js +187 -0
- package/src/grpc/cyberia/grpc-server.js +5 -7
- package/src/index.js +1 -1
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
- package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
- package/src/projects/cyberia/besu-genesis-generator.js +5 -14
- package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
- package/src/projects/cyberia/catalog-cyberia.js +3 -2
- package/src/projects/cyberia/gemini-client.js +3 -3
- package/src/projects/cyberia/generate-saga.js +4 -11
- package/src/projects/cyberia/hot-reload-trigger.js +17 -28
- package/src/projects/cyberia/instance-backup.js +210 -0
- package/src/projects/cyberia/instance-data.js +177 -257
- package/src/projects/cyberia/map-preview-generator.js +32 -68
- package/src/projects/cyberia/object-layer.js +152 -319
- package/src/projects/cyberia/seed-audio.js +139 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
- package/src/projects/cyberia/semantic-layer-generator.js +2 -18
- package/src/projects/cyberia/stat-balance.js +81 -0
- package/src/projects/cyberia/stat-commands.js +23 -0
- package/src/projects/cyberia/stat-contract-generator.js +101 -0
- package/src/runtime/cyberia-client/Dockerfile +8 -4
- package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
- package/src/runtime/cyberia-server/Dockerfile +9 -2
- package/src/runtime/cyberia-server/Dockerfile.dev +8 -1
- package/src/runtime/engine-cyberia/Dockerfile +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.test +2 -2
- package/src/runtime/engine-cyberia/compose.env +17 -5
- package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
- package/src/server/build/coverage.js +113 -42
- package/src/server/build/package.js +55 -12
- package/src/server/build/testing.js +59 -9
- package/src/server/network/middlewares.js +8 -2
- package/src/server/network/underpost-gateway.js +113 -14
- package/src/server/ops/logger.js +70 -35
- package/src/server/runtime/conf.js +40 -9
- package/src/server/runtime/start.js +11 -2
- package/src/server/storage/data-query.js +16 -0
- package/src/server/storage/downloader.js +62 -14
- package/src/server/storage/zip.js +153 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
- package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
- package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
- package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
- package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
- package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
- package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
- package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
- package/test/integration/app/cyberia/seed-audio.test.js +275 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
- package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
- package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
- package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
- package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
- package/test/unit/catalog.test.js +13 -2
- package/test/unit/client-build-docs.test.js +97 -70
- package/test/unit/client-bundle.test.js +313 -0
- package/test/unit/conf-resolution.test.js +58 -7
- package/test/unit/coverage-artifact.test.js +120 -29
- package/test/unit/cyberia/instance-backup.test.js +183 -0
- package/test/unit/cyberia/instance-data.test.js +314 -0
- package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
- package/test/unit/cyberia/publish-workflow.test.js +22 -0
- package/test/unit/cyberia/stat-balance.test.js +69 -0
- package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
- package/test/unit/deploy-log-table.test.js +76 -5
- package/test/unit/downloader-integrity.test.js +98 -0
- package/test/unit/file-reference-registry.test.js +141 -0
- package/test/unit/fs-storage-paths.test.js +596 -0
- package/test/unit/gateway-pod-fetch.test.js +95 -0
- package/test/unit/letsencrypt-issuer.test.js +101 -0
- package/test/unit/logger-redaction.test.js +29 -0
- package/test/unit/package.test.js +70 -16
- package/test/unit/prepare-host.test.js +84 -1
- package/test/unit/release-bump.test.js +3 -1
- package/test/unit/start-options.test.js +12 -0
- package/test/unit/test-tiers.test.js +27 -0
- package/test/unit/zip-archive.test.js +142 -0
- package/vitest.config.js +11 -3
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
* reads from code defaults INSTEAD of Mongo; a persisted instance reads
|
|
17
17
|
* them from Mongo, so they must exist there for the capture to serve the
|
|
18
18
|
* same world (see instance-data.js#fetchFullInstance).
|
|
19
|
+
* - CyberiaMapAudioConf — one per captured map. The client asks for audio by
|
|
20
|
+
* map code, so a namespaced capture would otherwise play nothing.
|
|
21
|
+
*
|
|
22
|
+
* CyberiaAudio assets are NOT captured: an asset is global, identified by its
|
|
23
|
+
* code, and imported once by `cyberia audio --import`. The capture reports the
|
|
24
|
+
* codes it bound that no asset carries yet.
|
|
19
25
|
*
|
|
20
26
|
* ObjectLayer/atlas assets are NOT generated here — they come from the asset
|
|
21
27
|
* pipeline (`bin/cyberia ol <ids> --import`). The capture reports the missing
|
|
@@ -31,6 +37,8 @@
|
|
|
31
37
|
|
|
32
38
|
import { loggerFactory } from '../../server/ops/logger.js';
|
|
33
39
|
import { collectReferencedItemIds } from './cyberia-fallback-world.js';
|
|
40
|
+
import { collectInstanceItemIds } from './cyberia-instance-items.js';
|
|
41
|
+
import { fallbackAudioConfig } from '../../projects/cyberia/seed-audio.js';
|
|
34
42
|
import {
|
|
35
43
|
DefaultCyberiaActions,
|
|
36
44
|
DefaultCyberiaQuests,
|
|
@@ -189,7 +197,6 @@ function planFallbackCapture({
|
|
|
189
197
|
...(spawn.sourceMapCode ? { sourceMapCode: mapCodes.get(spawn.sourceMapCode) || spawn.sourceMapCode } : {}),
|
|
190
198
|
},
|
|
191
199
|
topologyMode: 'procedural',
|
|
192
|
-
itemIds: (world.instance.itemIds || []).map((entry) => ({ ...entry })),
|
|
193
200
|
};
|
|
194
201
|
|
|
195
202
|
const conf = { ...fillInstanceConfDefaults(world.config || {}), instanceCode };
|
|
@@ -219,57 +226,24 @@ function planFallbackCapture({
|
|
|
219
226
|
codeMaps,
|
|
220
227
|
skippedActionCodes,
|
|
221
228
|
skippedQuestCodes,
|
|
222
|
-
itemIds: collectCaptureItemIds({
|
|
229
|
+
itemIds: collectCaptureItemIds({ maps, actions: capturedActions, quests: capturedQuests }),
|
|
223
230
|
};
|
|
224
231
|
}
|
|
225
232
|
|
|
226
233
|
/**
|
|
227
|
-
* Every ObjectLayer item id the captured instance needs an atlas for:
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* draw icons for.
|
|
234
|
+
* Every ObjectLayer item id the captured instance needs an atlas for: the canonical world's
|
|
235
|
+
* own ids, plus what this instance names — its maps, its entity-type defaults, and the
|
|
236
|
+
* vendor/assembler/quest catalogs the interact modal draws icons for.
|
|
231
237
|
*
|
|
232
238
|
* @returns {string[]} Sorted, de-duplicated item ids.
|
|
233
239
|
*/
|
|
234
|
-
function collectCaptureItemIds({
|
|
240
|
+
function collectCaptureItemIds({ maps, actions, quests, entityDefaults = [] }) {
|
|
241
|
+
// The canonical world's own ids — every fallback default, including the skill triggers and
|
|
242
|
+
// summoned entities DefaultSkillConfig names. What the captured instance adds on top follows
|
|
243
|
+
// the same rule the export and the boot payload use.
|
|
235
244
|
const ids = collectReferencedItemIds();
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
for (const map of maps || []) {
|
|
241
|
-
for (const entity of map.entities || []) (entity.objectLayerItemIds || []).forEach(push);
|
|
242
|
-
}
|
|
243
|
-
for (const entry of instance?.itemIds || []) push(typeof entry === 'string' ? entry : entry?.id);
|
|
244
|
-
|
|
245
|
-
for (const entityDefault of conf?.entityDefaults || []) {
|
|
246
|
-
(entityDefault.liveItemIds || []).forEach(push);
|
|
247
|
-
(entityDefault.deadItemIds || []).forEach(push);
|
|
248
|
-
(entityDefault.dropItemIds || []).forEach(push);
|
|
249
|
-
for (const slot of entityDefault.defaultObjectLayers || []) push(slot.itemId);
|
|
250
|
-
}
|
|
251
|
-
for (const skillConfig of conf?.skillConfig || []) {
|
|
252
|
-
push(skillConfig.triggerItemId);
|
|
253
|
-
for (const skill of skillConfig.skills || []) push(skill.summonedEntityItemId);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
for (const action of actions || []) {
|
|
257
|
-
for (const shopItem of action.shopItems || []) {
|
|
258
|
-
push(shopItem.itemId);
|
|
259
|
-
push(shopItem.priceItemId);
|
|
260
|
-
}
|
|
261
|
-
for (const recipe of action.craftRecipes || []) {
|
|
262
|
-
(recipe.ingredients || []).forEach((ingredient) => push(ingredient.itemId));
|
|
263
|
-
(recipe.outputItems || []).forEach((output) => push(output.itemId));
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
for (const quest of quests || []) {
|
|
267
|
-
for (const step of quest.steps || []) {
|
|
268
|
-
for (const objective of step.objectives || []) push(objective.itemId);
|
|
269
|
-
}
|
|
270
|
-
for (const reward of quest.rewards || []) push(reward.itemId);
|
|
271
|
-
}
|
|
272
|
-
|
|
245
|
+
// The conf references its entity-type defaults by _id, so the documents arrive resolved.
|
|
246
|
+
for (const itemId of collectInstanceItemIds({ maps, entityDefaults, actions, quests })) ids.add(itemId);
|
|
273
247
|
return [...ids].sort();
|
|
274
248
|
}
|
|
275
249
|
|
|
@@ -309,7 +283,8 @@ async function seedMissingContentDefaults({ CyberiaSkill, CyberiaEntityTypeDefau
|
|
|
309
283
|
liveItemIds: entityDefault.liveItemIds || [],
|
|
310
284
|
deadItemIds: entityDefault.deadItemIds || [],
|
|
311
285
|
dropItemIds: entityDefault.dropItemIds || [],
|
|
312
|
-
|
|
286
|
+
inventoryItemsIds: entityDefault.inventoryItemsIds || [],
|
|
287
|
+
overrideItemsIdsState: entityDefault.overrideItemsIdsState || [],
|
|
313
288
|
behavior: entityDefault.behavior || '',
|
|
314
289
|
},
|
|
315
290
|
'entityTypeDefaults',
|
|
@@ -329,10 +304,59 @@ async function seedMissingContentDefaults({ CyberiaSkill, CyberiaEntityTypeDefau
|
|
|
329
304
|
}
|
|
330
305
|
|
|
331
306
|
/**
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
307
|
+
* Write the audio configuration of the captured maps.
|
|
308
|
+
*
|
|
309
|
+
* Audio reaches the client per map code, so a world captured under namespaced codes needs its
|
|
310
|
+
* bindings rewritten under those codes — the same reason the actions and quests are re-coded.
|
|
311
|
+
* Only codes an imported asset actually carries are bound: a binding to an absent asset would be
|
|
312
|
+
* a dangling name, and the caller is told which ones are missing instead.
|
|
313
|
+
*
|
|
314
|
+
* @param {object} params
|
|
315
|
+
* @param {object} params.models - `{ CyberiaMapAudioConf, CyberiaAudio }`.
|
|
316
|
+
* @param {object} params.plan - Result of `planFallbackCapture()`.
|
|
317
|
+
* @returns {Promise<{audioConfs: number, missingAudioCodes: string[]}>}
|
|
318
|
+
*/
|
|
319
|
+
async function captureAudioConfig({ models, plan }) {
|
|
320
|
+
const { CyberiaMapAudioConf, CyberiaAudio } = models;
|
|
321
|
+
if (!CyberiaMapAudioConf) return { audioConfs: 0, missingAudioCodes: [] };
|
|
322
|
+
|
|
323
|
+
const config = fallbackAudioConfig();
|
|
324
|
+
const bound = new Set(
|
|
325
|
+
[...config.events.map(({ audioCode }) => audioCode), ...config.maps.map(({ defaultMusic }) => defaultMusic)].filter(
|
|
326
|
+
Boolean,
|
|
327
|
+
),
|
|
328
|
+
);
|
|
329
|
+
const present = new Set(
|
|
330
|
+
CyberiaAudio
|
|
331
|
+
? (await CyberiaAudio.find({ code: { $in: [...bound] } }, { code: 1 }).lean()).map((doc) => doc.code)
|
|
332
|
+
: [],
|
|
333
|
+
);
|
|
334
|
+
const events = config.events.filter(({ audioCode }) => present.has(audioCode));
|
|
335
|
+
|
|
336
|
+
let audioConfs = 0;
|
|
337
|
+
for (const map of config.maps) {
|
|
338
|
+
const mapCode = plan.codeMaps.mapCodes.get(map.mapCode);
|
|
339
|
+
if (!mapCode) continue;
|
|
340
|
+
await CyberiaMapAudioConf.findOneAndUpdate(
|
|
341
|
+
{ mapCode },
|
|
342
|
+
{
|
|
343
|
+
$set: {
|
|
344
|
+
defaultMusic: present.has(map.defaultMusic) ? map.defaultMusic : '',
|
|
345
|
+
settings: config.settings,
|
|
346
|
+
events,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
{ upsert: true },
|
|
350
|
+
);
|
|
351
|
+
audioConfs++;
|
|
352
|
+
}
|
|
353
|
+
return { audioConfs, missingAudioCodes: [...bound].filter((code) => !present.has(code)).sort() };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Remove documents that a larger previous capture left in this capture's own
|
|
358
|
+
* namespace. Touches only codes prefixed with the instance code, and never runs
|
|
359
|
+
* in `keepFallbackCodes` mode, where the codes are the shared canonical ones.
|
|
336
360
|
*/
|
|
337
361
|
async function pruneStaleCaptureDocs({ models, instanceCode, plan }) {
|
|
338
362
|
const namespace = new RegExp(`^${escapeRegExp(instanceCode)}-`);
|
|
@@ -373,7 +397,8 @@ async function pruneStaleCaptureDocs({ models, instanceCode, plan }) {
|
|
|
373
397
|
* @param {object} params
|
|
374
398
|
* @param {object} params.models `{ CyberiaInstance, CyberiaInstanceConf, CyberiaMap, CyberiaAction,
|
|
375
399
|
* CyberiaQuest, CyberiaSkill, CyberiaEntityTypeDefault,
|
|
376
|
-
* CyberiaDialogue,
|
|
400
|
+
* CyberiaDialogue, CyberiaMapAudioConf, CyberiaAudio,
|
|
401
|
+
* ObjectLayer }`
|
|
377
402
|
* @param {object} params.world Result of `generateFallbackWorld()`.
|
|
378
403
|
* @param {string} params.instanceCode
|
|
379
404
|
* @param {boolean} [params.keepFallbackCodes=false]
|
|
@@ -437,6 +462,16 @@ async function captureFallbackWorld({ models, world, instanceCode, keepFallbackC
|
|
|
437
462
|
const inserted = await seedMissingContentDefaults(models);
|
|
438
463
|
logger.info('Seeded missing content defaults', inserted);
|
|
439
464
|
|
|
465
|
+
const audio = await captureAudioConfig({ models, plan });
|
|
466
|
+
logger.info(`Captured ${audio.audioConfs} CyberiaMapAudioConf document(s)`);
|
|
467
|
+
if (audio.missingAudioCodes.length > 0) {
|
|
468
|
+
logger.warn(
|
|
469
|
+
'Audio codes with no imported CyberiaAudio document were left unbound — ' +
|
|
470
|
+
'run `node bin/cyberia run-workflow seed-audio` to record and import them',
|
|
471
|
+
{ codes: audio.missingAudioCodes },
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
440
475
|
// `keepFallbackCodes` reuses the globally shared canonical codes, so nothing
|
|
441
476
|
// in that namespace belongs exclusively to this capture.
|
|
442
477
|
const pruned = keepFallbackCodes
|
|
@@ -452,11 +487,12 @@ async function captureFallbackWorld({ models, world, instanceCode, keepFallbackC
|
|
|
452
487
|
);
|
|
453
488
|
const missingObjectLayerItemIds = plan.itemIds.filter((id) => !presentItemIds.has(id));
|
|
454
489
|
|
|
455
|
-
return { plan, inserted, pruned, missingObjectLayerItemIds };
|
|
490
|
+
return { plan, inserted, pruned, audio, missingObjectLayerItemIds };
|
|
456
491
|
}
|
|
457
492
|
|
|
458
493
|
export {
|
|
459
494
|
planFallbackCapture,
|
|
495
|
+
captureAudioConfig,
|
|
460
496
|
captureFallbackWorld,
|
|
461
497
|
collectCaptureItemIds,
|
|
462
498
|
captureMapCode,
|
|
@@ -53,16 +53,16 @@ import {
|
|
|
53
53
|
const DEFAULT_MAP_COUNT = 4;
|
|
54
54
|
const DEFAULT_GRID_SIZE = 64;
|
|
55
55
|
|
|
56
|
+
const getFallbackMapCodes = (mapCount = DEFAULT_MAP_COUNT) =>
|
|
57
|
+
Array.from({ length: mapCount }, (_, index) => `fallback-map-${index}`);
|
|
58
|
+
|
|
56
59
|
// ── Asset-id integrity ──────────────────────────────────────────────────────
|
|
57
60
|
//
|
|
58
|
-
// Every item id
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// back to a solid-colour rectangle instead of the intended sprite.
|
|
61
|
+
// Every item id the fallback world can place must also be in
|
|
62
|
+
// DefaultCyberiaItems. Otherwise the `import-default-items` seed creates no
|
|
63
|
+
// ObjectLayer for it and the client draws a solid-colour rectangle.
|
|
62
64
|
//
|
|
63
|
-
// auditFallbackItemIds() returns the
|
|
64
|
-
// canonical defaults so callers (CLI, test harness, gRPC fallback path)
|
|
65
|
-
// can surface drift immediately. Returns an empty array when in sync.
|
|
65
|
+
// auditFallbackItemIds() returns the unknown ids, empty when in sync.
|
|
66
66
|
|
|
67
67
|
const KNOWN_DEFAULT_ITEM_IDS = new Set(DefaultCyberiaItems.map((e) => e.item.id));
|
|
68
68
|
|
|
@@ -83,7 +83,7 @@ function collectReferencedItemIds() {
|
|
|
83
83
|
(e.liveItemIds || []).forEach(push);
|
|
84
84
|
(e.deadItemIds || []).forEach(push);
|
|
85
85
|
(e.dropItemIds || []).forEach(push);
|
|
86
|
-
(e.
|
|
86
|
+
(e.inventoryItemsIds || []).forEach(push);
|
|
87
87
|
}
|
|
88
88
|
for (const r of RESOURCE_ENTITY_TYPE_DEFAULTS) {
|
|
89
89
|
(r.liveItemIds || []).forEach(push);
|
|
@@ -239,11 +239,6 @@ function generateFallbackMap(mapCode, colors, opts = {}) {
|
|
|
239
239
|
* @param {number} [opts.resourceCount] Resources per map (random if omitted).
|
|
240
240
|
* @param {number} [opts.staticCount] Static decorators per map (random if omitted).
|
|
241
241
|
* @param {Array} [opts.colors] Override palette.
|
|
242
|
-
* @param {Array<{id: string, defaultPlayerInventory: boolean}>} [opts.itemIds=[]]
|
|
243
|
-
* Instance-level default items, mirroring `CyberiaInstance.itemIds`. Entries
|
|
244
|
-
* flagged `defaultPlayerInventory` are merged into the player entity default
|
|
245
|
-
* by `applyInstanceDefaultPlayerInventory`; every id is also resolved to an
|
|
246
|
-
* atlas at boot. Layout is unaffected, so this stays outside the world seed.
|
|
247
242
|
* @returns {{
|
|
248
243
|
* instance: object,
|
|
249
244
|
* maps: object[],
|
|
@@ -262,18 +257,13 @@ function generateFallbackWorld(opts = {}) {
|
|
|
262
257
|
botCount,
|
|
263
258
|
resourceCount,
|
|
264
259
|
staticCount,
|
|
265
|
-
// Palette
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
// fallback before atlases load. The C client resolves real colours
|
|
269
|
-
// through domain/presentation_runtime — it does not read this value.
|
|
260
|
+
// Palette from SharedDefaultsCyberia. The generator only stamps a cosmetic
|
|
261
|
+
// rgba(...) string on entities, for previews drawn before atlases load. The
|
|
262
|
+
// game client resolves its own colours and ignores this value.
|
|
270
263
|
colors = PALETTE,
|
|
271
|
-
itemIds = [],
|
|
272
264
|
} = opts;
|
|
273
265
|
|
|
274
|
-
//
|
|
275
|
-
// `bin/cyberia run-workflow import-default-items` run shows up at startup
|
|
276
|
-
// instead of as silent grey rectangles later.
|
|
266
|
+
// Report item-id drift at the first build, not as grey rectangles later.
|
|
277
267
|
const missing = auditFallbackItemIds();
|
|
278
268
|
if (missing.length > 0) {
|
|
279
269
|
// eslint-disable-next-line no-console
|
|
@@ -284,18 +274,12 @@ function generateFallbackWorld(opts = {}) {
|
|
|
284
274
|
);
|
|
285
275
|
}
|
|
286
276
|
|
|
287
|
-
|
|
288
|
-
const mapCodes = [];
|
|
289
|
-
for (let i = 0; i < mapCount; i++) {
|
|
290
|
-
mapCodes.push(`fallback-map-${i}`);
|
|
291
|
-
}
|
|
277
|
+
const mapCodes = getFallbackMapCodes(mapCount);
|
|
292
278
|
|
|
293
|
-
// Deterministic layout:
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
// lands exactly where the preview draws that resource. The seed folds in the
|
|
298
|
-
// layout-affecting options so a custom count set stays internally consistent.
|
|
279
|
+
// Deterministic layout: the seeded random source makes every call reproduce
|
|
280
|
+
// the same world. The `/static` POIs and the `/preview` image come from
|
|
281
|
+
// independent requests and must agree on where each entity sits. The seed
|
|
282
|
+
// folds in the layout options, so a custom count set stays consistent.
|
|
299
283
|
const seed = `cyberia-fallback-world-v1:${mapCount}:${gridSize ?? ''}:${obstacleCount ?? ''}:${
|
|
300
284
|
foregroundCount ?? ''
|
|
301
285
|
}:${botCount ?? ''}:${resourceCount ?? ''}:${staticCount ?? ''}`;
|
|
@@ -327,7 +311,6 @@ function generateFallbackWorld(opts = {}) {
|
|
|
327
311
|
portals,
|
|
328
312
|
topologyMode: 'procedural',
|
|
329
313
|
playerSpawn: { ...DEFAULT_PLAYER_SPAWN },
|
|
330
|
-
itemIds: itemIds.map((entry) => ({ ...entry })),
|
|
331
314
|
};
|
|
332
315
|
|
|
333
316
|
return {
|
|
@@ -343,12 +326,9 @@ function generateFallbackWorld(opts = {}) {
|
|
|
343
326
|
|
|
344
327
|
// ── Selection-view fallback instance ─────────────────────────────────────────
|
|
345
328
|
//
|
|
346
|
-
// A
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
// CyberiaInstance list row — only the fields InstanceSelectionView reads
|
|
350
|
-
// (code/name/description/tags/status/thumbnail). Injected into the instance list
|
|
351
|
-
// return by CyberiaInstanceService.get when the caller opts in (?fallback=true).
|
|
329
|
+
// A never-persisted instance row that keeps the selection list non-empty. It
|
|
330
|
+
// points at the always-on TEST variant and carries only the fields the
|
|
331
|
+
// selection view reads. CyberiaInstanceService.get injects it on ?fallback=true.
|
|
352
332
|
|
|
353
333
|
const DEFAULT_FALLBACK_INSTANCE_CODE = 'TEST';
|
|
354
334
|
|
|
@@ -370,6 +350,7 @@ function fallbackListInstance() {
|
|
|
370
350
|
// ── Public API ───────────────────────────────────────────────────────────────
|
|
371
351
|
|
|
372
352
|
export {
|
|
353
|
+
getFallbackMapCodes,
|
|
373
354
|
generateFallbackWorld,
|
|
374
355
|
generateFallbackMap,
|
|
375
356
|
auditFallbackItemIds,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* REST boot service — the /api/cyberia-instance/boot/* fallback transport.
|
|
3
3
|
*
|
|
4
|
-
* Serves the
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* src/grpc/cyberia/grpc-server.js so both transports stay equivalent.
|
|
4
|
+
* Serves the CyberiaDataService payloads (world load, hot reload) over REST, so
|
|
5
|
+
* the game server can boot from its --data-server-url when the engine gRPC
|
|
6
|
+
* server is off for the deploy. The data assembly lives in
|
|
7
|
+
* src/projects/cyberia/instance-data.js, shared with
|
|
8
|
+
* src/grpc/cyberia/grpc-server.js, so both transports stay equivalent.
|
|
9
9
|
*
|
|
10
10
|
* Endpoints (all GET, internal server-to-server, mirroring the RPCs):
|
|
11
11
|
* /boot/ping → { serverTimeMs }
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What content an instance owns, and which skills follow from it.
|
|
3
|
+
*
|
|
4
|
+
* An instance is a set of maps plus the content bound to them. Everything else it needs —
|
|
5
|
+
* atlases to draw, skills to fire — follows from the item ids that content names. Deriving
|
|
6
|
+
* that set in one place is what keeps the export, the capture, the boot payload and the
|
|
7
|
+
* editor's report agreeing: they used to each answer "what belongs to this world" slightly
|
|
8
|
+
* differently, and a skill would export but not boot, or boot but not export.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** A '$'-prefixed id is a runtime placeholder the simulation resolves, never a document. */
|
|
12
|
+
const isMaterialItemId = (itemId) => typeof itemId === 'string' && itemId.length > 0 && !itemId.startsWith('$');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Every ObjectLayer item id an instance's own content names.
|
|
16
|
+
*
|
|
17
|
+
* Four sources, and they are the whole list: what its maps place, what its entity-type
|
|
18
|
+
* defaults wire, what its vendors and assemblers trade, and what its quests ask for or pay
|
|
19
|
+
* out. A skill trigger is not a source — skills are selected from this set, not folded into
|
|
20
|
+
* it — and neither is another instance's content.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} content
|
|
23
|
+
* @param {Array<{entities?: Array<{objectLayerItemIds?: string[]}>}>} [content.maps]
|
|
24
|
+
* @param {Array<object>} [content.entityDefaults] - Resolved documents, not references.
|
|
25
|
+
* @param {Array<object>} [content.actions] - Vendor / assembler catalogs.
|
|
26
|
+
* @param {Array<object>} [content.quests]
|
|
27
|
+
* @returns {Set<string>} Item ids, placeholders excluded.
|
|
28
|
+
*/
|
|
29
|
+
function collectInstanceItemIds({ maps = [], entityDefaults = [], actions = [], quests = [] } = {}) {
|
|
30
|
+
const itemIds = new Set();
|
|
31
|
+
const push = (itemId) => {
|
|
32
|
+
if (isMaterialItemId(itemId)) itemIds.add(itemId);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
for (const map of maps) {
|
|
36
|
+
for (const entity of map?.entities || []) (entity?.objectLayerItemIds || []).forEach(push);
|
|
37
|
+
}
|
|
38
|
+
for (const entityDefault of entityDefaults) {
|
|
39
|
+
for (const list of ['liveItemIds', 'deadItemIds', 'dropItemIds', 'inventoryItemsIds']) {
|
|
40
|
+
(entityDefault?.[list] || []).forEach(push);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const action of actions) {
|
|
44
|
+
for (const shopItem of action?.shopItems || []) {
|
|
45
|
+
push(shopItem?.itemId);
|
|
46
|
+
push(shopItem?.priceItemId);
|
|
47
|
+
}
|
|
48
|
+
for (const recipe of action?.craftRecipes || []) {
|
|
49
|
+
(recipe?.ingredients || []).forEach((ingredient) => push(ingredient?.itemId));
|
|
50
|
+
(recipe?.outputItems || []).forEach((output) => push(output?.itemId));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const quest of quests) {
|
|
54
|
+
for (const step of quest?.steps || []) {
|
|
55
|
+
for (const objective of step?.objectives || []) push(objective?.itemId);
|
|
56
|
+
}
|
|
57
|
+
for (const reward of quest?.rewards || []) push(reward?.itemId);
|
|
58
|
+
}
|
|
59
|
+
return itemIds;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The skills an instance runs: one per trigger item its content names.
|
|
64
|
+
*
|
|
65
|
+
* This is the whole membership rule, and it is why `hatchet` belongs to a world whose maps
|
|
66
|
+
* place no hatchet — a quest objective or a vendor's shelf names it, so an entity there can
|
|
67
|
+
* come to hold one and fire it. A skill for a trigger nothing in the world names is
|
|
68
|
+
* unreachable and is left out.
|
|
69
|
+
*
|
|
70
|
+
* @param {Array<{triggerItemId?: string}>} skills - Candidate CyberiaSkill documents.
|
|
71
|
+
* @param {Set<string>|string[]} itemIds - {@link collectInstanceItemIds} output.
|
|
72
|
+
* @returns {Array<object>} The subset that belongs, in the order given.
|
|
73
|
+
*/
|
|
74
|
+
function selectInstanceSkills(skills = [], itemIds = new Set()) {
|
|
75
|
+
const owned = itemIds instanceof Set ? itemIds : new Set(itemIds);
|
|
76
|
+
return skills.filter((skill) => skill?.triggerItemId && owned.has(skill.triggerItemId));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Item ids the given skills summon, which need atlases of their own.
|
|
81
|
+
*
|
|
82
|
+
* Folded back into the instance's item set after selection, never before: a summoned bullet
|
|
83
|
+
* is drawn by the world, but it does not make the world own a skill that summons it.
|
|
84
|
+
*
|
|
85
|
+
* @param {Array<{skills?: Array<{summonedEntityItemId?: string}>}>} skills
|
|
86
|
+
* @returns {Set<string>}
|
|
87
|
+
*/
|
|
88
|
+
function collectSummonedItemIds(skills = []) {
|
|
89
|
+
const itemIds = new Set();
|
|
90
|
+
for (const skill of skills) {
|
|
91
|
+
for (const definition of skill?.skills || []) {
|
|
92
|
+
if (isMaterialItemId(definition?.summonedEntityItemId)) itemIds.add(definition.summonedEntityItemId);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return itemIds;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { collectInstanceItemIds, collectSummonedItemIds, isMaterialItemId, selectInstanceSkills };
|