@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
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
|
|
2
2
|
import { loggerFactory } from '../../server/ops/logger.js';
|
|
3
3
|
import { DataQuery } from '../../server/storage/data-query.js';
|
|
4
|
-
import {
|
|
5
|
-
import { FileFactory } from '../file/file.service.js';
|
|
4
|
+
import { FileCleanup } from '../file/file.service.js';
|
|
6
5
|
import { AtlasSpriteSheetDto } from './atlas-sprite-sheet.model.js';
|
|
7
6
|
import { IpfsClient } from '../../projects/cyberia/ipfs-client.js';
|
|
8
|
-
import {
|
|
7
|
+
import { removePinRecordsAndUnpin } from '../ipfs/ipfs.service.js';
|
|
8
|
+
import {
|
|
9
|
+
ATLAS_FILE_FIELDS,
|
|
10
|
+
AtlasSpriteSheetStore,
|
|
11
|
+
atlasMfsPaths,
|
|
12
|
+
} from '../../projects/cyberia/atlas-sprite-sheet-store.js';
|
|
9
13
|
|
|
10
14
|
const logger = loggerFactory(import.meta);
|
|
11
15
|
const DEFAULT_ATLAS_FRAME_DURATION = 100;
|
|
@@ -51,133 +55,91 @@ async function withResolvedAtlasFrameDuration(doc, ObjectLayer) {
|
|
|
51
55
|
};
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Removes one atlas: its IPFS pins and MFS entries, both render File documents,
|
|
60
|
+
* and the AtlasSpriteSheet document itself.
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} params
|
|
63
|
+
* @param {Object} params.atlasDoc - The AtlasSpriteSheet document.
|
|
64
|
+
* @param {Object} params.options - Router options ({ host, path }).
|
|
65
|
+
* @param {string} [params.itemKey] - Item key, when the caller already resolved it.
|
|
66
|
+
* @param {string} [params.metadataCid] - Atlas metadata CID, when the object layer holds it.
|
|
67
|
+
* @returns {Promise<void>}
|
|
68
|
+
*/
|
|
69
|
+
const purgeAtlasDoc = async ({ atlasDoc, options, itemKey, metadataCid }) => {
|
|
70
|
+
const AtlasSpriteSheet = DataBaseProviderService.getModel('AtlasSpriteSheet', options);
|
|
71
|
+
const File = DataBaseProviderService.getModel('File', options);
|
|
72
|
+
const key = itemKey || atlasDoc.metadata?.itemKey;
|
|
73
|
+
const mfsPaths = key ? atlasMfsPaths(key) : null;
|
|
74
|
+
|
|
75
|
+
for (const [cid, mfsPath] of [
|
|
76
|
+
[atlasDoc.cid, mfsPaths?.png],
|
|
77
|
+
[metadataCid, mfsPaths?.metadata],
|
|
78
|
+
]) {
|
|
79
|
+
if (!cid) continue;
|
|
80
|
+
try {
|
|
81
|
+
await removePinRecordsAndUnpin(cid, options);
|
|
82
|
+
if (mfsPath) await IpfsClient.removeMfsPath(mfsPath);
|
|
83
|
+
logger.info(`Cleaned up IPFS CID ${cid} for AtlasSpriteSheet ${atlasDoc._id}`);
|
|
84
|
+
} catch (ipfsErr) {
|
|
85
|
+
logger.warn(`Failed to clean up IPFS CID ${cid}: ${ipfsErr.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await FileCleanup.deleteDocumentFiles({ doc: atlasDoc, fileFields: ATLAS_FILE_FIELDS, File });
|
|
90
|
+
await AtlasSpriteSheet.findByIdAndDelete(atlasDoc._id);
|
|
91
|
+
};
|
|
92
|
+
|
|
54
93
|
class AtlasSpriteSheetService {
|
|
55
|
-
|
|
94
|
+
// Serves the minified render, the one `metadata` describes. The client runtime
|
|
95
|
+
// pairs this blob with GET /metadata/:itemKey, so the two must be the same layout.
|
|
96
|
+
/* One render of an item's atlas, by the File field that holds it. */
|
|
97
|
+
static render = async ({ itemKey, field, label }, options) => {
|
|
56
98
|
/** @type {import('./atlas-sprite-sheet.model.js').AtlasSpriteSheetModel} */
|
|
57
99
|
const AtlasSpriteSheet = DataBaseProviderService.getModel('AtlasSpriteSheet', options);
|
|
58
100
|
/** @type {import('../file/file.model.js').FileModel} */
|
|
59
101
|
const File = DataBaseProviderService.getModel('File', options);
|
|
60
102
|
|
|
61
|
-
const itemKey = req.params.itemKey;
|
|
62
103
|
const atlasDoc = await AtlasSpriteSheet.findOne({ 'metadata.itemKey': itemKey }).lean();
|
|
63
104
|
if (!atlasDoc) throw new Error(`Atlas not found for itemKey: ${itemKey}`);
|
|
105
|
+
if (!atlasDoc[field]) throw new Error(`${label} missing for itemKey: ${itemKey}`);
|
|
64
106
|
|
|
65
|
-
const fileDoc = await File.findById(atlasDoc
|
|
107
|
+
const fileDoc = await File.findById(atlasDoc[field]);
|
|
66
108
|
if (!fileDoc || !fileDoc.data) throw new Error(`File not found for atlas itemKey: ${itemKey}`);
|
|
67
109
|
|
|
68
110
|
return { buffer: Buffer.from(fileDoc.data), mimetype: fileDoc.mimetype || 'image/png', name: fileDoc.name };
|
|
69
111
|
};
|
|
70
|
-
static generate = async (req, res, options, generateOptions = {}) => {
|
|
71
|
-
/** @type {import('../object-layer/object-layer.model.js').ObjectLayerModel} */
|
|
72
|
-
const ObjectLayer = DataBaseProviderService.getModel('ObjectLayer', options);
|
|
73
|
-
/** @type {import('../file/file.model.js').FileModel} */
|
|
74
|
-
const File = DataBaseProviderService.getModel('File', options);
|
|
75
|
-
/** @type {import('./atlas-sprite-sheet.model.js').AtlasSpriteSheetModel} */
|
|
76
|
-
const AtlasSpriteSheet = DataBaseProviderService.getModel('AtlasSpriteSheet', options);
|
|
77
112
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!objectLayer) {
|
|
85
|
-
throw new Error('ObjectLayer not found');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (!objectLayer.objectLayerRenderFramesId) {
|
|
89
|
-
throw new Error('ObjectLayer has no render frames');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const itemKey = objectLayer.data.item.id;
|
|
93
|
-
const { buffer, metadata } = await AtlasSpriteSheetGenerator.generateAtlas(
|
|
94
|
-
objectLayer.objectLayerRenderFramesId,
|
|
95
|
-
itemKey,
|
|
113
|
+
static blob = (req, res, options) =>
|
|
114
|
+
AtlasSpriteSheetService.render(
|
|
115
|
+
{ itemKey: req.params.itemKey, field: 'minifyFileId', label: 'Minified atlas render' },
|
|
116
|
+
options,
|
|
96
117
|
);
|
|
97
118
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
buffer,
|
|
107
|
-
`${itemKey}_atlas_sprite_sheet.png`,
|
|
108
|
-
`/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet.png`,
|
|
109
|
-
);
|
|
110
|
-
if (ipfsResult) {
|
|
111
|
-
atlasCid = ipfsResult.cid;
|
|
112
|
-
logger.info(`Atlas sprite sheet pinned to IPFS – CID: ${atlasCid}`);
|
|
113
|
-
}
|
|
114
|
-
} catch (ipfsError) {
|
|
115
|
-
logger.warn('Failed to add atlas sprite sheet to IPFS:', ipfsError.message);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Pin atlas metadata JSON to IPFS (fast-json-stable-stringify) and obtain its CID
|
|
119
|
-
try {
|
|
120
|
-
const metadataIpfsResult = await IpfsClient.addJsonToIpfs(
|
|
121
|
-
metadata,
|
|
122
|
-
`${itemKey}_atlas_sprite_sheet_metadata.json`,
|
|
123
|
-
`/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet_metadata.json`,
|
|
124
|
-
);
|
|
125
|
-
if (metadataIpfsResult) {
|
|
126
|
-
atlasMetadataCid = metadataIpfsResult.cid;
|
|
127
|
-
logger.info(`Atlas metadata pinned to IPFS – CID: ${atlasMetadataCid}`);
|
|
128
|
-
}
|
|
129
|
-
} catch (ipfsError) {
|
|
130
|
-
logger.warn('Failed to add atlas metadata to IPFS:', ipfsError.message);
|
|
131
|
-
}
|
|
119
|
+
static idlePreview = (req, res, options) =>
|
|
120
|
+
AtlasSpriteSheetService.render(
|
|
121
|
+
{ itemKey: req.params.itemKey, field: 'idlePreviewFileId', label: 'Idle preview' },
|
|
122
|
+
options,
|
|
123
|
+
);
|
|
124
|
+
static generate = async (req, res, options, generateOptions = {}) => {
|
|
125
|
+
/** @type {import('../object-layer/object-layer.model.js').ObjectLayerModel} */
|
|
126
|
+
const ObjectLayer = DataBaseProviderService.getModel('ObjectLayer', options);
|
|
132
127
|
|
|
133
|
-
|
|
128
|
+
const objectLayer =
|
|
129
|
+
req.objectLayer || (await ObjectLayer.findById(req.params.id).populate('objectLayerRenderFramesId'));
|
|
134
130
|
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
if (atlasDoc.fileId) {
|
|
138
|
-
await File.findByIdAndDelete(atlasDoc.fileId);
|
|
139
|
-
}
|
|
140
|
-
atlasDoc.fileId = fileDoc._id;
|
|
141
|
-
atlasDoc.cid = atlasCid;
|
|
142
|
-
atlasDoc.metadata = metadata;
|
|
143
|
-
await atlasDoc.save();
|
|
144
|
-
} else {
|
|
145
|
-
atlasDoc = await new AtlasSpriteSheet({
|
|
146
|
-
fileId: fileDoc._id,
|
|
147
|
-
cid: atlasCid,
|
|
148
|
-
metadata,
|
|
149
|
-
}).save();
|
|
150
|
-
}
|
|
131
|
+
if (!objectLayer) throw new Error('ObjectLayer not found');
|
|
132
|
+
if (!objectLayer.objectLayerRenderFramesId) throw new Error('ObjectLayer has no render frames');
|
|
151
133
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
mfsPath: `/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet.png`,
|
|
159
|
-
options,
|
|
160
|
-
});
|
|
161
|
-
} catch (e) {
|
|
162
|
-
logger.warn('IPFS registry update failed (atlas PNG):', e.message);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (atlasMetadataCid) {
|
|
166
|
-
try {
|
|
167
|
-
await createPinRecord({
|
|
168
|
-
cid: atlasMetadataCid,
|
|
169
|
-
resourceType: 'atlas-metadata',
|
|
170
|
-
mfsPath: `/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet_metadata.json`,
|
|
171
|
-
options,
|
|
172
|
-
});
|
|
173
|
-
} catch (e) {
|
|
174
|
-
logger.warn('IPFS registry update failed (atlas metadata):', e.message);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
134
|
+
const { atlasDoc, atlasCid, atlasMetadataCid } = await AtlasSpriteSheetStore.persist({
|
|
135
|
+
itemKey: objectLayer.data.item.id,
|
|
136
|
+
objectLayerRenderFrames: objectLayer.objectLayerRenderFramesId,
|
|
137
|
+
upscaleFactor: generateOptions.upscaleFactor,
|
|
138
|
+
options,
|
|
139
|
+
});
|
|
177
140
|
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
// ObjectLayer atomically only after all CIDs are computed.
|
|
141
|
+
// Callers that stage CIDs in memory write the ObjectLayer themselves, so the
|
|
142
|
+
// document stays untouched until every CID is known.
|
|
181
143
|
if (generateOptions.skipObjectLayerSave) {
|
|
182
144
|
return { atlasDoc, atlasCid, atlasMetadataCid };
|
|
183
145
|
}
|
|
@@ -194,8 +156,6 @@ class AtlasSpriteSheetService {
|
|
|
194
156
|
static deleteByObjectLayerId = async (req, res, options) => {
|
|
195
157
|
/** @type {import('../object-layer/object-layer.model.js').ObjectLayerModel} */
|
|
196
158
|
const ObjectLayer = DataBaseProviderService.getModel('ObjectLayer', options);
|
|
197
|
-
/** @type {import('../file/file.model.js').FileModel} */
|
|
198
|
-
const File = DataBaseProviderService.getModel('File', options);
|
|
199
159
|
/** @type {import('./atlas-sprite-sheet.model.js').AtlasSpriteSheetModel} */
|
|
200
160
|
const AtlasSpriteSheet = DataBaseProviderService.getModel('AtlasSpriteSheet', options);
|
|
201
161
|
|
|
@@ -207,37 +167,12 @@ class AtlasSpriteSheetService {
|
|
|
207
167
|
if (objectLayer.atlasSpriteSheetId) {
|
|
208
168
|
const atlasDoc = await AtlasSpriteSheet.findById(objectLayer.atlasSpriteSheetId);
|
|
209
169
|
if (atlasDoc) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// Remove the MFS entry for the atlas sprite sheet PNG
|
|
217
|
-
const itemId = objectLayer.data.item.id;
|
|
218
|
-
await IpfsClient.removeMfsPath(`/object-layer/${itemId}/${itemId}_atlas_sprite_sheet.png`);
|
|
219
|
-
logger.info(`Cleaned up IPFS atlas CID ${atlasCid} for ObjectLayer ${objectLayer._id}`);
|
|
220
|
-
} catch (ipfsErr) {
|
|
221
|
-
logger.warn(`Failed to clean up IPFS atlas CID ${atlasCid}: ${ipfsErr.message}`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
if (atlasMetadataCid) {
|
|
225
|
-
try {
|
|
226
|
-
await removePinRecordsAndUnpin(atlasMetadataCid, options);
|
|
227
|
-
const itemId = objectLayer.data.item.id;
|
|
228
|
-
await IpfsClient.removeMfsPath(`/object-layer/${itemId}/${itemId}_atlas_sprite_sheet_metadata.json`);
|
|
229
|
-
logger.info(`Cleaned up IPFS atlas metadata CID ${atlasMetadataCid} for ObjectLayer ${objectLayer._id}`);
|
|
230
|
-
} catch (ipfsErr) {
|
|
231
|
-
logger.warn(`Failed to clean up IPFS atlas metadata CID ${atlasMetadataCid}: ${ipfsErr.message}`);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Delete the atlas File document from MongoDB
|
|
236
|
-
if (atlasDoc.fileId) {
|
|
237
|
-
await File.findByIdAndDelete(atlasDoc.fileId);
|
|
238
|
-
}
|
|
239
|
-
// Delete the AtlasSpriteSheet document itself
|
|
240
|
-
await AtlasSpriteSheet.findByIdAndDelete(atlasDoc._id);
|
|
170
|
+
await purgeAtlasDoc({
|
|
171
|
+
atlasDoc,
|
|
172
|
+
options,
|
|
173
|
+
itemKey: objectLayer.data.item.id,
|
|
174
|
+
metadataCid: objectLayer.data.render?.metadataCid,
|
|
175
|
+
});
|
|
241
176
|
}
|
|
242
177
|
objectLayer.atlasSpriteSheetId = undefined;
|
|
243
178
|
if (!objectLayer.data.render) objectLayer.data.render = {};
|
|
@@ -328,54 +263,23 @@ class AtlasSpriteSheetService {
|
|
|
328
263
|
static delete = async (req, res, options) => {
|
|
329
264
|
/** @type {import('./atlas-sprite-sheet.model.js').AtlasSpriteSheetModel} */
|
|
330
265
|
const AtlasSpriteSheet = DataBaseProviderService.getModel('AtlasSpriteSheet', options);
|
|
331
|
-
/** @type {import('../file/file.model.js').FileModel} */
|
|
332
|
-
const File = DataBaseProviderService.getModel('File', options);
|
|
333
266
|
|
|
334
267
|
if (req.params.id) {
|
|
335
268
|
const atlasDoc = await AtlasSpriteSheet.findById(req.params.id);
|
|
336
269
|
if (!atlasDoc) return null;
|
|
270
|
+
await purgeAtlasDoc({ atlasDoc, options });
|
|
271
|
+
return atlasDoc;
|
|
272
|
+
}
|
|
337
273
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
await IpfsClient.removeMfsPath(`/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet.png`);
|
|
345
|
-
}
|
|
346
|
-
logger.info(`Cleaned up IPFS atlas CID ${atlasDoc.cid} for AtlasSpriteSheet ${atlasDoc._id}`);
|
|
347
|
-
} catch (ipfsErr) {
|
|
348
|
-
logger.warn(`Failed to clean up IPFS atlas CID ${atlasDoc.cid}: ${ipfsErr.message}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Delete the referenced File document (the atlas PNG blob)
|
|
353
|
-
if (atlasDoc.fileId) {
|
|
354
|
-
await File.findByIdAndDelete(atlasDoc.fileId);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
return await AtlasSpriteSheet.findByIdAndDelete(req.params.id);
|
|
358
|
-
} else {
|
|
359
|
-
// Bulk delete: iterate each atlas to clean up File, IPFS pins, and pin records
|
|
360
|
-
const allAtlases = await AtlasSpriteSheet.find({});
|
|
361
|
-
for (const atlasDoc of allAtlases) {
|
|
362
|
-
try {
|
|
363
|
-
if (atlasDoc.cid) {
|
|
364
|
-
await removePinRecordsAndUnpin(atlasDoc.cid, options);
|
|
365
|
-
if (atlasDoc.metadata?.itemKey) {
|
|
366
|
-
const itemKey = atlasDoc.metadata.itemKey;
|
|
367
|
-
await IpfsClient.removeMfsPath(`/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet.png`);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
if (atlasDoc.fileId) {
|
|
371
|
-
await File.findByIdAndDelete(atlasDoc.fileId);
|
|
372
|
-
}
|
|
373
|
-
} catch (err) {
|
|
374
|
-
logger.error(`Failed to clean up AtlasSpriteSheet ${atlasDoc._id} during bulk delete: ${err.message}`);
|
|
375
|
-
}
|
|
274
|
+
const allAtlases = await AtlasSpriteSheet.find({});
|
|
275
|
+
for (const atlasDoc of allAtlases) {
|
|
276
|
+
try {
|
|
277
|
+
await purgeAtlasDoc({ atlasDoc, options });
|
|
278
|
+
} catch (err) {
|
|
279
|
+
logger.error(`Failed to clean up AtlasSpriteSheet ${atlasDoc._id} during bulk delete: ${err.message}`);
|
|
376
280
|
}
|
|
377
|
-
return await AtlasSpriteSheet.deleteMany();
|
|
378
281
|
}
|
|
282
|
+
return { deletedCount: allAtlases.length };
|
|
379
283
|
};
|
|
380
284
|
}
|
|
381
285
|
|
|
@@ -9,8 +9,8 @@ class CyberiaActionRouter {
|
|
|
9
9
|
*/
|
|
10
10
|
static router(options) {
|
|
11
11
|
const router = express.Router();
|
|
12
|
-
// Direct lookup by code
|
|
13
|
-
// (label, dialogue map) by the action code the
|
|
12
|
+
// Direct lookup by code. The client fetches an NPC's action metadata
|
|
13
|
+
// (label, dialogue map) by the action code the AOI stream carries.
|
|
14
14
|
router.get(`/code/:code`, async (req, res) => await CyberiaActionController.getByCode(req, res, options));
|
|
15
15
|
return registerCrudRoutes(router, CyberiaActionController, options);
|
|
16
16
|
}
|
|
@@ -34,10 +34,9 @@ class CyberiaActionService {
|
|
|
34
34
|
if (!code) throw new Error('code parameter is required');
|
|
35
35
|
const data = await CyberiaAction.findOne({ code }).lean();
|
|
36
36
|
if (data) return data;
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// action
|
|
40
|
-
// the client locates offered quests via the cyberia-quest API by cell.
|
|
37
|
+
// The fallback world serves actions from the canonical defaults and never
|
|
38
|
+
// persists them. Serve the same defaults here, so the client can fetch an
|
|
39
|
+
// action by code. Quest offers come from the cyberia-quest API by cell.
|
|
41
40
|
const fallback = DefaultCyberiaActions.find((a) => a.code === code);
|
|
42
41
|
if (fallback) return fallback;
|
|
43
42
|
throw new Error(`No action found for code: ${code}`);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One audio asset: what it is, never when it plays.
|
|
3
|
+
*
|
|
4
|
+
* The asset carries its identity (`code`), its bytes (`fileId`) and the manifest the recorder
|
|
5
|
+
* wrote beside them. It holds no classification of its own: whether a sound behaves as a bed, a
|
|
6
|
+
* one-shot or a transition is a property of the context that binds it — see
|
|
7
|
+
* cyberia-map-audio-conf — not of the asset, which the same map or several maps may reuse.
|
|
8
|
+
*
|
|
9
|
+
* @module src/api/cyberia-audio/cyberia-audio.model.js
|
|
10
|
+
* @namespace CyberiaAudioModelServer
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Schema, model } from 'mongoose';
|
|
14
|
+
|
|
15
|
+
// https://mongoosejs.com/docs/2.7.x/docs/schematypes.html
|
|
16
|
+
|
|
17
|
+
// Mirrors the `<name-out>.json` manifest the cyberia-audio package writes beside every recorded
|
|
18
|
+
// WAV. `defaults` and `parameters` are the asset's own parameter contract, so they stay
|
|
19
|
+
// free-form: each asset owns its parameter names and ranges.
|
|
20
|
+
const ManifestSchema = new Schema(
|
|
21
|
+
{
|
|
22
|
+
id: { type: String, required: true },
|
|
23
|
+
// The bus the module was authored on — `src/audio-module/<bus-id>/` in the cyberia-audio
|
|
24
|
+
// package, and the same vocabulary a binding's `settings.bus` selects. It is the asset's
|
|
25
|
+
// natural route, not a decision: a map binding chooses where an asset actually plays.
|
|
26
|
+
bus: { type: String, default: '', trim: true },
|
|
27
|
+
tags: [{ type: String }],
|
|
28
|
+
author: { type: String, required: true },
|
|
29
|
+
version: { type: String, required: true },
|
|
30
|
+
duration: { type: Number, required: true },
|
|
31
|
+
sampleRate: { type: Number },
|
|
32
|
+
channels: { type: Number },
|
|
33
|
+
loop: { type: Boolean, default: false },
|
|
34
|
+
audio: { type: String, default: '' },
|
|
35
|
+
defaults: { type: Schema.Types.Mixed, default: {} },
|
|
36
|
+
parameters: { type: Schema.Types.Mixed, default: {} },
|
|
37
|
+
},
|
|
38
|
+
{ _id: false },
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const CyberiaAudioSchema = new Schema(
|
|
42
|
+
{
|
|
43
|
+
// The asset's canonical identity across the platform: what a map binding, an import and an
|
|
44
|
+
// export all refer to. It is unique on its own, so a reference needs nothing beside it.
|
|
45
|
+
code: { type: String, required: true, trim: true },
|
|
46
|
+
fileId: { type: Schema.Types.ObjectId, ref: 'File' },
|
|
47
|
+
manifest: { type: ManifestSchema, required: true },
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
timestamps: true,
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Declared here rather than as `index: true` on the field, so the unique index is defined once.
|
|
55
|
+
CyberiaAudioSchema.index({ code: 1 }, { unique: true });
|
|
56
|
+
|
|
57
|
+
const CyberiaAudioModel = model('CyberiaAudio', CyberiaAudioSchema);
|
|
58
|
+
|
|
59
|
+
const ProviderSchema = CyberiaAudioSchema;
|
|
60
|
+
|
|
61
|
+
export { CyberiaAudioSchema, CyberiaAudioModel, ProviderSchema };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { registerCrudRoutes } from '../../server/network/middlewares.js';
|
|
3
|
+
import { CyberiaAudioController } from './cyberia-audio.controller.js';
|
|
4
|
+
|
|
5
|
+
class CyberiaAudioRouter {
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('../types.js').RouterOptions} options
|
|
8
|
+
* @returns {import('express').Router}
|
|
9
|
+
*/
|
|
10
|
+
static router(options) {
|
|
11
|
+
// Fully unguarded (matches prior behavior).
|
|
12
|
+
return registerCrudRoutes(express.Router(), CyberiaAudioController, options, {
|
|
13
|
+
writeGuards: [],
|
|
14
|
+
deleteAllGuards: [],
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ApiRouter = (options) => CyberiaAudioRouter.router(options);
|
|
20
|
+
|
|
21
|
+
export { ApiRouter, CyberiaAudioRouter };
|