cyberia 3.2.22 → 3.2.70
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 +127 -68
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +49 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +48 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +69 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +69 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +52 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +52 -0
- package/.github/workflows/engine-cyberia.cd.yml +33 -24
- package/.github/workflows/engine-cyberia.ci.yml +13 -3
- package/.github/workflows/ghpkg.ci.yml +88 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/hardhat.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +10 -7
- package/.github/workflows/publish.ci.yml +2 -2
- package/.github/workflows/publish.cyberia.ci.yml +5 -16
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -8
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +301 -1
- package/CLI-HELP.md +71 -6
- package/Dockerfile +141 -43
- package/Dockerfile.dev +143 -0
- package/Dockerfile.test +165 -0
- package/README.md +1 -1
- package/baremetal/commission-workflows.json +1 -0
- package/bin/build.js +31 -0
- package/bin/cyberia.js +1080 -184
- package/bin/deploy.js +2 -2
- package/bin/index.js +1080 -184
- package/bump.config.js +1 -0
- package/compose.env +131 -0
- package/conf.js +18 -1
- package/deployment.yaml +2 -2
- package/docker-compose.yml +316 -0
- package/hardhat/hardhat.config.js +2 -2
- package/hardhat/package-lock.json +620 -2041
- package/hardhat/package.json +7 -5
- package/hardhat/scripts/deployObjectLayerToken.js +18 -18
- package/hardhat/test/ObjectLayerToken.js +378 -274
- package/ipfs/configure-ipfs.sh +13 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +32 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +87 -0
- package/package.json +31 -19
- package/pv-pvc.yaml +32 -0
- package/scripts/disk-clean.sh +85 -60
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/rocky-kickstart.sh +877 -185
- package/scripts/rpmfusion-ffmpeg-setup.sh +26 -50
- package/scripts/test-monitor.sh +3 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +43 -7
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +17 -25
- package/src/api/cyberia-action/cyberia-action.controller.js +19 -0
- package/src/api/cyberia-action/cyberia-action.model.js +21 -29
- package/src/api/cyberia-action/cyberia-action.router.js +42 -7
- package/src/api/cyberia-action/cyberia-action.service.js +20 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +58 -57
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +37 -6
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +8 -2
- package/src/api/cyberia-entity/cyberia-entity.router.js +39 -7
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +74 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +63 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +62 -10
- package/src/api/cyberia-instance/cyberia-instance.model.js +32 -2
- package/src/api/cyberia-instance/cyberia-instance.router.js +6 -6
- package/src/api/cyberia-instance/cyberia-world-generator.js +116 -3
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +3 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +39 -7
- package/src/api/cyberia-map/cyberia-map.router.js +21 -6
- package/src/api/cyberia-quest/cyberia-quest.controller.js +38 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +47 -7
- package/src/api/cyberia-quest/cyberia-quest.service.js +59 -4
- package/src/api/cyberia-saga/cyberia-saga.controller.js +74 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +63 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +551 -129
- package/src/api/cyberia-skill/cyberia-skill.controller.js +74 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +63 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/ipfs/ipfs.service.js +28 -15
- package/src/api/object-layer/object-layer.router.js +25 -15
- package/src/api/object-layer/object-layer.service.js +15 -20
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +39 -7
- package/src/cli/baremetal.js +717 -16
- package/src/cli/cluster.js +80 -5
- package/src/cli/deploy.js +127 -11
- package/src/cli/docker-compose.js +648 -0
- package/src/cli/env.js +11 -2
- package/src/cli/fs.js +75 -30
- package/src/cli/image.js +129 -51
- package/src/cli/index.js +110 -6
- package/src/cli/kickstart.js +142 -20
- package/src/cli/release.js +46 -4
- package/src/cli/repository.js +238 -33
- package/src/cli/run.js +520 -114
- package/src/cli/secrets.js +82 -48
- package/src/cli/ssh.js +234 -0
- package/src/cli/static.js +2 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +219 -13
- package/src/client/components/cyberia/MapEngineCyberia.js +154 -71
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +40 -63
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +34 -20
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +195 -4
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +66 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +8 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +45 -27
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +3 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +39 -22
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +88 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +8 -1
- package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +5 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +2 -2
- package/src/client/services/cyberia-map/cyberia-map.management.js +2 -2
- package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -6
- package/src/{server → client-builder}/client-build-docs.js +15 -5
- package/src/{server → client-builder}/client-build-live.js +3 -3
- package/src/{server → client-builder}/client-build.js +25 -22
- package/src/{server → client-builder}/client-dev-server.js +3 -3
- package/src/{server → client-builder}/client-icons.js +2 -2
- package/src/{server → client-builder}/ssr.js +5 -5
- package/src/client.build.js +1 -3
- package/src/client.dev.js +1 -1
- package/src/db/mongo/MongoBootstrap.js +12 -12
- package/src/grpc/cyberia/grpc-server.js +255 -70
- package/src/index.js +12 -1
- package/src/mailer/EmailRender.js +1 -1
- package/src/{server → projects/cyberia}/atlas-sprite-sheet-generator.js +2 -2
- package/src/{server → projects/cyberia}/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +81 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2107 -0
- package/src/{server → projects/cyberia}/ipfs-client.js +2 -2
- package/src/{server → projects/cyberia}/object-layer.js +12 -108
- package/src/{server → projects/cyberia}/semantic-layer-generator-floor.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-resource.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-skin.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator.js +2 -2
- package/src/{server → projects/cyberia}/shape-generator.js +2 -2
- package/src/{server → projects/underpost}/catalog-underpost.js +1 -2
- package/src/runtime/cyberia-client/Dockerfile +27 -61
- package/src/runtime/cyberia-client/Dockerfile.dev +20 -12
- package/src/runtime/cyberia-server/Dockerfile +21 -20
- package/src/runtime/cyberia-server/Dockerfile.dev +17 -8
- package/src/runtime/engine-cyberia/Dockerfile +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +131 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +316 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +87 -0
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/server/catalog.js +9 -14
- package/src/server/conf.js +3 -6
- package/src/server/runtime-status.js +18 -1
- package/src/server/start.js +12 -2
- package/src/server.js +6 -2
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +26 -10
- package/test/shape-generator.test.js +7 -1
- package/typedoc.dd-cyberia.json +3 -1
- package/typedoc.json +3 -1
- /package/src/client/ssr/{Render.js → RootDocument.js} +0 -0
- /package/src/{server → client-builder}/client-formatted.js +0 -0
package/bin/index.js
CHANGED
|
@@ -5,10 +5,6 @@
|
|
|
5
5
|
* Provides commands for importing, viewing, and managing object layer assets,
|
|
6
6
|
* render frames, and atlas sprite sheets from the command line.
|
|
7
7
|
*
|
|
8
|
-
* Delegates shared object layer creation logic to {@link ObjectLayerEngine} in
|
|
9
|
-
* `src/server/object-layer.js` to keep a single source of truth shared with
|
|
10
|
-
* the REST API service layer.
|
|
11
|
-
*
|
|
12
8
|
* @module bin/cyberia.js
|
|
13
9
|
* @namespace CyberiaCLI
|
|
14
10
|
*/
|
|
@@ -19,34 +15,44 @@ import fs from 'fs-extra';
|
|
|
19
15
|
import stringify from 'fast-json-stable-stringify';
|
|
20
16
|
import { shellExec } from '../src/server/process.js';
|
|
21
17
|
import { loggerFactory } from '../src/server/logger.js';
|
|
22
|
-
import { generateBesuManifests, deployBesu, removeBesu } from '../src/
|
|
18
|
+
import { generateBesuManifests, deployBesu, removeBesu } from '../src/projects/cyberia/besu-genesis-generator.js';
|
|
23
19
|
import { DataBaseProviderService } from '../src/db/DataBaseProvider.js';
|
|
24
20
|
import { loadConfServerJson } from '../src/server/conf.js';
|
|
25
21
|
import {
|
|
26
22
|
ObjectLayerEngine,
|
|
27
23
|
resolveCanonicalCid,
|
|
28
24
|
pngDirectoryIteratorByObjectLayerType,
|
|
29
|
-
getKeyFramesDirectionsFromNumberFolderDirection,
|
|
30
25
|
buildImgFromTile,
|
|
31
|
-
} from '../src/
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
26
|
+
} from '../src/projects/cyberia/object-layer.js';
|
|
27
|
+
import { getKeyframeDirectionsByCode } from '../src/client/components/cyberia/SharedDefaultsCyberia.js';
|
|
28
|
+
import { AtlasSpriteSheetGenerator } from '../src/projects/cyberia/atlas-sprite-sheet-generator.js';
|
|
29
|
+
import {
|
|
30
|
+
generateMultiFrame,
|
|
31
|
+
lookupSemantic,
|
|
32
|
+
semanticRegistry,
|
|
33
|
+
} from '../src/projects/cyberia/semantic-layer-generator.js';
|
|
34
|
+
import { IpfsClient } from '../src/projects/cyberia/ipfs-client.js';
|
|
35
35
|
import { createPinRecord } from '../src/api/ipfs/ipfs.service.js';
|
|
36
36
|
import { program as underpostProgram } from '../src/cli/index.js';
|
|
37
|
+
import { generateSaga, importSaga } from '../src/projects/cyberia/generate-saga.js';
|
|
37
38
|
import crypto from 'crypto';
|
|
38
39
|
import nodePath from 'path';
|
|
39
40
|
import Underpost from '../src/index.js';
|
|
40
41
|
import { newInstance } from '../src/client/components/core/CommonJs.js';
|
|
41
42
|
import {
|
|
42
|
-
ITEM_TYPES as itemTypes,
|
|
43
|
-
DefaultCyberiaItems,
|
|
44
43
|
DefaultSkillConfig,
|
|
45
44
|
DefaultCyberiaDialogues,
|
|
46
45
|
DefaultCyberiaActions,
|
|
47
46
|
DefaultCyberiaQuests,
|
|
47
|
+
ENTITY_TYPE_DEFAULTS,
|
|
48
|
+
fillInstanceConfDefaults,
|
|
48
49
|
} from '../src/api/cyberia-server-defaults/cyberia-server-defaults.js';
|
|
49
50
|
|
|
51
|
+
import {
|
|
52
|
+
ITEM_TYPES as itemTypes,
|
|
53
|
+
DefaultCyberiaItems,
|
|
54
|
+
} from '../src/client/components/cyberia/SharedDefaultsCyberia.js';
|
|
55
|
+
|
|
50
56
|
/**
|
|
51
57
|
* Connect to the project MongoDB instance using the standard env / conf layout.
|
|
52
58
|
*
|
|
@@ -744,7 +750,7 @@ try {
|
|
|
744
750
|
|
|
745
751
|
/**
|
|
746
752
|
* Accumulated object layer data keyed by objectLayerId.
|
|
747
|
-
* @type {Object<string, import('../src/
|
|
753
|
+
* @type {Object<string, import('../src/projects/cyberia/object-layer.js').ObjectLayerData>}
|
|
748
754
|
*/
|
|
749
755
|
const objectLayers = {};
|
|
750
756
|
|
|
@@ -1221,7 +1227,7 @@ try {
|
|
|
1221
1227
|
}
|
|
1222
1228
|
|
|
1223
1229
|
// Get the keyframe direction name from the numerical direction code
|
|
1224
|
-
const objectLayerFrameDirections =
|
|
1230
|
+
const objectLayerFrameDirections = getKeyframeDirectionsByCode(direction);
|
|
1225
1231
|
if (objectLayerFrameDirections.length === 0) {
|
|
1226
1232
|
logger.error(`Invalid direction code: ${direction}. Valid codes: 08, 18, 02, 12, 04, 14, 06, 16`);
|
|
1227
1233
|
process.exit(1);
|
|
@@ -1711,11 +1717,18 @@ try {
|
|
|
1711
1717
|
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
1712
1718
|
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
1713
1719
|
.option('--dev', 'Force development environment')
|
|
1720
|
+
.option('--publish-build', 'Build instance backup directory with all related maps, entities and object layers')
|
|
1721
|
+
.option('--publish-remove', 'Remove published instance from underpostnet/cyberia-instances repository')
|
|
1722
|
+
.option('--publish', 'Publish instance in underpostnet/cyberia-instances repository')
|
|
1723
|
+
.option(
|
|
1724
|
+
'--from-n-commit <n>',
|
|
1725
|
+
'Number of latest engine commits to use for the publish commit message (default: 1).',
|
|
1726
|
+
)
|
|
1714
1727
|
.description('Export/import a Cyberia instance with all related maps, entities and object layers')
|
|
1715
1728
|
.action(async (instanceCode, options = {}) => {
|
|
1716
1729
|
if (!instanceCode) {
|
|
1717
|
-
|
|
1718
|
-
|
|
1730
|
+
instanceCode = 'amethyst-strata-expansion';
|
|
1731
|
+
logger.warn(`No instance code provided, defaulting to: ${instanceCode}`);
|
|
1719
1732
|
}
|
|
1720
1733
|
|
|
1721
1734
|
if (!options.envPath) options.envPath = `./.env`;
|
|
@@ -1737,6 +1750,113 @@ try {
|
|
|
1737
1750
|
logger.error(`Server config not found: ${confServerPath}`);
|
|
1738
1751
|
process.exit(1);
|
|
1739
1752
|
}
|
|
1753
|
+
|
|
1754
|
+
if (options.publish || options.publishBuild || options.publishRemove) {
|
|
1755
|
+
if (options.publishBuild) {
|
|
1756
|
+
if (!fs.existsSync('/home/dd/cyberia-instances')) {
|
|
1757
|
+
shellExec('cd /home/dd && underpost clone underpostnet/cyberia-instances');
|
|
1758
|
+
} else {
|
|
1759
|
+
shellExec(`underpost run clean /home/dd/cyberia-instances`);
|
|
1760
|
+
shellExec(`cd /home/dd/cyberia-instances && underpost pull . underpostnet/cyberia-instances`, {
|
|
1761
|
+
silentOnError: true,
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
fs.mkdirpSync(`/home/dd/cyberia-instances/conf/dd-cyberia`);
|
|
1766
|
+
fs.copyFileSync(
|
|
1767
|
+
`./engine-private/conf/dd-cyberia/conf.server.dev.dev.json`,
|
|
1768
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/conf.server.json`,
|
|
1769
|
+
);
|
|
1770
|
+
fs.copyFileSync(
|
|
1771
|
+
`./engine-private/conf/dd-cyberia/conf.client.json`,
|
|
1772
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/conf.client.json`,
|
|
1773
|
+
);
|
|
1774
|
+
fs.copyFileSync(
|
|
1775
|
+
`./engine-private/conf/dd-cyberia/conf.cron.json`,
|
|
1776
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/conf.cron.json`,
|
|
1777
|
+
);
|
|
1778
|
+
fs.copyFileSync(
|
|
1779
|
+
`./engine-private/conf/dd-cyberia/conf.ssr.json`,
|
|
1780
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/conf.ssr.json`,
|
|
1781
|
+
);
|
|
1782
|
+
fs.copyFileSync(
|
|
1783
|
+
`./engine-private/conf/dd-cyberia/conf.volume.json`,
|
|
1784
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/conf.volume.json`,
|
|
1785
|
+
);
|
|
1786
|
+
fs.copyFileSync(
|
|
1787
|
+
`./engine-private/conf/dd-cyberia/package.json`,
|
|
1788
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/package.json`,
|
|
1789
|
+
);
|
|
1790
|
+
fs.copyFileSync(
|
|
1791
|
+
`./engine-private/conf/dd-cyberia/docker-compose/cyberia/compose.env`,
|
|
1792
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/.env.production`,
|
|
1793
|
+
);
|
|
1794
|
+
fs.copyFileSync(
|
|
1795
|
+
`./engine-private/conf/dd-cyberia/docker-compose/cyberia/compose.env`,
|
|
1796
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/.env.development`,
|
|
1797
|
+
);
|
|
1798
|
+
fs.copyFileSync(
|
|
1799
|
+
`./engine-private/conf/dd-cyberia/docker-compose/cyberia/compose.env`,
|
|
1800
|
+
`/home/dd/cyberia-instances/conf/dd-cyberia/.env.test`,
|
|
1801
|
+
);
|
|
1802
|
+
|
|
1803
|
+
fs.mkdirpSync(`/home/dd/cyberia-instances/deployments`);
|
|
1804
|
+
fs.copySync(`./src/runtime/engine-cyberia`, `/home/dd/cyberia-instances/deployments/engine-cyberia`);
|
|
1805
|
+
fs.copySync(
|
|
1806
|
+
`./manifests/deployment/dd-cyberia-development/.`,
|
|
1807
|
+
`/home/dd/cyberia-instances/deployments/engine-cyberia/.`,
|
|
1808
|
+
);
|
|
1809
|
+
fs.copySync(`./src/runtime/cyberia-client`, `/home/dd/cyberia-instances/deployments/cyberia-client`);
|
|
1810
|
+
fs.copySync(
|
|
1811
|
+
`./engine-private/conf/dd-cyberia/instances/mmo-client/build/development/.`,
|
|
1812
|
+
`/home/dd/cyberia-instances/deployments/cyberia-client/.`,
|
|
1813
|
+
);
|
|
1814
|
+
fs.copySync(`./src/runtime/cyberia-server`, `/home/dd/cyberia-instances/deployments/cyberia-server`);
|
|
1815
|
+
fs.copySync(
|
|
1816
|
+
`./engine-private/conf/dd-cyberia/instances/mmo-server/build/development/.`,
|
|
1817
|
+
`/home/dd/cyberia-instances/deployments/cyberia-server/.`,
|
|
1818
|
+
);
|
|
1819
|
+
fs.removeSync(`/home/dd/cyberia-instances/public/cyberia`);
|
|
1820
|
+
fs.mkdirpSync(`/home/dd/cyberia-instances/public/cyberia`);
|
|
1821
|
+
for (const assetPath of Object.keys(
|
|
1822
|
+
JSON.parse(fs.readFileSync(`./engine-private/conf/dd-cyberia/storage.engine-cyberia.json`, 'utf-8')),
|
|
1823
|
+
)) {
|
|
1824
|
+
const relativePath = assetPath.replace(/^src\/client\/public\/cyberia\//, '');
|
|
1825
|
+
const targetPath = `/home/dd/cyberia-instances/public/cyberia/${relativePath}`;
|
|
1826
|
+
fs.mkdirpSync(nodePath.dirname(targetPath));
|
|
1827
|
+
logger.info(`Copying asset: ${assetPath} → ${targetPath}`);
|
|
1828
|
+
fs.copySync(`./${assetPath}`, targetPath);
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
fs.mkdirpSync(`/home/dd/cyberia-instances/instances`);
|
|
1832
|
+
fs.copySync(
|
|
1833
|
+
`./engine-private/cyberia-instances/${instanceCode}`,
|
|
1834
|
+
`/home/dd/cyberia-instances/instances/${instanceCode}`,
|
|
1835
|
+
);
|
|
1836
|
+
fs.mkdirpSync(`/home/dd/cyberia-instances/sagas`);
|
|
1837
|
+
fs.copyFileSync(
|
|
1838
|
+
`./engine-private/cyberia-sagas/${instanceCode}.json`,
|
|
1839
|
+
`/home/dd/cyberia-instances/sagas/${instanceCode}.json`,
|
|
1840
|
+
);
|
|
1841
|
+
return;
|
|
1842
|
+
} else if (options.publishRemove) {
|
|
1843
|
+
shellExec(`rm -rf /home/dd/cyberia-instances/instances/${instanceCode}`);
|
|
1844
|
+
shellExec(`rm -rf /home/dd/cyberia-instances/sagas/${instanceCode}.json`);
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
const fromN = parseInt(options.fromNCommit) > 0 ? parseInt(options.fromNCommit) : 1;
|
|
1848
|
+
const publishMessage =
|
|
1849
|
+
shellExec(`node bin cmt --changelog-msg --from-n-commit ${fromN} --changelog-no-hash`, {
|
|
1850
|
+
stdout: true,
|
|
1851
|
+
silent: true,
|
|
1852
|
+
}).trim() || `Update instance ${instanceCode}`;
|
|
1853
|
+
shellExec(`cd /home/dd/cyberia-instances \
|
|
1854
|
+
&& git add . \
|
|
1855
|
+
&& git commit -m "${publishMessage.replace(/"/g, '\\"')}" \
|
|
1856
|
+
&& underpost push . underpostnet/cyberia-instances`);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1740
1860
|
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
1741
1861
|
const { db } = confServer[host][path];
|
|
1742
1862
|
|
|
@@ -1755,6 +1875,11 @@ try {
|
|
|
1755
1875
|
'cyberia-dialogue',
|
|
1756
1876
|
'cyberia-map',
|
|
1757
1877
|
'cyberia-entity',
|
|
1878
|
+
'cyberia-quest',
|
|
1879
|
+
'cyberia-action',
|
|
1880
|
+
'cyberia-skill',
|
|
1881
|
+
'cyberia-entity-type-default',
|
|
1882
|
+
'cyberia-saga',
|
|
1758
1883
|
'object-layer',
|
|
1759
1884
|
'object-layer-render-frames',
|
|
1760
1885
|
'atlas-sprite-sheet',
|
|
@@ -1770,6 +1895,11 @@ try {
|
|
|
1770
1895
|
const CyberiaInstanceConf = DataBaseProviderService.getModel('cyberia-instance-conf', { host, path });
|
|
1771
1896
|
const CyberiaDialogue = DataBaseProviderService.getModel('cyberia-dialogue', { host, path });
|
|
1772
1897
|
const CyberiaMap = DataBaseProviderService.getModel('cyberia-map', { host, path });
|
|
1898
|
+
const CyberiaQuest = DataBaseProviderService.getModel('cyberia-quest', { host, path });
|
|
1899
|
+
const CyberiaAction = DataBaseProviderService.getModel('cyberia-action', { host, path });
|
|
1900
|
+
const CyberiaSkill = DataBaseProviderService.getModel('cyberia-skill', { host, path });
|
|
1901
|
+
const CyberiaEntityTypeDefault = DataBaseProviderService.getModel('cyberia-entity-type-default', { host, path });
|
|
1902
|
+
const CyberiaSaga = DataBaseProviderService.getModel('cyberia-saga', { host, path });
|
|
1773
1903
|
const ObjectLayer = DataBaseProviderService.getModel('object-layer', { host, path });
|
|
1774
1904
|
const ObjectLayerRenderFrames = DataBaseProviderService.getModel('object-layer-render-frames', { host, path });
|
|
1775
1905
|
const AtlasSpriteSheet = DataBaseProviderService.getModel('atlas-sprite-sheet', { host, path });
|
|
@@ -1852,36 +1982,6 @@ try {
|
|
|
1852
1982
|
...(entry.mfsPaths.length ? { mfsPaths: entry.mfsPaths } : {}),
|
|
1853
1983
|
}));
|
|
1854
1984
|
|
|
1855
|
-
const getDefaultDialoguesByItemId = (itemIds = []) => {
|
|
1856
|
-
const requestedItemIds = new Set(itemIds.filter(Boolean));
|
|
1857
|
-
const defaultsByItemId = new Map();
|
|
1858
|
-
|
|
1859
|
-
for (const dialogue of DefaultCyberiaDialogues) {
|
|
1860
|
-
// Match by code prefix: "default-<itemId>" covers the common case;
|
|
1861
|
-
// callers may also pass a full code directly.
|
|
1862
|
-
const matchingIds = [...requestedItemIds].filter(
|
|
1863
|
-
(id) => dialogue.code === `default-${id}` || dialogue.code === id,
|
|
1864
|
-
);
|
|
1865
|
-
if (!matchingIds.length) continue;
|
|
1866
|
-
for (const id of matchingIds) {
|
|
1867
|
-
if (!defaultsByItemId.has(id)) defaultsByItemId.set(id, []);
|
|
1868
|
-
defaultsByItemId.get(id).push({
|
|
1869
|
-
code: dialogue.code,
|
|
1870
|
-
order: dialogue.order ?? 0,
|
|
1871
|
-
speaker: dialogue.speaker ?? '',
|
|
1872
|
-
text: dialogue.text,
|
|
1873
|
-
mood: dialogue.mood ?? 'neutral',
|
|
1874
|
-
});
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
for (const dialogues of defaultsByItemId.values()) {
|
|
1879
|
-
dialogues.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
return defaultsByItemId;
|
|
1883
|
-
};
|
|
1884
|
-
|
|
1885
1985
|
const rewriteImportedCidReferences = async ({ oldCid, newCid, resourceType }) => {
|
|
1886
1986
|
if (!oldCid || !newCid || oldCid === newCid) return;
|
|
1887
1987
|
|
|
@@ -1965,6 +2065,10 @@ try {
|
|
|
1965
2065
|
instanceConf = created?.toObject ? created.toObject() : created;
|
|
1966
2066
|
}
|
|
1967
2067
|
if (instanceConf) {
|
|
2068
|
+
// `.lean()` skips Mongoose schema defaults and older docs may predate
|
|
2069
|
+
// some fields, so backfill every CyberiaInstanceConfSchema field from
|
|
2070
|
+
// the canonical defaults before writing the backup.
|
|
2071
|
+
instanceConf = fillInstanceConfDefaults(instanceConf);
|
|
1968
2072
|
fs.writeJsonSync(`${backupDir}/cyberia-instance-conf.json`, instanceConf, { spaces: 2 });
|
|
1969
2073
|
logger.info('Exported CyberiaInstanceConf', { instanceCode });
|
|
1970
2074
|
} else {
|
|
@@ -1998,6 +2102,36 @@ try {
|
|
|
1998
2102
|
}
|
|
1999
2103
|
logger.info(`Exported ${maps.length} CyberiaMap document(s)`, { codes: maps.map((m) => m.code) });
|
|
2000
2104
|
|
|
2105
|
+
// 3b. Export quests + actions bound to THIS instance's maps (sourceMapCode
|
|
2106
|
+
// in the instance's map codes) — only the content tied to this instance.
|
|
2107
|
+
// Dialogue codes the actions reference are collected so they travel too.
|
|
2108
|
+
const actionDialogueCodes = new Set();
|
|
2109
|
+
const quests = await CyberiaQuest.find({ sourceMapCode: { $in: [...mapCodes] } }).lean();
|
|
2110
|
+
if (quests.length > 0) {
|
|
2111
|
+
fs.ensureDirSync(`${backupDir}/cyberia-quests`);
|
|
2112
|
+
for (const quest of quests) {
|
|
2113
|
+
fs.writeJsonSync(`${backupDir}/cyberia-quests/${encodeURIComponent(quest.code)}.json`, quest, {
|
|
2114
|
+
spaces: 2,
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
logger.info(`Exported ${quests.length} CyberiaQuest document(s)`, { codes: quests.map((q) => q.code) });
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
const actions = await CyberiaAction.find({ sourceMapCode: { $in: [...mapCodes] } }).lean();
|
|
2121
|
+
if (actions.length > 0) {
|
|
2122
|
+
fs.ensureDirSync(`${backupDir}/cyberia-actions`);
|
|
2123
|
+
for (const action of actions) {
|
|
2124
|
+
fs.writeJsonSync(`${backupDir}/cyberia-actions/${encodeURIComponent(action.code)}.json`, action, {
|
|
2125
|
+
spaces: 2,
|
|
2126
|
+
});
|
|
2127
|
+
if (action.dialogCode) actionDialogueCodes.add(action.dialogCode);
|
|
2128
|
+
for (const qd of action.questDialogueCodes || []) {
|
|
2129
|
+
if (qd.dialogCode) actionDialogueCodes.add(qd.dialogCode);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
logger.info(`Exported ${actions.length} CyberiaAction document(s)`, { codes: actions.map((a) => a.code) });
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2001
2135
|
// 4. Collect all objectLayerItemIds from map entities
|
|
2002
2136
|
const objectLayerItemIds = new Set();
|
|
2003
2137
|
for (const map of maps) {
|
|
@@ -2008,11 +2142,14 @@ try {
|
|
|
2008
2142
|
}
|
|
2009
2143
|
}
|
|
2010
2144
|
|
|
2011
|
-
// 4b. Add instance-level itemIds
|
|
2012
|
-
for (const
|
|
2145
|
+
// 4b. Add instance-level itemIds ({ id, defaultPlayerInventory }).
|
|
2146
|
+
for (const entry of instance.itemIds || []) {
|
|
2147
|
+
const id = typeof entry === 'string' ? entry : entry?.id;
|
|
2013
2148
|
if (id) objectLayerItemIds.add(id);
|
|
2014
2149
|
}
|
|
2015
2150
|
|
|
2151
|
+
const contentItemIds = new Set(objectLayerItemIds);
|
|
2152
|
+
|
|
2016
2153
|
// 4c. Add all itemIds referenced by CyberiaInstanceConf (entityDefaults + skillConfig).
|
|
2017
2154
|
// This ensures liveItemIds, deadItemIds, dropItemIds, defaultObjectLayers and
|
|
2018
2155
|
// skill trigger items are included even if no map entity currently uses them.
|
|
@@ -2035,50 +2172,106 @@ try {
|
|
|
2035
2172
|
}
|
|
2036
2173
|
}
|
|
2037
2174
|
|
|
2038
|
-
// 4d. Export
|
|
2039
|
-
//
|
|
2040
|
-
//
|
|
2175
|
+
// 4d. Export skills whose trigger item belongs to this instance (own model:
|
|
2176
|
+
// CyberiaSkill, keyed by triggerItemId). Their summoned-entity items are
|
|
2177
|
+
// added to the OL set so those atlases export too. Runs before the
|
|
2178
|
+
// dialogue + OL queries so the summoned ids are included.
|
|
2041
2179
|
if (objectLayerItemIds.size > 0) {
|
|
2042
|
-
const
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2180
|
+
const skills = await CyberiaSkill.find({ triggerItemId: { $in: [...objectLayerItemIds] } }).lean();
|
|
2181
|
+
if (skills.length > 0) {
|
|
2182
|
+
fs.ensureDirSync(`${backupDir}/cyberia-skills`);
|
|
2183
|
+
for (const skill of skills) {
|
|
2184
|
+
fs.writeJsonSync(`${backupDir}/cyberia-skills/${encodeURIComponent(skill.triggerItemId)}.json`, skill, {
|
|
2185
|
+
spaces: 2,
|
|
2186
|
+
});
|
|
2187
|
+
for (const def of skill.skills || []) {
|
|
2188
|
+
if (def.summonedEntityItemId && !def.summonedEntityItemId.startsWith('$')) {
|
|
2189
|
+
objectLayerItemIds.add(def.summonedEntityItemId);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
logger.info(`Exported ${skills.length} CyberiaSkill document(s)`, {
|
|
2194
|
+
triggerItemIds: skills.map((sk) => sk.triggerItemId),
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2057
2198
|
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2199
|
+
// 4d-bis. Export entity-type defaults whose item ids belong to this
|
|
2200
|
+
// instance's real content (own model: CyberiaEntityTypeDefault). A
|
|
2201
|
+
// default is related when any of its live/dead/drop ids or default
|
|
2202
|
+
// object-layer ids appears in contentItemIds — map/instance content
|
|
2203
|
+
// only, NOT the canonical conf defaults every instance shares (which
|
|
2204
|
+
// would spuriously drag in the global seed entity-type-defaults).
|
|
2205
|
+
// Matched ids are folded back into objectLayerItemIds so the related
|
|
2206
|
+
// atlases + dialogues export too.
|
|
2207
|
+
if (contentItemIds.size > 0) {
|
|
2208
|
+
const idsForMatch = [...contentItemIds];
|
|
2209
|
+
const entityDefaults = await CyberiaEntityTypeDefault.find({
|
|
2210
|
+
$or: [
|
|
2211
|
+
{ liveItemIds: { $in: idsForMatch } },
|
|
2212
|
+
{ deadItemIds: { $in: idsForMatch } },
|
|
2213
|
+
{ dropItemIds: { $in: idsForMatch } },
|
|
2214
|
+
{ 'defaultObjectLayers.itemId': { $in: idsForMatch } },
|
|
2215
|
+
],
|
|
2216
|
+
}).lean();
|
|
2217
|
+
if (entityDefaults.length > 0) {
|
|
2218
|
+
fs.ensureDirSync(`${backupDir}/cyberia-entity-type-defaults`);
|
|
2219
|
+
for (const ed of entityDefaults) {
|
|
2220
|
+
fs.writeJsonSync(`${backupDir}/cyberia-entity-type-defaults/${ed._id}.json`, ed, { spaces: 2 });
|
|
2221
|
+
for (const id of ed.liveItemIds || []) if (id) objectLayerItemIds.add(id);
|
|
2222
|
+
for (const id of ed.deadItemIds || []) if (id) objectLayerItemIds.add(id);
|
|
2223
|
+
for (const id of ed.dropItemIds || []) if (id) objectLayerItemIds.add(id);
|
|
2224
|
+
for (const slot of ed.defaultObjectLayers || []) if (slot.itemId) objectLayerItemIds.add(slot.itemId);
|
|
2071
2225
|
}
|
|
2226
|
+
logger.info(`Exported ${entityDefaults.length} CyberiaEntityTypeDefault document(s)`, {
|
|
2227
|
+
entityTypes: entityDefaults.map((ed) => ed.entityType),
|
|
2228
|
+
});
|
|
2072
2229
|
}
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
// 4e. Export sagas related to this instance. A saga is considered related
|
|
2233
|
+
// when its code matches the instance code (direct namespace match), or
|
|
2234
|
+
// when its mapCodes or itemIds overlap with the instance's data.
|
|
2235
|
+
// At this point objectLayerItemIds contains all map-entity, instance-level,
|
|
2236
|
+
// conf-default, and skill-summoned item IDs — giving the broadest possible
|
|
2237
|
+
// match surface for saga discovery.
|
|
2238
|
+
const sagaCodeMatch = instanceCode ? await CyberiaSaga.find({ code: instanceCode }).lean() : [];
|
|
2239
|
+
// Disabling overlaps queries for now because they can be very expensive and are not strictly necessary for a backup.
|
|
2240
|
+
const sagaMapOverlap = true
|
|
2241
|
+
? []
|
|
2242
|
+
: mapCodes.size > 0
|
|
2243
|
+
? await CyberiaSaga.find({ mapCodes: { $in: [...mapCodes] } }).lean()
|
|
2244
|
+
: [];
|
|
2245
|
+
const sagaItemOverlap = true
|
|
2246
|
+
? []
|
|
2247
|
+
: objectLayerItemIds.size > 0
|
|
2248
|
+
? await CyberiaSaga.find({ itemIds: { $in: [...objectLayerItemIds] } }).lean()
|
|
2249
|
+
: [];
|
|
2250
|
+
const allSagas = [
|
|
2251
|
+
...new Map(
|
|
2252
|
+
[...sagaCodeMatch, ...sagaMapOverlap, ...sagaItemOverlap].map((s) => [s._id.toString(), s]),
|
|
2253
|
+
).values(),
|
|
2254
|
+
];
|
|
2255
|
+
if (allSagas.length > 0) {
|
|
2256
|
+
fs.ensureDirSync(`${backupDir}/cyberia-sagas`);
|
|
2257
|
+
for (const saga of allSagas) {
|
|
2258
|
+
fs.writeJsonSync(`${backupDir}/cyberia-sagas/${encodeURIComponent(saga.code)}.json`, saga, { spaces: 2 });
|
|
2259
|
+
}
|
|
2260
|
+
logger.info(`Exported ${allSagas.length} CyberiaSaga document(s)`, { codes: allSagas.map((s) => s.code) });
|
|
2261
|
+
}
|
|
2073
2262
|
|
|
2263
|
+
// 4f. Export dialogues for all relevant object-layer items (codes follow the
|
|
2264
|
+
// pattern "default-<itemId>") plus the dialogue codes the instance's
|
|
2265
|
+
// actions reference. If an item has no dialogue docs yet but ships with
|
|
2266
|
+
// DefaultCyberiaDialogues, seed those defaults into Mongo first.
|
|
2267
|
+
if (objectLayerItemIds.size > 0 || actionDialogueCodes.size > 0) {
|
|
2268
|
+
const requestedItemIds = [...objectLayerItemIds];
|
|
2269
|
+
const requestedCodes = [
|
|
2270
|
+
...new Set([...requestedItemIds.map((id) => `default-${id}`), ...actionDialogueCodes]),
|
|
2271
|
+
];
|
|
2074
2272
|
const dialogueDocs = await CyberiaDialogue.find({ code: { $in: requestedCodes } })
|
|
2075
2273
|
.sort({ code: 1, order: 1 })
|
|
2076
2274
|
.lean();
|
|
2077
|
-
|
|
2078
|
-
if (seededDialogueCount > 0) {
|
|
2079
|
-
logger.info(`Seeded ${seededDialogueCount} CyberiaDialogue default record(s) for export`);
|
|
2080
|
-
}
|
|
2081
|
-
|
|
2082
2275
|
if (dialogueDocs.length > 0) {
|
|
2083
2276
|
fs.ensureDirSync(`${backupDir}/cyberia-dialogues`);
|
|
2084
2277
|
const dialoguesByCode = new Map();
|
|
@@ -2164,20 +2357,12 @@ try {
|
|
|
2164
2357
|
}
|
|
2165
2358
|
}
|
|
2166
2359
|
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
ipfsPayloadFailures.push({
|
|
2172
|
-
itemKey,
|
|
2173
|
-
resourceType: 'atlas-sprite-sheet',
|
|
2174
|
-
mfsPath: itemPaths.atlasSpriteSheet,
|
|
2175
|
-
reason: 'AtlasSpriteSheet document not found in MongoDB',
|
|
2176
|
-
});
|
|
2177
|
-
continue;
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2360
|
+
const atlas =
|
|
2361
|
+
(ol.atlasSpriteSheetId ? await AtlasSpriteSheet.findById(ol.atlasSpriteSheetId).lean() : null) ||
|
|
2362
|
+
(await AtlasSpriteSheet.findOne({ 'metadata.itemKey': itemKey }).lean());
|
|
2363
|
+
if (atlas) {
|
|
2180
2364
|
const atlasExport = newInstance(atlas);
|
|
2365
|
+
objectLayerExport.atlasSpriteSheetId = atlas._id;
|
|
2181
2366
|
if (atlas.fileId) {
|
|
2182
2367
|
await exportFileDoc(atlas.fileId, `atlas-${itemKey}`);
|
|
2183
2368
|
}
|
|
@@ -2376,6 +2561,11 @@ try {
|
|
|
2376
2561
|
|
|
2377
2562
|
logger.info('Importing instance', { code: instanceCode, backupDir });
|
|
2378
2563
|
|
|
2564
|
+
// Item ids belonging to this instance (collected from imported object
|
|
2565
|
+
// layers + the instance doc) — used to backfill missing skills from the
|
|
2566
|
+
// canonical DefaultSkillConfig when the backup predates the skill model.
|
|
2567
|
+
const importedItemIds = new Set();
|
|
2568
|
+
|
|
2379
2569
|
// 0. Drop existing documents if --drop is set
|
|
2380
2570
|
if (options.drop && !options.conf) {
|
|
2381
2571
|
const existingInstance = await CyberiaInstance.findOne({ code: instanceCode }).lean();
|
|
@@ -2395,7 +2585,10 @@ try {
|
|
|
2395
2585
|
const otherInstances = await CyberiaInstance.find({ code: { $ne: instanceCode } }, { thumbnail: 1 }).lean();
|
|
2396
2586
|
|
|
2397
2587
|
// Add instance-level itemIds (may not appear in any map entity)
|
|
2398
|
-
for (const
|
|
2588
|
+
for (const entry of existingInstance.itemIds || []) {
|
|
2589
|
+
const id = typeof entry === 'string' ? entry : entry?.id;
|
|
2590
|
+
if (id) dropOlItemIds.add(id);
|
|
2591
|
+
}
|
|
2399
2592
|
|
|
2400
2593
|
// Add conf entityDefaults and skillConfig itemIds (liveItemIds, deadItemIds, dropItemIds, defaultObjectLayers)
|
|
2401
2594
|
const existingConf =
|
|
@@ -2435,6 +2628,37 @@ try {
|
|
|
2435
2628
|
|
|
2436
2629
|
const mapResult = await CyberiaMap.deleteMany({ code: { $in: [...dropMapCodes] } });
|
|
2437
2630
|
logger.info(`Dropped ${mapResult.deletedCount} CyberiaMap document(s)`);
|
|
2631
|
+
|
|
2632
|
+
// Quests + actions are bound to maps by sourceMapCode, so they drop
|
|
2633
|
+
// with this instance's maps — only the content tied to this instance.
|
|
2634
|
+
const questResult = await CyberiaQuest.deleteMany({ sourceMapCode: { $in: [...dropMapCodes] } });
|
|
2635
|
+
if (questResult.deletedCount > 0)
|
|
2636
|
+
logger.info(`Dropped ${questResult.deletedCount} CyberiaQuest document(s)`);
|
|
2637
|
+
|
|
2638
|
+
// Collect instance-specific dialogue codes the actions reference
|
|
2639
|
+
// (e.g. quest-talk-<questCode>) before deleting the actions. The
|
|
2640
|
+
// shared "default-<itemId>" greetings are left to the item-shared
|
|
2641
|
+
// logic below so dialogues shared with other instances survive.
|
|
2642
|
+
const dropActions = await CyberiaAction.find(
|
|
2643
|
+
{ sourceMapCode: { $in: [...dropMapCodes] } },
|
|
2644
|
+
{ dialogCode: 1, 'questDialogueCodes.dialogCode': 1 },
|
|
2645
|
+
).lean();
|
|
2646
|
+
const dropActionDialogueCodes = new Set();
|
|
2647
|
+
const collectDlg = (code) => {
|
|
2648
|
+
if (code && !code.startsWith('default-')) dropActionDialogueCodes.add(code);
|
|
2649
|
+
};
|
|
2650
|
+
for (const a of dropActions) {
|
|
2651
|
+
collectDlg(a.dialogCode);
|
|
2652
|
+
for (const qd of a.questDialogueCodes || []) collectDlg(qd.dialogCode);
|
|
2653
|
+
}
|
|
2654
|
+
const actionResult = await CyberiaAction.deleteMany({ sourceMapCode: { $in: [...dropMapCodes] } });
|
|
2655
|
+
if (actionResult.deletedCount > 0)
|
|
2656
|
+
logger.info(`Dropped ${actionResult.deletedCount} CyberiaAction document(s)`);
|
|
2657
|
+
if (dropActionDialogueCodes.size > 0) {
|
|
2658
|
+
const advResult = await CyberiaDialogue.deleteMany({ code: { $in: [...dropActionDialogueCodes] } });
|
|
2659
|
+
if (advResult.deletedCount > 0)
|
|
2660
|
+
logger.info(`Dropped ${advResult.deletedCount} CyberiaDialogue document(s) (action-referenced)`);
|
|
2661
|
+
}
|
|
2438
2662
|
}
|
|
2439
2663
|
|
|
2440
2664
|
// Exclude OL item IDs referenced by maps outside this instance
|
|
@@ -2463,6 +2687,12 @@ try {
|
|
|
2463
2687
|
const dropDialogueCodes = [...dropOlItemIds].map((id) => `default-${id}`);
|
|
2464
2688
|
const dialogueResult = await CyberiaDialogue.deleteMany({ code: { $in: dropDialogueCodes } });
|
|
2465
2689
|
logger.info(`Dropped ${dialogueResult.deletedCount} CyberiaDialogue document(s)`);
|
|
2690
|
+
|
|
2691
|
+
// Skills are keyed by triggerItemId — drop those whose trigger item is
|
|
2692
|
+
// being removed (i.e. not shared with another instance's maps).
|
|
2693
|
+
const skillResult = await CyberiaSkill.deleteMany({ triggerItemId: { $in: [...dropOlItemIds] } });
|
|
2694
|
+
if (skillResult.deletedCount > 0)
|
|
2695
|
+
logger.info(`Dropped ${skillResult.deletedCount} CyberiaSkill document(s)`);
|
|
2466
2696
|
const olDocs = await ObjectLayer.find(
|
|
2467
2697
|
{ 'data.item.id': { $in: [...dropOlItemIds] } },
|
|
2468
2698
|
{
|
|
@@ -2557,7 +2787,9 @@ try {
|
|
|
2557
2787
|
const confImportPath = `${backupDir}/cyberia-instance-conf.json`;
|
|
2558
2788
|
let importedConf = null;
|
|
2559
2789
|
if (fs.existsSync(confImportPath)) {
|
|
2560
|
-
|
|
2790
|
+
// Backfill any missing schema fields so older backups import a
|
|
2791
|
+
// complete, playable config into the DB.
|
|
2792
|
+
const confData = fillInstanceConfDefaults(fs.readJsonSync(confImportPath));
|
|
2561
2793
|
if (confData._id) await CyberiaInstanceConf.deleteOne({ _id: confData._id });
|
|
2562
2794
|
await CyberiaInstanceConf.deleteOne({ instanceCode: confData.instanceCode });
|
|
2563
2795
|
// Always bump updatedAt so the Go server's version hash changes and
|
|
@@ -2664,6 +2896,7 @@ try {
|
|
|
2664
2896
|
await ObjectLayer.deleteOne({ sha256: olData.sha256 });
|
|
2665
2897
|
}
|
|
2666
2898
|
await ObjectLayer.create(olData);
|
|
2899
|
+
if (olData.data?.item?.id) importedItemIds.add(olData.data.item.id);
|
|
2667
2900
|
olCount++;
|
|
2668
2901
|
}
|
|
2669
2902
|
logger.info(`Imported ${olCount} ObjectLayer document(s)`);
|
|
@@ -2735,7 +2968,9 @@ try {
|
|
|
2735
2968
|
// 6. Import CyberiaInstanceConf (skillRules, equipmentRules, entityDefaults, etc.)
|
|
2736
2969
|
const confImportPath = `${backupDir}/cyberia-instance-conf.json`;
|
|
2737
2970
|
if (fs.existsSync(confImportPath)) {
|
|
2738
|
-
|
|
2971
|
+
// Backfill any missing schema fields so older backups import a
|
|
2972
|
+
// complete, playable config into the DB.
|
|
2973
|
+
const confData = fillInstanceConfDefaults(fs.readJsonSync(confImportPath));
|
|
2739
2974
|
if (confData._id) await CyberiaInstanceConf.deleteOne({ _id: confData._id });
|
|
2740
2975
|
await CyberiaInstanceConf.deleteOne({ instanceCode: confData.instanceCode });
|
|
2741
2976
|
await CyberiaInstanceConf.create(confData);
|
|
@@ -2748,6 +2983,13 @@ try {
|
|
|
2748
2983
|
const instancePath = `${backupDir}/cyberia-instance.json`;
|
|
2749
2984
|
if (fs.existsSync(instancePath)) {
|
|
2750
2985
|
const instanceData = fs.readJsonSync(instancePath);
|
|
2986
|
+
// Heal legacy shapes against the current model. itemIds migrated from a
|
|
2987
|
+
// flat string[] to [{ id, defaultPlayerInventory }] — a raw old backup
|
|
2988
|
+
// would fail Mongoose embedded-cast validation, so normalize it here.
|
|
2989
|
+
instanceData.itemIds = (instanceData.itemIds || [])
|
|
2990
|
+
.map((entry) => (typeof entry === 'string' ? { id: entry, defaultPlayerInventory: false } : entry))
|
|
2991
|
+
.filter((entry) => entry && entry.id);
|
|
2992
|
+
for (const entry of instanceData.itemIds) importedItemIds.add(entry.id);
|
|
2751
2993
|
await CyberiaInstance.deleteOne({ code: instanceCode });
|
|
2752
2994
|
await CyberiaInstance.deleteOne({ _id: instanceData._id });
|
|
2753
2995
|
await CyberiaInstance.create(instanceData);
|
|
@@ -2785,6 +3027,126 @@ try {
|
|
|
2785
3027
|
logger.info(`Imported ${dialogueCount} CyberiaDialogue document(s)`);
|
|
2786
3028
|
}
|
|
2787
3029
|
|
|
3030
|
+
// 8b. Import CyberiaQuest documents (overwrite by code).
|
|
3031
|
+
const questsDir = `${backupDir}/cyberia-quests`;
|
|
3032
|
+
if (fs.existsSync(questsDir)) {
|
|
3033
|
+
const questFiles = fs.readdirSync(questsDir).filter((f) => f.endsWith('.json'));
|
|
3034
|
+
let questCount = 0;
|
|
3035
|
+
for (const file of questFiles) {
|
|
3036
|
+
const questData = fs.readJsonSync(`${questsDir}/${file}`);
|
|
3037
|
+
if (!questData.code) {
|
|
3038
|
+
logger.warn(`Skipping CyberiaQuest backup without code: ${file}`);
|
|
3039
|
+
continue;
|
|
3040
|
+
}
|
|
3041
|
+
await CyberiaQuest.deleteOne({ code: questData.code });
|
|
3042
|
+
if (questData._id) await CyberiaQuest.deleteOne({ _id: questData._id });
|
|
3043
|
+
await CyberiaQuest.create(questData);
|
|
3044
|
+
questCount++;
|
|
3045
|
+
}
|
|
3046
|
+
logger.info(`Imported ${questCount} CyberiaQuest document(s)`);
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
// 8c. Import CyberiaAction documents (overwrite by code).
|
|
3050
|
+
const actionsDir = `${backupDir}/cyberia-actions`;
|
|
3051
|
+
if (fs.existsSync(actionsDir)) {
|
|
3052
|
+
const actionFiles = fs.readdirSync(actionsDir).filter((f) => f.endsWith('.json'));
|
|
3053
|
+
let actionCount = 0;
|
|
3054
|
+
for (const file of actionFiles) {
|
|
3055
|
+
const actionData = fs.readJsonSync(`${actionsDir}/${file}`);
|
|
3056
|
+
if (!actionData.code) {
|
|
3057
|
+
logger.warn(`Skipping CyberiaAction backup without code: ${file}`);
|
|
3058
|
+
continue;
|
|
3059
|
+
}
|
|
3060
|
+
await CyberiaAction.deleteOne({ code: actionData.code });
|
|
3061
|
+
if (actionData._id) await CyberiaAction.deleteOne({ _id: actionData._id });
|
|
3062
|
+
await CyberiaAction.create(actionData);
|
|
3063
|
+
actionCount++;
|
|
3064
|
+
}
|
|
3065
|
+
logger.info(`Imported ${actionCount} CyberiaAction document(s)`);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
// 8d. Import CyberiaSkill documents (own model, overwrite by triggerItemId).
|
|
3069
|
+
const skillsDir = `${backupDir}/cyberia-skills`;
|
|
3070
|
+
if (fs.existsSync(skillsDir)) {
|
|
3071
|
+
const skillFiles = fs.readdirSync(skillsDir).filter((f) => f.endsWith('.json'));
|
|
3072
|
+
let skillCount = 0;
|
|
3073
|
+
for (const file of skillFiles) {
|
|
3074
|
+
const skillData = fs.readJsonSync(`${skillsDir}/${file}`);
|
|
3075
|
+
if (!skillData.triggerItemId) {
|
|
3076
|
+
logger.warn(`Skipping CyberiaSkill backup without triggerItemId: ${file}`);
|
|
3077
|
+
continue;
|
|
3078
|
+
}
|
|
3079
|
+
await CyberiaSkill.deleteOne({ triggerItemId: skillData.triggerItemId });
|
|
3080
|
+
if (skillData._id) await CyberiaSkill.deleteOne({ _id: skillData._id });
|
|
3081
|
+
await CyberiaSkill.create(skillData);
|
|
3082
|
+
skillCount++;
|
|
3083
|
+
}
|
|
3084
|
+
logger.info(`Imported ${skillCount} CyberiaSkill document(s)`);
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
// 8d-bis. Import CyberiaEntityTypeDefault documents (own model, overwrite
|
|
3088
|
+
// by the natural (entityType, liveItemIds) key, then by _id).
|
|
3089
|
+
const entityDefaultsDir = `${backupDir}/cyberia-entity-type-defaults`;
|
|
3090
|
+
if (fs.existsSync(entityDefaultsDir)) {
|
|
3091
|
+
const entityDefaultFiles = fs.readdirSync(entityDefaultsDir).filter((f) => f.endsWith('.json'));
|
|
3092
|
+
let entityDefaultCount = 0;
|
|
3093
|
+
for (const file of entityDefaultFiles) {
|
|
3094
|
+
const edData = fs.readJsonSync(`${entityDefaultsDir}/${file}`);
|
|
3095
|
+
if (!edData.entityType) {
|
|
3096
|
+
logger.warn(`Skipping CyberiaEntityTypeDefault backup without entityType: ${file}`);
|
|
3097
|
+
continue;
|
|
3098
|
+
}
|
|
3099
|
+
await CyberiaEntityTypeDefault.deleteMany({
|
|
3100
|
+
entityType: edData.entityType,
|
|
3101
|
+
liveItemIds: edData.liveItemIds || [],
|
|
3102
|
+
});
|
|
3103
|
+
if (edData._id) await CyberiaEntityTypeDefault.deleteOne({ _id: edData._id });
|
|
3104
|
+
await CyberiaEntityTypeDefault.create(edData);
|
|
3105
|
+
entityDefaultCount++;
|
|
3106
|
+
}
|
|
3107
|
+
logger.info(`Imported ${entityDefaultCount} CyberiaEntityTypeDefault document(s)`);
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
// 8e. Backfill missing skills from the canonical DefaultSkillConfig. Old
|
|
3111
|
+
// backups predate the CyberiaSkill model and ship no skills/ dir, so
|
|
3112
|
+
// any instance item that has a canonical skill (e.g. atlas_pistol_mk2,
|
|
3113
|
+
// coin, hatchet) but no document yet is seeded from defaults. Existing
|
|
3114
|
+
// skills are never overwritten.
|
|
3115
|
+
let backfilledSkillCount = 0;
|
|
3116
|
+
for (const sk of DefaultSkillConfig) {
|
|
3117
|
+
if (!importedItemIds.has(sk.triggerItemId)) continue;
|
|
3118
|
+
const exists = await CyberiaSkill.findOne({ triggerItemId: sk.triggerItemId }).lean();
|
|
3119
|
+
if (exists) continue;
|
|
3120
|
+
await CyberiaSkill.create({
|
|
3121
|
+
triggerItemId: sk.triggerItemId,
|
|
3122
|
+
logicEventIds: sk.logicEventIds || [],
|
|
3123
|
+
skills: sk.skills || [],
|
|
3124
|
+
});
|
|
3125
|
+
backfilledSkillCount++;
|
|
3126
|
+
}
|
|
3127
|
+
if (backfilledSkillCount > 0) {
|
|
3128
|
+
logger.info(`Backfilled ${backfilledSkillCount} CyberiaSkill document(s) from DefaultSkillConfig`);
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3131
|
+
// 8f. Import CyberiaSaga documents (overwrite by code).
|
|
3132
|
+
const sagasDir = `${backupDir}/cyberia-sagas`;
|
|
3133
|
+
if (fs.existsSync(sagasDir)) {
|
|
3134
|
+
const sagaFiles = fs.readdirSync(sagasDir).filter((f) => f.endsWith('.json'));
|
|
3135
|
+
let sagaCount = 0;
|
|
3136
|
+
for (const file of sagaFiles) {
|
|
3137
|
+
const sagaData = fs.readJsonSync(`${sagasDir}/${file}`);
|
|
3138
|
+
if (!sagaData.code) {
|
|
3139
|
+
logger.warn(`Skipping CyberiaSaga backup without code: ${file}`);
|
|
3140
|
+
continue;
|
|
3141
|
+
}
|
|
3142
|
+
await CyberiaSaga.deleteOne({ code: sagaData.code });
|
|
3143
|
+
if (sagaData._id) await CyberiaSaga.deleteOne({ _id: sagaData._id });
|
|
3144
|
+
await CyberiaSaga.create(sagaData);
|
|
3145
|
+
sagaCount++;
|
|
3146
|
+
}
|
|
3147
|
+
logger.info(`Imported ${sagaCount} CyberiaSaga document(s)`);
|
|
3148
|
+
}
|
|
3149
|
+
|
|
2788
3150
|
// 9. Restore IPFS pin records and payloads
|
|
2789
3151
|
const ipfsFile = `${backupDir}/ipfs/pins.json`;
|
|
2790
3152
|
if (fs.existsSync(ipfsFile)) {
|
|
@@ -2815,10 +3177,6 @@ try {
|
|
|
2815
3177
|
}
|
|
2816
3178
|
|
|
2817
3179
|
const backupPinEntries = serialiseCanonicalPins(backupPins);
|
|
2818
|
-
const backupCids = [...new Set(backupPinEntries.map((entry) => entry.cid).filter(Boolean))];
|
|
2819
|
-
if (backupCids.length > 0) {
|
|
2820
|
-
await Ipfs.deleteMany({ cid: { $in: backupCids } });
|
|
2821
|
-
}
|
|
2822
3180
|
|
|
2823
3181
|
const restoreAdditionalMfsPaths = async (cid, mfsPaths, primaryPath) => {
|
|
2824
3182
|
let restoredCount = 0;
|
|
@@ -2830,78 +3188,99 @@ try {
|
|
|
2830
3188
|
return restoredCount;
|
|
2831
3189
|
};
|
|
2832
3190
|
|
|
2833
|
-
const upsertImportedPin = async ({ cid, resourceType, mfsPath }) => {
|
|
2834
|
-
if (!cid || !resourceType) return;
|
|
2835
|
-
await Ipfs.deleteMany({ cid, resourceType });
|
|
2836
|
-
await createPinRecord({ cid, resourceType, mfsPath: mfsPath || '', options: { host, path } });
|
|
2837
|
-
};
|
|
2838
|
-
|
|
2839
3191
|
if (fs.existsSync(ipfsContentDir)) {
|
|
2840
3192
|
let cidRewriteCount = 0;
|
|
2841
3193
|
let extraMfsRestoreCount = 0;
|
|
3194
|
+
let ipfsAlreadyPresent = 0;
|
|
2842
3195
|
|
|
2843
3196
|
for (const [index, doc] of backupPinEntries.entries()) {
|
|
2844
3197
|
const mfsPaths = collectMfsPaths(doc);
|
|
2845
3198
|
const primaryPath = mfsPaths[0] || '';
|
|
2846
3199
|
const payloadPath = `${ipfsContentDir}/${doc.cid}.bin`;
|
|
2847
3200
|
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
3201
|
+
try {
|
|
3202
|
+
const existing = await Ipfs.findOne({ cid: doc.cid, resourceType: doc.resourceType })
|
|
3203
|
+
.select({ _id: 1 })
|
|
3204
|
+
.lean();
|
|
3205
|
+
if (existing) {
|
|
3206
|
+
ipfsAlreadyPresent++;
|
|
3207
|
+
continue;
|
|
3208
|
+
}
|
|
2855
3209
|
|
|
2856
|
-
|
|
2857
|
-
|
|
3210
|
+
logger.info('IPFS raw payload restore start', {
|
|
3211
|
+
index: index + 1,
|
|
3212
|
+
total: backupPinEntries.length,
|
|
2858
3213
|
cid: doc.cid,
|
|
2859
3214
|
resourceType: doc.resourceType,
|
|
2860
3215
|
mfsPath: primaryPath || null,
|
|
2861
3216
|
});
|
|
2862
|
-
ipfsSkipped++;
|
|
2863
|
-
continue;
|
|
2864
|
-
}
|
|
2865
3217
|
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
3218
|
+
if (!fs.existsSync(payloadPath)) {
|
|
3219
|
+
logger.warn('IPFS raw payload file missing from backup', {
|
|
3220
|
+
cid: doc.cid,
|
|
3221
|
+
resourceType: doc.resourceType,
|
|
3222
|
+
mfsPath: primaryPath || null,
|
|
3223
|
+
});
|
|
3224
|
+
ipfsSkipped++;
|
|
3225
|
+
continue;
|
|
3226
|
+
}
|
|
2871
3227
|
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
});
|
|
2878
|
-
ipfsSkipped++;
|
|
2879
|
-
continue;
|
|
2880
|
-
}
|
|
3228
|
+
const addResult = await IpfsClient.addToIpfs(
|
|
3229
|
+
fs.readFileSync(payloadPath),
|
|
3230
|
+
nodePath.basename(primaryPath || doc.cid),
|
|
3231
|
+
primaryPath || undefined,
|
|
3232
|
+
);
|
|
2881
3233
|
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
3234
|
+
if (!addResult?.cid) {
|
|
3235
|
+
logger.warn('IPFS raw payload restore failed', {
|
|
3236
|
+
cid: doc.cid,
|
|
3237
|
+
resourceType: doc.resourceType,
|
|
3238
|
+
mfsPath: primaryPath || null,
|
|
3239
|
+
});
|
|
3240
|
+
ipfsSkipped++;
|
|
3241
|
+
continue;
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
const finalCid = addResult.cid;
|
|
3245
|
+
if (doc.cid !== finalCid) {
|
|
3246
|
+
await rewriteImportedCidReferences({
|
|
3247
|
+
oldCid: doc.cid,
|
|
3248
|
+
newCid: finalCid,
|
|
3249
|
+
resourceType: doc.resourceType,
|
|
3250
|
+
});
|
|
3251
|
+
cidRewriteCount++;
|
|
3252
|
+
logger.warn('IPFS raw payload CID mismatch during import; rewriting imported references', {
|
|
3253
|
+
oldCid: doc.cid,
|
|
3254
|
+
newCid: finalCid,
|
|
3255
|
+
resourceType: doc.resourceType,
|
|
3256
|
+
mfsPath: primaryPath || null,
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
extraMfsRestoreCount += await restoreAdditionalMfsPaths(finalCid, mfsPaths, primaryPath);
|
|
3261
|
+
// createPinRecord is an idempotent upsert on (cid, resourceType).
|
|
3262
|
+
await createPinRecord({
|
|
3263
|
+
cid: finalCid,
|
|
2887
3264
|
resourceType: doc.resourceType,
|
|
3265
|
+
mfsPath: primaryPath || '',
|
|
3266
|
+
options: { host, path },
|
|
2888
3267
|
});
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
3268
|
+
ipfsCount++;
|
|
3269
|
+
} catch (entryError) {
|
|
3270
|
+
logger.warn('IPFS pin restore failed for one entry — skipping it, the import continues', {
|
|
3271
|
+
cid: doc.cid,
|
|
2893
3272
|
resourceType: doc.resourceType,
|
|
2894
3273
|
mfsPath: primaryPath || null,
|
|
3274
|
+
error: entryError?.message ?? String(entryError),
|
|
2895
3275
|
});
|
|
3276
|
+
ipfsSkipped++;
|
|
2896
3277
|
}
|
|
2897
|
-
|
|
2898
|
-
extraMfsRestoreCount += await restoreAdditionalMfsPaths(finalCid, mfsPaths, primaryPath);
|
|
2899
|
-
await upsertImportedPin({ cid: finalCid, resourceType: doc.resourceType, mfsPath: primaryPath });
|
|
2900
|
-
ipfsCount++;
|
|
2901
3278
|
}
|
|
2902
3279
|
|
|
2903
3280
|
logger.info(
|
|
2904
|
-
`Imported ${ipfsCount} Ipfs pin record(s) from exact backup payloads
|
|
3281
|
+
`Imported ${ipfsCount} Ipfs pin record(s) from exact backup payloads` +
|
|
3282
|
+
`${ipfsAlreadyPresent ? `, ${ipfsAlreadyPresent} already present (skipped)` : ''}` +
|
|
3283
|
+
`${ipfsSkipped ? `, ${ipfsSkipped} skipped` : ''}`,
|
|
2905
3284
|
);
|
|
2906
3285
|
logger.info(
|
|
2907
3286
|
`IPFS raw payload restore: ${ipfsCount}/${backupPinEntries.length} record(s) restored, ${extraMfsRestoreCount} additional MFS path(s) restored${cidRewriteCount ? `, ${cidRewriteCount} CID rewrite(s)` : ''}`,
|
|
@@ -3055,7 +3434,10 @@ try {
|
|
|
3055
3434
|
const otherInstances = await CyberiaInstance.find({ code: { $ne: instanceCode } }, { thumbnail: 1 }).lean();
|
|
3056
3435
|
|
|
3057
3436
|
// Add instance-level itemIds (may not appear in any map entity)
|
|
3058
|
-
for (const
|
|
3437
|
+
for (const entry of existingInstance.itemIds || []) {
|
|
3438
|
+
const id = typeof entry === 'string' ? entry : entry?.id;
|
|
3439
|
+
if (id) dropOlItemIds.add(id);
|
|
3440
|
+
}
|
|
3059
3441
|
|
|
3060
3442
|
// Add conf entityDefaults and skillConfig itemIds (liveItemIds, deadItemIds, dropItemIds, defaultObjectLayers)
|
|
3061
3443
|
const existingConf =
|
|
@@ -3311,6 +3693,154 @@ try {
|
|
|
3311
3693
|
}
|
|
3312
3694
|
});
|
|
3313
3695
|
|
|
3696
|
+
// ── generate-saga: Top-Down PCG guided by LLMs (Semantic Reverse-Engineering) ──
|
|
3697
|
+
program
|
|
3698
|
+
.command('generate-saga')
|
|
3699
|
+
.option(
|
|
3700
|
+
'--prompt <theme>',
|
|
3701
|
+
'Theme seed for the saga. If omitted, a distinct theme is auto-generated from the Cyberia base lore',
|
|
3702
|
+
)
|
|
3703
|
+
.option('--import <file>', 'Load a previously generated payload file (the shape --out writes) into the database')
|
|
3704
|
+
.option('--model <model>', 'Gemini model id (default: gemma-4-26b-a4b-it)')
|
|
3705
|
+
.option('--timeout <ms>', 'Per-request timeout in ms (default: 300000)', (v) => parseInt(v, 10))
|
|
3706
|
+
.option('--thinking-level <level>', 'Gemini thinking level: low | medium | high (default: high)')
|
|
3707
|
+
.option(
|
|
3708
|
+
'--lore-path <path>',
|
|
3709
|
+
'Override path to the base-lore doc (default: src/client/public/cyberia-docs/CYBERIA-LORE.md)',
|
|
3710
|
+
)
|
|
3711
|
+
.option(
|
|
3712
|
+
'--space-context <context>',
|
|
3713
|
+
'Force the auto-theme spatial layer: physical | mixed | hyperspace (default: random ~33% each)',
|
|
3714
|
+
)
|
|
3715
|
+
.option(
|
|
3716
|
+
'--tone <tone>',
|
|
3717
|
+
'Force the auto-theme narrative type: adventure | politics | tragic | comedy (default: random ~25% each)',
|
|
3718
|
+
)
|
|
3719
|
+
.option(
|
|
3720
|
+
'--faction-context <keys>',
|
|
3721
|
+
'Comma-separated factions that DRIVE the auto-theme: zenith | nova | atlas | neutral ' +
|
|
3722
|
+
"(e.g. 'nova,zenith'). If unset, confederations stay background, not the main theme",
|
|
3723
|
+
)
|
|
3724
|
+
.option(
|
|
3725
|
+
'--character-context <keys>',
|
|
3726
|
+
'Comma-separated CHARACTER_NAMES_POOL keys to inspire NPC/character names: low_level_synthetics | ' +
|
|
3727
|
+
'high_fidelity_synthetics | global_latin_diaspora | east_asian_pacific_diaspora | ' +
|
|
3728
|
+
'middle_eastern_turkish_diaspora | sub_saharan_african_diaspora | classic_western_scifi | ' +
|
|
3729
|
+
'mutagen_clans (inspiration only). If unset, a random subset is chosen',
|
|
3730
|
+
)
|
|
3731
|
+
.option(
|
|
3732
|
+
'--cultural-exposure <mode>',
|
|
3733
|
+
'Naming diversity mode: cosmopolitan (high mixing) | local (isolated, consistent). ' +
|
|
3734
|
+
'If unset, chosen at random',
|
|
3735
|
+
)
|
|
3736
|
+
.option(
|
|
3737
|
+
'--temperature <value>',
|
|
3738
|
+
'Model sampling temperature, valid range 0.0 (deterministic) to 2.0 (most creative); ' +
|
|
3739
|
+
'higher = more creative/divergent (default: 2.0 for theme synthesis)',
|
|
3740
|
+
parseFloat,
|
|
3741
|
+
)
|
|
3742
|
+
.option('--out <file>', 'Path to dump the payload JSON (default: ./engine-private/cyberia-sagas/<saga-code>.json)')
|
|
3743
|
+
.option('--dry-run', 'Generate and normalize without writing to the database')
|
|
3744
|
+
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
3745
|
+
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
3746
|
+
.option('--dev', 'Force development environment')
|
|
3747
|
+
.description('Generate (via Google Gemini) or import the non-spatial textual layer of a CyberiaSaga ecosystem')
|
|
3748
|
+
.action(async (options) => {
|
|
3749
|
+
if (!options.envPath) options.envPath = `./.env`;
|
|
3750
|
+
if (fs.existsSync(options.envPath)) dotenv.config({ path: options.envPath, override: true });
|
|
3751
|
+
|
|
3752
|
+
if (options.dev && process.env.DEFAULT_DEPLOY_ID) {
|
|
3753
|
+
const devEnvPath = `./engine-private/conf/${process.env.DEFAULT_DEPLOY_ID}/.env.development`;
|
|
3754
|
+
if (fs.existsSync(devEnvPath)) dotenv.config({ path: devEnvPath, override: true });
|
|
3755
|
+
}
|
|
3756
|
+
|
|
3757
|
+
let models = null;
|
|
3758
|
+
let host;
|
|
3759
|
+
let path;
|
|
3760
|
+
|
|
3761
|
+
if (!options.dryRun) {
|
|
3762
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
3763
|
+
host = process.env.DEFAULT_DEPLOY_HOST;
|
|
3764
|
+
path = process.env.DEFAULT_DEPLOY_PATH;
|
|
3765
|
+
|
|
3766
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
3767
|
+
if (!fs.existsSync(confServerPath)) {
|
|
3768
|
+
logger.error(`Server config not found: ${confServerPath}`);
|
|
3769
|
+
process.exit(1);
|
|
3770
|
+
}
|
|
3771
|
+
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
3772
|
+
const { db } = confServer[host][path];
|
|
3773
|
+
|
|
3774
|
+
db.host = options.mongoHost
|
|
3775
|
+
? options.mongoHost
|
|
3776
|
+
: options.dev
|
|
3777
|
+
? db.host
|
|
3778
|
+
: db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
|
|
3779
|
+
|
|
3780
|
+
logger.info('generate-saga', { deployId, host, path, db });
|
|
3781
|
+
|
|
3782
|
+
await DataBaseProviderService.load({
|
|
3783
|
+
apis: [
|
|
3784
|
+
'cyberia-saga',
|
|
3785
|
+
'cyberia-map',
|
|
3786
|
+
'cyberia-quest',
|
|
3787
|
+
'cyberia-dialogue',
|
|
3788
|
+
'cyberia-action',
|
|
3789
|
+
'cyberia-skill',
|
|
3790
|
+
'cyberia-instance',
|
|
3791
|
+
'object-layer',
|
|
3792
|
+
],
|
|
3793
|
+
host,
|
|
3794
|
+
path,
|
|
3795
|
+
db,
|
|
3796
|
+
});
|
|
3797
|
+
|
|
3798
|
+
models = {
|
|
3799
|
+
CyberiaSaga: DataBaseProviderService.getModel('cyberia-saga', { host, path }),
|
|
3800
|
+
CyberiaMap: DataBaseProviderService.getModel('cyberia-map', { host, path }),
|
|
3801
|
+
CyberiaQuest: DataBaseProviderService.getModel('cyberia-quest', { host, path }),
|
|
3802
|
+
CyberiaDialogue: DataBaseProviderService.getModel('cyberia-dialogue', { host, path }),
|
|
3803
|
+
CyberiaAction: DataBaseProviderService.getModel('cyberia-action', { host, path }),
|
|
3804
|
+
CyberiaSkill: DataBaseProviderService.getModel('cyberia-skill', { host, path }),
|
|
3805
|
+
CyberiaInstance: DataBaseProviderService.getModel('cyberia-instance', { host, path }),
|
|
3806
|
+
ObjectLayer: DataBaseProviderService.getModel('object-layer', { host, path }),
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3810
|
+
try {
|
|
3811
|
+
if (options.import) {
|
|
3812
|
+
await importSaga({
|
|
3813
|
+
file: options.import,
|
|
3814
|
+
models,
|
|
3815
|
+
dryRun: !!options.dryRun,
|
|
3816
|
+
out: options.out,
|
|
3817
|
+
});
|
|
3818
|
+
} else {
|
|
3819
|
+
await generateSaga({
|
|
3820
|
+
prompt: options.prompt,
|
|
3821
|
+
models,
|
|
3822
|
+
model: options.model,
|
|
3823
|
+
timeout: options.timeout,
|
|
3824
|
+
thinkingLevel: options.thinkingLevel,
|
|
3825
|
+
lorePath: options.lorePath,
|
|
3826
|
+
spaceContext: options.spaceContext,
|
|
3827
|
+
tone: options.tone,
|
|
3828
|
+
factionContext: options.factionContext,
|
|
3829
|
+
characterContext: options.characterContext,
|
|
3830
|
+
culturalExposure: options.culturalExposure,
|
|
3831
|
+
temperature: options.temperature,
|
|
3832
|
+
dryRun: !!options.dryRun,
|
|
3833
|
+
out: options.out,
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
} catch (err) {
|
|
3837
|
+
logger.error('generate-saga command error:', err);
|
|
3838
|
+
process.exitCode = 1;
|
|
3839
|
+
} finally {
|
|
3840
|
+
if (models) await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
3841
|
+
}
|
|
3842
|
+
});
|
|
3843
|
+
|
|
3314
3844
|
// ── chain: Hyperledger Besu / ERC-1155 lifecycle commands ────────────────
|
|
3315
3845
|
const chain = program.command('chain').description('Hyperledger Besu chain & ERC-1155 ObjectLayerToken lifecycle');
|
|
3316
3846
|
|
|
@@ -4133,9 +4663,11 @@ try {
|
|
|
4133
4663
|
.option('--dev', 'Force development environment (loads .env.development for IPFS localhost, etc.)')
|
|
4134
4664
|
.option(
|
|
4135
4665
|
'--mongo-host <mongo-host>',
|
|
4136
|
-
'Mongo host override (forwarded to ol, seed-
|
|
4666
|
+
'Mongo host override (forwarded to ol, seed-skills, seed-entities, seed-dialogues, seed-actions-quests, client-hints)',
|
|
4667
|
+
)
|
|
4668
|
+
.description(
|
|
4669
|
+
'Import default Object Layer items, skills, entity defaults, dialogues, actions/quests, and client-hints into MongoDB',
|
|
4137
4670
|
)
|
|
4138
|
-
.description('Import default Object Layer items, skill config, dialogues, and client-hints into MongoDB')
|
|
4139
4671
|
.action(async (options) => {
|
|
4140
4672
|
// Pre-flight: every item id referenced by the fallback world must
|
|
4141
4673
|
// exist in DefaultCyberiaItems. Drift here causes silent missing
|
|
@@ -4155,19 +4687,89 @@ try {
|
|
|
4155
4687
|
const mongoHostFlag = options.mongoHost ? ` --mongo-host ${options.mongoHost}` : '';
|
|
4156
4688
|
const instanceCode = process.env.INSTANCE_CODE || 'cyberia-main';
|
|
4157
4689
|
shellExec(`node bin/cyberia ol ${DefaultCyberiaItems.map((e) => e.item.id)} --import${devFlag}${mongoHostFlag}`);
|
|
4158
|
-
shellExec(`node bin/cyberia run-workflow seed-
|
|
4690
|
+
shellExec(`node bin/cyberia run-workflow seed-skills${devFlag}${mongoHostFlag}`);
|
|
4691
|
+
shellExec(`node bin/cyberia run-workflow seed-entities${devFlag}${mongoHostFlag}`);
|
|
4159
4692
|
shellExec(`node bin/cyberia run-workflow seed-dialogues${devFlag}${mongoHostFlag}`);
|
|
4693
|
+
shellExec(`node bin/cyberia run-workflow seed-actions-quests${devFlag}${mongoHostFlag}`);
|
|
4160
4694
|
shellExec(`node bin/cyberia client-hints ${instanceCode} --seed-defaults${devFlag}${mongoHostFlag}`);
|
|
4161
4695
|
});
|
|
4162
4696
|
|
|
4697
|
+
runner.command('sync-src').action(() => {
|
|
4698
|
+
fs.copyFileSync('./cyberia-server/README.md', './src/client/public/cyberia-docs/CYBERIA-SERVER.md');
|
|
4699
|
+
fs.copyFileSync('./cyberia-server/Dockerfile', './src/runtime/cyberia-server/Dockerfile');
|
|
4700
|
+
fs.copyFileSync('./cyberia-client/README.md', './src/client/public/cyberia-docs/CYBERIA-CLIENT.md');
|
|
4701
|
+
fs.copyFileSync('./cyberia-client/Dockerfile', './src/runtime/cyberia-client/Dockerfile');
|
|
4702
|
+
shellExec('cp -a ./engine-private/conf/dd-cyberia/docker-compose/cyberia/. ./src/runtime/engine-cyberia/');
|
|
4703
|
+
});
|
|
4704
|
+
|
|
4705
|
+
runner
|
|
4706
|
+
.command('dev-env')
|
|
4707
|
+
.option('--run', 'Run docker:reset, cluster --dev --reset, docker-image, and docker:up after updating compose.env')
|
|
4708
|
+
.option(
|
|
4709
|
+
'--clean',
|
|
4710
|
+
'Restore repositories to canonical state (git checkout Dockerfile, etc.) before updating compose.env',
|
|
4711
|
+
)
|
|
4712
|
+
.action((options) => {
|
|
4713
|
+
if (options.clean) {
|
|
4714
|
+
shellExec(`node bin run clean`);
|
|
4715
|
+
shellExec(`node bin run clean ./cyberia-server`);
|
|
4716
|
+
shellExec(`node bin run clean ./cyberia-client`);
|
|
4717
|
+
return;
|
|
4718
|
+
}
|
|
4719
|
+
const envPath = `./engine-private/conf/dd-cyberia/docker-compose/cyberia/compose.env`;
|
|
4720
|
+
const canonicalDevDockerfile = './src/runtime/engine-cyberia/Dockerfile.dev';
|
|
4721
|
+
fs.writeFileSync(
|
|
4722
|
+
canonicalDevDockerfile,
|
|
4723
|
+
fs
|
|
4724
|
+
.readFileSync(canonicalDevDockerfile, 'utf8')
|
|
4725
|
+
.replace('ENGINE_CYBERIA_REPO="engine-cyberia"', 'ENGINE_CYBERIA_REPO="engine-test-cyberia"')
|
|
4726
|
+
.replace(` # --mount=type=secret,id=github_token`, ` --mount=type=secret,id=github_token`)
|
|
4727
|
+
.replace(
|
|
4728
|
+
` # export GITHUB_TOKEN="$(cat /run/secrets/github_token)";`,
|
|
4729
|
+
` export GITHUB_TOKEN="$(cat /run/secrets/github_token)";`,
|
|
4730
|
+
)
|
|
4731
|
+
.replace(` for _secret in "$GITHUB_USERNAME"; do`, ` # for _secret in "$GITHUB_USERNAME"; do`)
|
|
4732
|
+
.replace(` unset GITHUB_USERNAME;`, ` # unset GITHUB_USERNAME;`)
|
|
4733
|
+
.replace(
|
|
4734
|
+
` # for _secret in "$GITHUB_USERNAME" "$GITHUB_TOKEN"; do`,
|
|
4735
|
+
` for _secret in "$GITHUB_USERNAME" "$GITHUB_TOKEN"; do`,
|
|
4736
|
+
)
|
|
4737
|
+
.replace(` # unset GITHUB_TOKEN GITHUB_USERNAME;`, ` unset GITHUB_TOKEN GITHUB_USERNAME;`),
|
|
4738
|
+
|
|
4739
|
+
'utf8',
|
|
4740
|
+
);
|
|
4741
|
+
fs.writeFileSync(
|
|
4742
|
+
'./src/cli/image.js',
|
|
4743
|
+
fs
|
|
4744
|
+
.readFileSync('./src/cli/image.js', 'utf8')
|
|
4745
|
+
.replace(
|
|
4746
|
+
` // addBuildSecret('github_token', process.env.GITHUB_TOKEN);`,
|
|
4747
|
+
` addBuildSecret('github_token', process.env.GITHUB_TOKEN);`,
|
|
4748
|
+
),
|
|
4749
|
+
'utf8',
|
|
4750
|
+
);
|
|
4751
|
+
fs.writeFileSync(envPath, fs.readFileSync(envPath, 'utf8').replaceAll('underpost/', 'localhost/'), 'utf8');
|
|
4752
|
+
shellExec('node bin/cyberia run-workflow sync-src');
|
|
4753
|
+
shellExec(`git checkout './src/runtime/cyberia-server/Dockerfile'`);
|
|
4754
|
+
shellExec(`git checkout './src/runtime/cyberia-client/Dockerfile'`);
|
|
4755
|
+
shellExec('node bin/cyberia run-workflow build-manifest');
|
|
4756
|
+
if (options.run) {
|
|
4757
|
+
shellExec('node bin/cyberia run-workflow docker:reset');
|
|
4758
|
+
shellExec('node bin cluster --dev --reset');
|
|
4759
|
+
shellExec('node bin/cyberia run-workflow docker-image engine-cyberia');
|
|
4760
|
+
shellExec('node bin/cyberia run-workflow docker-image cyberia-server');
|
|
4761
|
+
shellExec('node bin/cyberia run-workflow docker-image cyberia-client');
|
|
4762
|
+
shellExec('node bin/cyberia run-workflow docker:up');
|
|
4763
|
+
}
|
|
4764
|
+
});
|
|
4765
|
+
|
|
4163
4766
|
runner
|
|
4164
|
-
.command('
|
|
4165
|
-
.option('--instance-code <code>', 'CyberiaInstance code to update (default: $INSTANCE_CODE env or "default")')
|
|
4767
|
+
.command('drop-db')
|
|
4166
4768
|
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
4167
4769
|
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
4168
4770
|
.option('--dev', 'Force development environment')
|
|
4169
|
-
.description('
|
|
4170
|
-
.action(async (options) => {
|
|
4771
|
+
.description('Drop all Cyberia collections and remove File documents referenced by instance/map thumbnails')
|
|
4772
|
+
.action(async (options = {}) => {
|
|
4171
4773
|
if (!options.envPath) options.envPath = `./.env`;
|
|
4172
4774
|
if (fs.existsSync(options.envPath)) dotenv.config({ path: options.envPath, override: true });
|
|
4173
4775
|
|
|
@@ -4179,7 +4781,6 @@ try {
|
|
|
4179
4781
|
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
4180
4782
|
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
4181
4783
|
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
4182
|
-
const instanceCode = options.instanceCode || process.env.INSTANCE_CODE || 'default';
|
|
4183
4784
|
|
|
4184
4785
|
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
4185
4786
|
if (!fs.existsSync(confServerPath)) {
|
|
@@ -4195,42 +4796,133 @@ try {
|
|
|
4195
4796
|
? db.host
|
|
4196
4797
|
: db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
|
|
4197
4798
|
|
|
4198
|
-
logger.info('
|
|
4799
|
+
logger.info('drop-db', { deployId, host, path, db });
|
|
4800
|
+
|
|
4801
|
+
const cyberiaCollections = [
|
|
4802
|
+
'cyberia-entity',
|
|
4803
|
+
'cyberia-map',
|
|
4804
|
+
'cyberia-instance',
|
|
4805
|
+
'cyberia-instance-conf',
|
|
4806
|
+
'cyberia-dialogue',
|
|
4807
|
+
'cyberia-quest',
|
|
4808
|
+
'cyberia-quest-progress',
|
|
4809
|
+
'cyberia-action',
|
|
4810
|
+
'cyberia-skill',
|
|
4811
|
+
'cyberia-entity-type-default',
|
|
4812
|
+
'cyberia-client-hints',
|
|
4813
|
+
'cyberia-saga',
|
|
4814
|
+
];
|
|
4199
4815
|
|
|
4200
|
-
await DataBaseProviderService.load({ apis: [
|
|
4816
|
+
await DataBaseProviderService.load({ apis: [...cyberiaCollections, 'file'], host, path, db });
|
|
4201
4817
|
|
|
4202
|
-
const
|
|
4203
|
-
const CyberiaInstanceConf = DataBaseProviderService.getModel('cyberia-instance-conf', { host, path });
|
|
4204
|
-
|
|
4205
|
-
const instance = await CyberiaInstance.findOne({ code: instanceCode }).lean();
|
|
4818
|
+
const File = DataBaseProviderService.getModel('file', { host, path });
|
|
4206
4819
|
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
);
|
|
4820
|
+
// Thumbnails on instances/maps are File _id references; collect them before
|
|
4821
|
+
// dropping so the backing File documents don't leak as orphans.
|
|
4822
|
+
const thumbnailFileIds = new Set();
|
|
4823
|
+
for (const api of ['cyberia-instance', 'cyberia-map']) {
|
|
4824
|
+
const Model = DataBaseProviderService.getModel(api, { host, path });
|
|
4825
|
+
const docs = await Model.find({ thumbnail: { $ne: null } }, { thumbnail: 1 }).lean();
|
|
4826
|
+
for (const doc of docs) {
|
|
4827
|
+
if (doc.thumbnail) thumbnailFileIds.add(doc.thumbnail.toString());
|
|
4828
|
+
}
|
|
4212
4829
|
}
|
|
4213
4830
|
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
{
|
|
4217
|
-
{ $set: { skillConfig: DefaultSkillConfig } },
|
|
4218
|
-
{ upsert: true, returnDocument: 'after' },
|
|
4219
|
-
);
|
|
4220
|
-
|
|
4221
|
-
// If a live instance exists, ensure its conf ref is linked.
|
|
4222
|
-
if (instance && (!instance.conf || String(instance.conf) !== String(conf._id))) {
|
|
4223
|
-
await CyberiaInstance.findByIdAndUpdate(instance._id, { conf: conf._id });
|
|
4831
|
+
if (thumbnailFileIds.size > 0) {
|
|
4832
|
+
const result = await File.deleteMany({ _id: { $in: [...thumbnailFileIds] } });
|
|
4833
|
+
logger.info(`Removed ${result.deletedCount} thumbnail File document(s)`);
|
|
4224
4834
|
}
|
|
4225
4835
|
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4836
|
+
for (const api of cyberiaCollections) {
|
|
4837
|
+
const Model = DataBaseProviderService.getModel(api, { host, path });
|
|
4838
|
+
const result = await Model.deleteMany();
|
|
4839
|
+
logger.info(`Dropped ${result.deletedCount} ${api} document(s)`);
|
|
4840
|
+
}
|
|
4230
4841
|
|
|
4231
4842
|
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
4232
4843
|
});
|
|
4233
4844
|
|
|
4845
|
+
const dockerImageIds = ['engine-cyberia', 'cyberia-server', 'cyberia-client'];
|
|
4846
|
+
|
|
4847
|
+
runner.command('deploy [id]').action((id) => {
|
|
4848
|
+
if (!dockerImageIds.includes(id)) {
|
|
4849
|
+
logger.error(`Invalid deploy id: ${id}. Must be one of: ${dockerImageIds.join(', ')}`);
|
|
4850
|
+
process.exit(1);
|
|
4851
|
+
}
|
|
4852
|
+
shellExec(`gh workflow run ${id}.cd.yml -R underpostnet/${id} -f job=deploy`);
|
|
4853
|
+
});
|
|
4854
|
+
|
|
4855
|
+
runner
|
|
4856
|
+
.command('docker-image [id]')
|
|
4857
|
+
.option('--load-tar', 'Load a pre-built image tar archive into the enabled target(s) without building.')
|
|
4858
|
+
.action((id, options) => {
|
|
4859
|
+
// no funca
|
|
4860
|
+
if (options.loadTar) {
|
|
4861
|
+
for (const imageId of dockerImageIds)
|
|
4862
|
+
if (imageId === id || id === '.') shellExec(`docker load -i ./${imageId}-dev_v3.2.70.tar`);
|
|
4863
|
+
return;
|
|
4864
|
+
}
|
|
4865
|
+
switch (id) {
|
|
4866
|
+
case 'engine-cyberia':
|
|
4867
|
+
shellExec(`clear
|
|
4868
|
+
node bin/build dd-cyberia --conf
|
|
4869
|
+
node bin/build dd-cyberia --update-private
|
|
4870
|
+
node bin image --path src/runtime/engine-cyberia \
|
|
4871
|
+
--docker-compose --pull-base --build \
|
|
4872
|
+
--dockerfile-name Dockerfile.dev \
|
|
4873
|
+
--image-name engine-cyberia-dev:v3.2.70 \
|
|
4874
|
+
--image-out-path .
|
|
4875
|
+
`);
|
|
4876
|
+
break;
|
|
4877
|
+
|
|
4878
|
+
case 'cyberia-server':
|
|
4879
|
+
shellExec(
|
|
4880
|
+
`clear && node bin/cyberia run-workflow build-server-dashboard --output-path ./cyberia-server/public/index.html`,
|
|
4881
|
+
);
|
|
4882
|
+
shellExec(`
|
|
4883
|
+
cp -f src/runtime/cyberia-server/Dockerfile.dev cyberia-server/Dockerfile.dev
|
|
4884
|
+
node bin image --path cyberia-server \
|
|
4885
|
+
--docker-compose --pull-base --build \
|
|
4886
|
+
--dockerfile-name Dockerfile.dev \
|
|
4887
|
+
--image-name cyberia-server-dev:v3.2.70 \
|
|
4888
|
+
--image-out-path .
|
|
4889
|
+
`);
|
|
4890
|
+
break;
|
|
4891
|
+
case 'cyberia-client':
|
|
4892
|
+
shellExec(`clear
|
|
4893
|
+
cp -f src/runtime/cyberia-client/Dockerfile.dev cyberia-client/Dockerfile.dev
|
|
4894
|
+
node bin image --path cyberia-client \
|
|
4895
|
+
--docker-compose --pull-base --build \
|
|
4896
|
+
--dockerfile-name Dockerfile.dev \
|
|
4897
|
+
--image-name cyberia-client-dev:v3.2.70 \
|
|
4898
|
+
--image-out-path .
|
|
4899
|
+
`);
|
|
4900
|
+
break;
|
|
4901
|
+
}
|
|
4902
|
+
});
|
|
4903
|
+
|
|
4904
|
+
{
|
|
4905
|
+
// docker compose lyfe cycle commands for the dd-cyberia deployment
|
|
4906
|
+
const dockerComposeId = 'cyberia';
|
|
4907
|
+
const deployId = 'dd-cyberia';
|
|
4908
|
+
const commands = {
|
|
4909
|
+
'docker:generate': `node bin docker-compose --generate --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4910
|
+
'docker:up': `node bin docker-compose --up --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4911
|
+
'docker:up:build': `node bin docker-compose --up --build --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4912
|
+
'docker:down': `node bin docker-compose --down --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4913
|
+
'docker:down:volumes': `node bin docker-compose --down --volumes --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4914
|
+
'docker:restart': `node bin docker-compose --restart --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4915
|
+
'docker:pull': `node bin docker-compose --pull --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4916
|
+
'docker:logs': `node bin docker-compose --logs --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4917
|
+
'docker:status': `node bin docker-compose --status --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4918
|
+
'docker:reset': `node bin docker-compose --reset --deploy-id ${deployId} --docker-compose-id ${dockerComposeId}`,
|
|
4919
|
+
};
|
|
4920
|
+
for (const [cmd, action] of Object.entries(commands))
|
|
4921
|
+
runner.command(cmd).action(() => {
|
|
4922
|
+
shellExec(action);
|
|
4923
|
+
});
|
|
4924
|
+
}
|
|
4925
|
+
|
|
4234
4926
|
runner
|
|
4235
4927
|
.command('seed-dialogues')
|
|
4236
4928
|
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
@@ -4286,6 +4978,202 @@ try {
|
|
|
4286
4978
|
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
4287
4979
|
});
|
|
4288
4980
|
|
|
4981
|
+
runner
|
|
4982
|
+
.command('seed-actions-quests')
|
|
4983
|
+
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
4984
|
+
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
4985
|
+
.option('--dev', 'Force development environment')
|
|
4986
|
+
.description('Upsert DefaultCyberiaActions + DefaultCyberiaQuests into Mongo (idempotent)')
|
|
4987
|
+
.action(async (options) => {
|
|
4988
|
+
if (!options.envPath) options.envPath = `./.env`;
|
|
4989
|
+
if (fs.existsSync(options.envPath)) dotenv.config({ path: options.envPath, override: true });
|
|
4990
|
+
|
|
4991
|
+
if (options.dev && process.env.DEFAULT_DEPLOY_ID) {
|
|
4992
|
+
const devEnvPath = `./engine-private/conf/${process.env.DEFAULT_DEPLOY_ID}/.env.development`;
|
|
4993
|
+
if (fs.existsSync(devEnvPath)) dotenv.config({ path: devEnvPath, override: true });
|
|
4994
|
+
}
|
|
4995
|
+
|
|
4996
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
4997
|
+
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
4998
|
+
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
4999
|
+
|
|
5000
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
5001
|
+
if (!fs.existsSync(confServerPath)) {
|
|
5002
|
+
logger.error(`Server config not found: ${confServerPath}`);
|
|
5003
|
+
process.exit(1);
|
|
5004
|
+
}
|
|
5005
|
+
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
5006
|
+
const { db } = confServer[host][path];
|
|
5007
|
+
|
|
5008
|
+
db.host = options.mongoHost
|
|
5009
|
+
? options.mongoHost
|
|
5010
|
+
: options.dev
|
|
5011
|
+
? db.host
|
|
5012
|
+
: db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
|
|
5013
|
+
|
|
5014
|
+
logger.info('seed-actions-quests', { deployId, host, path, db });
|
|
5015
|
+
|
|
5016
|
+
await DataBaseProviderService.load({ apis: ['cyberia-action', 'cyberia-quest'], host, path, db });
|
|
5017
|
+
|
|
5018
|
+
const CyberiaAction = DataBaseProviderService.getModel('cyberia-action', { host, path });
|
|
5019
|
+
const CyberiaQuest = DataBaseProviderService.getModel('cyberia-quest', { host, path });
|
|
5020
|
+
|
|
5021
|
+
let actions = 0;
|
|
5022
|
+
for (const a of DefaultCyberiaActions) {
|
|
5023
|
+
await CyberiaAction.findOneAndUpdate({ code: a.code }, { $set: a }, { upsert: true });
|
|
5024
|
+
actions++;
|
|
5025
|
+
}
|
|
5026
|
+
let quests = 0;
|
|
5027
|
+
for (const q of DefaultCyberiaQuests) {
|
|
5028
|
+
await CyberiaQuest.findOneAndUpdate({ code: q.code }, { $set: q }, { upsert: true });
|
|
5029
|
+
quests++;
|
|
5030
|
+
}
|
|
5031
|
+
|
|
5032
|
+
logger.info(`seed-actions-quests: ${actions} actions, ${quests} quests upserted`);
|
|
5033
|
+
|
|
5034
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
5035
|
+
});
|
|
5036
|
+
|
|
5037
|
+
runner
|
|
5038
|
+
.command('seed-skills')
|
|
5039
|
+
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
5040
|
+
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
5041
|
+
.option('--dev', 'Force development environment')
|
|
5042
|
+
.description('Upsert DefaultSkillConfig into the cyberia-skill collection (full records, idempotent)')
|
|
5043
|
+
.action(async (options) => {
|
|
5044
|
+
if (!options.envPath) options.envPath = `./.env`;
|
|
5045
|
+
if (fs.existsSync(options.envPath)) dotenv.config({ path: options.envPath, override: true });
|
|
5046
|
+
|
|
5047
|
+
if (options.dev && process.env.DEFAULT_DEPLOY_ID) {
|
|
5048
|
+
const devEnvPath = `./engine-private/conf/${process.env.DEFAULT_DEPLOY_ID}/.env.development`;
|
|
5049
|
+
if (fs.existsSync(devEnvPath)) dotenv.config({ path: devEnvPath, override: true });
|
|
5050
|
+
}
|
|
5051
|
+
|
|
5052
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
5053
|
+
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
5054
|
+
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
5055
|
+
|
|
5056
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
5057
|
+
if (!fs.existsSync(confServerPath)) {
|
|
5058
|
+
logger.error(`Server config not found: ${confServerPath}`);
|
|
5059
|
+
process.exit(1);
|
|
5060
|
+
}
|
|
5061
|
+
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
5062
|
+
const { db } = confServer[host][path];
|
|
5063
|
+
|
|
5064
|
+
db.host = options.mongoHost
|
|
5065
|
+
? options.mongoHost
|
|
5066
|
+
: options.dev
|
|
5067
|
+
? db.host
|
|
5068
|
+
: db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
|
|
5069
|
+
|
|
5070
|
+
logger.info('seed-skills', { deployId, host, path, db });
|
|
5071
|
+
|
|
5072
|
+
await DataBaseProviderService.load({ apis: ['cyberia-skill'], host, path, db });
|
|
5073
|
+
|
|
5074
|
+
const CyberiaSkill = DataBaseProviderService.getModel('cyberia-skill', { host, path });
|
|
5075
|
+
|
|
5076
|
+
// Upsert each skill record keyed by triggerItemId — full record (logic
|
|
5077
|
+
// event keys + expanded skills metadata), unlike the instance-conf
|
|
5078
|
+
// skillConfig schema which keeps only triggerItemId + logicEventIds.
|
|
5079
|
+
let upserted = 0;
|
|
5080
|
+
for (const sk of DefaultSkillConfig) {
|
|
5081
|
+
await CyberiaSkill.findOneAndUpdate(
|
|
5082
|
+
{ triggerItemId: sk.triggerItemId },
|
|
5083
|
+
{ $set: { logicEventIds: sk.logicEventIds || [], skills: sk.skills || [] } },
|
|
5084
|
+
{ upsert: true },
|
|
5085
|
+
);
|
|
5086
|
+
upserted++;
|
|
5087
|
+
}
|
|
5088
|
+
|
|
5089
|
+
logger.info(
|
|
5090
|
+
`seed-skills: ${upserted} skill records upserted`,
|
|
5091
|
+
DefaultSkillConfig.map((e) => `${e.triggerItemId} → [${(e.logicEventIds || []).join(', ')}]`),
|
|
5092
|
+
);
|
|
5093
|
+
|
|
5094
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
5095
|
+
});
|
|
5096
|
+
|
|
5097
|
+
runner
|
|
5098
|
+
.command('seed-entities')
|
|
5099
|
+
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
5100
|
+
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
5101
|
+
.option('--dev', 'Force development environment')
|
|
5102
|
+
.description('Upsert ENTITY_TYPE_DEFAULTS into the cyberia-entity-type-default collection (idempotent)')
|
|
5103
|
+
.action(async (options) => {
|
|
5104
|
+
if (!options.envPath) options.envPath = `./.env`;
|
|
5105
|
+
if (fs.existsSync(options.envPath)) dotenv.config({ path: options.envPath, override: true });
|
|
5106
|
+
|
|
5107
|
+
if (options.dev && process.env.DEFAULT_DEPLOY_ID) {
|
|
5108
|
+
const devEnvPath = `./engine-private/conf/${process.env.DEFAULT_DEPLOY_ID}/.env.development`;
|
|
5109
|
+
if (fs.existsSync(devEnvPath)) dotenv.config({ path: devEnvPath, override: true });
|
|
5110
|
+
}
|
|
5111
|
+
|
|
5112
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
5113
|
+
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
5114
|
+
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
5115
|
+
|
|
5116
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
5117
|
+
if (!fs.existsSync(confServerPath)) {
|
|
5118
|
+
logger.error(`Server config not found: ${confServerPath}`);
|
|
5119
|
+
process.exit(1);
|
|
5120
|
+
}
|
|
5121
|
+
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
5122
|
+
const { db } = confServer[host][path];
|
|
5123
|
+
|
|
5124
|
+
db.host = options.mongoHost
|
|
5125
|
+
? options.mongoHost
|
|
5126
|
+
: options.dev
|
|
5127
|
+
? db.host
|
|
5128
|
+
: db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
|
|
5129
|
+
|
|
5130
|
+
logger.info('seed-entities', { deployId, host, path, db });
|
|
5131
|
+
|
|
5132
|
+
await DataBaseProviderService.load({ apis: ['cyberia-entity-type-default'], host, path, db });
|
|
5133
|
+
|
|
5134
|
+
const CyberiaEntityTypeDefault = DataBaseProviderService.getModel('cyberia-entity-type-default', { host, path });
|
|
5135
|
+
|
|
5136
|
+
// Reconcile DB indexes with the current schema. This drops the obsolete
|
|
5137
|
+
// unique (entityType, liveItemIds) index from earlier builds so the same
|
|
5138
|
+
// itemId may appear in multiple same-type defaults (subset matching), and
|
|
5139
|
+
// (re)creates the non-unique liveItemIds lookup index.
|
|
5140
|
+
try {
|
|
5141
|
+
await CyberiaEntityTypeDefault.syncIndexes();
|
|
5142
|
+
} catch (error) {
|
|
5143
|
+
logger.warn(`seed-entities: syncIndexes skipped: ${error?.message || error}`);
|
|
5144
|
+
}
|
|
5145
|
+
|
|
5146
|
+
// Resolution is by subset containment (most-specific match wins), so there
|
|
5147
|
+
// is NO per-itemId uniqueness — the same itemId may appear in many entries
|
|
5148
|
+
// (across entity types, or within one type at different specificity). Every
|
|
5149
|
+
// entry is upserted, idempotently, by its exact (entityType, liveItemIds) key.
|
|
5150
|
+
let upserted = 0;
|
|
5151
|
+
for (const ed of ENTITY_TYPE_DEFAULTS) {
|
|
5152
|
+
await CyberiaEntityTypeDefault.findOneAndUpdate(
|
|
5153
|
+
{ entityType: ed.entityType, liveItemIds: ed.liveItemIds || [] },
|
|
5154
|
+
{
|
|
5155
|
+
$set: {
|
|
5156
|
+
entityType: ed.entityType,
|
|
5157
|
+
liveItemIds: ed.liveItemIds || [],
|
|
5158
|
+
deadItemIds: ed.deadItemIds || [],
|
|
5159
|
+
dropItemIds: ed.dropItemIds || [],
|
|
5160
|
+
defaultObjectLayers: ed.defaultObjectLayers || [],
|
|
5161
|
+
behavior: ed.behavior || '',
|
|
5162
|
+
},
|
|
5163
|
+
},
|
|
5164
|
+
{ upsert: true },
|
|
5165
|
+
);
|
|
5166
|
+
upserted++;
|
|
5167
|
+
}
|
|
5168
|
+
|
|
5169
|
+
logger.info(
|
|
5170
|
+
`seed-entities: ${upserted} entity-type-default records upserted`,
|
|
5171
|
+
ENTITY_TYPE_DEFAULTS.map((e) => `${e.entityType} → [${(e.liveItemIds || []).join(', ')}]`),
|
|
5172
|
+
);
|
|
5173
|
+
|
|
5174
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
5175
|
+
});
|
|
5176
|
+
|
|
4289
5177
|
runner
|
|
4290
5178
|
.command('generate-semantic-examples')
|
|
4291
5179
|
.option('--seed <seed>', 'Base seed string (each type gets a unique suffix appended)', 'example')
|
|
@@ -4369,6 +5257,14 @@ try {
|
|
|
4369
5257
|
// Edit the canonical sources; never hand-edit these generated outputs.
|
|
4370
5258
|
fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-CLIENT.md', './cyberia-client/README.md');
|
|
4371
5259
|
fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-SERVER.md', './cyberia-server/README.md');
|
|
5260
|
+
fs.copyFileSync(
|
|
5261
|
+
'./.github/workflows/cyberia-client.cd.yml',
|
|
5262
|
+
'./cyberia-client/.github/workflows/cyberia-client.cd.yml',
|
|
5263
|
+
);
|
|
5264
|
+
fs.copyFileSync(
|
|
5265
|
+
'./.github/workflows/cyberia-server.cd.yml',
|
|
5266
|
+
'./cyberia-server/.github/workflows/cyberia-server.cd.yml',
|
|
5267
|
+
);
|
|
4372
5268
|
logger.info(`run-workflow build-manifest complete (${isDev ? 'dev' : 'prod'})`);
|
|
4373
5269
|
});
|
|
4374
5270
|
|