cyberia 3.2.5 → 3.2.12
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/.github/workflows/engine-cyberia.cd.yml +8 -2
- package/.github/workflows/npmpkg.ci.yml +1 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +2 -2
- package/.vscode/extensions.json +9 -9
- package/.vscode/settings.json +20 -4
- package/CHANGELOG.md +563 -1
- package/CLI-HELP.md +130 -34
- package/Dockerfile +0 -4
- package/README.md +194 -607
- package/bin/build.js +42 -12
- package/bin/build.template.js +187 -0
- package/bin/cyberia.js +1367 -281
- package/bin/deploy.js +582 -3
- package/bin/index.js +1367 -281
- package/bump.config.js +26 -0
- package/conf.js +195 -111
- package/deployment.yaml +6 -222
- package/hardhat/package-lock.json +118 -149
- package/hardhat/package.json +5 -4
- package/jsconfig.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -2
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +6 -222
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +10 -118
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
- package/manifests/deployment/dd-test-development/deployment.yaml +138 -66
- package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
- package/manifests/kind-config-dev.yaml +8 -0
- package/manifests/lxd/lxd-admin-profile.yaml +12 -3
- package/manifests/mongodb/pv-pvc.yaml +44 -8
- package/manifests/mongodb/statefulset.yaml +55 -68
- package/manifests/mongodb-4.4/headless-service.yaml +10 -0
- package/manifests/mongodb-4.4/kustomization.yaml +3 -1
- package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
- package/manifests/mongodb-4.4/statefulset.yaml +79 -0
- package/manifests/mongodb-4.4/storage-class.yaml +9 -0
- package/manifests/valkey/statefulset.yaml +1 -1
- package/manifests/valkey/valkey-nodeport.yaml +17 -0
- package/package.json +45 -24
- package/proxy.yaml +10 -118
- package/scripts/ipxe-setup.sh +52 -49
- package/scripts/k3s-node-setup.sh +83 -48
- package/scripts/lxd-vm-setup.sh +193 -8
- package/scripts/maas-nat-firewalld.sh +145 -0
- package/scripts/nat-iptables.sh +103 -18
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +18 -18
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +7 -14
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +38 -33
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +91 -36
- package/src/api/core/core.controller.js +10 -10
- package/src/api/core/core.router.js +19 -14
- package/src/api/core/core.service.js +15 -15
- package/src/api/crypto/crypto.controller.js +8 -8
- package/src/api/crypto/crypto.router.js +18 -12
- package/src/api/crypto/crypto.service.js +11 -11
- package/src/api/cyberia-action/cyberia-action.controller.js +74 -0
- package/src/api/cyberia-action/cyberia-action.model.js +87 -0
- package/src/api/cyberia-action/cyberia-action.router.js +31 -0
- package/src/api/cyberia-action/cyberia-action.service.js +42 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +74 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +99 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +98 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +13 -13
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +11 -11
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +25 -20
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +22 -22
- package/src/api/cyberia-entity/cyberia-entity.controller.js +10 -10
- package/src/api/cyberia-entity/cyberia-entity.router.js +22 -18
- package/src/api/cyberia-entity/cyberia-entity.service.js +15 -15
- package/src/api/cyberia-instance/cyberia-fallback-world.js +83 -198
- package/src/api/cyberia-instance/cyberia-instance.controller.js +14 -14
- package/src/api/cyberia-instance/cyberia-instance.model.js +3 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +57 -52
- package/src/api/cyberia-instance/cyberia-instance.service.js +32 -67
- package/src/api/cyberia-instance/cyberia-portal-connector.js +20 -246
- package/src/api/cyberia-instance/cyberia-world-generator.js +505 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +10 -10
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +18 -49
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +22 -18
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +19 -15
- package/src/api/cyberia-map/cyberia-map.controller.js +10 -10
- package/src/api/cyberia-map/cyberia-map.router.js +35 -30
- package/src/api/cyberia-map/cyberia-map.service.js +17 -17
- package/src/api/cyberia-quest/cyberia-quest.controller.js +74 -0
- package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +31 -0
- package/src/api/cyberia-quest/cyberia-quest.service.js +42 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +74 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +31 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +451 -0
- package/src/api/default/default.controller.js +10 -10
- package/src/api/default/default.router.js +22 -18
- package/src/api/default/default.service.js +15 -15
- package/src/api/document/document.controller.js +12 -12
- package/src/api/document/document.model.js +10 -16
- package/src/api/document/document.router.js +28 -23
- package/src/api/document/document.service.js +100 -23
- package/src/api/file/file.controller.js +8 -8
- package/src/api/file/file.model.js +10 -10
- package/src/api/file/file.router.js +19 -13
- package/src/api/file/file.service.js +45 -43
- package/src/api/instance/instance.controller.js +10 -10
- package/src/api/instance/instance.model.js +4 -10
- package/src/api/instance/instance.router.js +29 -24
- package/src/api/instance/instance.service.js +16 -16
- package/src/api/ipfs/ipfs.controller.js +12 -12
- package/src/api/ipfs/ipfs.model.js +4 -13
- package/src/api/ipfs/ipfs.router.js +21 -16
- package/src/api/ipfs/ipfs.service.js +22 -36
- package/src/api/object-layer/object-layer.controller.js +12 -12
- package/src/api/object-layer/object-layer.model.js +4 -17
- package/src/api/object-layer/object-layer.router.js +512 -507
- package/src/api/object-layer/object-layer.service.js +29 -26
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +10 -10
- package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +6 -16
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +22 -18
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +19 -15
- package/src/api/test/test.controller.js +8 -8
- package/src/api/test/test.router.js +17 -12
- package/src/api/test/test.service.js +8 -8
- package/src/api/types.js +24 -0
- package/src/api/user/guest.service.js +100 -0
- package/src/api/user/user.controller.js +6 -6
- package/src/api/user/user.model.js +8 -13
- package/src/api/user/user.router.js +297 -288
- package/src/api/user/user.service.js +103 -55
- package/src/cli/baremetal.js +132 -101
- package/src/cli/cluster.js +732 -217
- package/src/cli/db.js +106 -62
- package/src/cli/deploy.js +260 -149
- package/src/cli/fs.js +90 -9
- package/src/cli/image.js +43 -1
- package/src/cli/index.js +106 -16
- package/src/cli/ipfs.js +4 -6
- package/src/cli/kubectl.js +4 -1
- package/src/cli/lxd.js +1099 -223
- package/src/cli/monitor.js +9 -3
- package/src/cli/release.js +336 -86
- package/src/cli/repository.js +136 -53
- package/src/cli/run.js +599 -76
- package/src/cli/secrets.js +11 -2
- package/src/cli/ssh.js +1 -1
- package/src/cli/static.js +43 -115
- package/src/cli/test.js +9 -3
- package/src/client/Cryptokoyn.index.js +18 -21
- package/src/client/CyberiaPortal.index.js +19 -23
- package/src/client/Default.index.js +30 -36
- package/src/client/Itemledger.index.js +20 -26
- package/src/client/Underpost.index.js +19 -23
- package/src/client/components/core/404.js +4 -4
- package/src/client/components/core/500.js +4 -4
- package/src/client/components/core/Account.js +73 -60
- package/src/client/components/core/AgGrid.js +23 -33
- package/src/client/components/core/Alert.js +12 -13
- package/src/client/components/core/AppStore.js +1 -1
- package/src/client/components/core/Auth.js +40 -37
- package/src/client/components/core/Badge.js +7 -13
- package/src/client/components/core/BtnIcon.js +15 -17
- package/src/client/components/core/CalendarCore.js +42 -63
- package/src/client/components/core/Chat.js +13 -15
- package/src/client/components/core/ClientEvents.js +163 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/Content.js +17 -14
- package/src/client/components/core/Css.js +15 -71
- package/src/client/components/core/CssCore.js +12 -16
- package/src/client/components/core/D3Chart.js +4 -4
- package/src/client/components/core/Docs.js +64 -91
- package/src/client/components/core/DropDown.js +69 -91
- package/src/client/components/core/EventBus.js +96 -0
- package/src/client/components/core/EventsUI.js +14 -17
- package/src/client/components/core/FileExplorer.js +96 -228
- package/src/client/components/core/FullScreen.js +47 -75
- package/src/client/components/core/Input.js +24 -69
- package/src/client/components/core/Keyboard.js +25 -18
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +25 -31
- package/src/client/components/core/LogIn.js +41 -41
- package/src/client/components/core/LogOut.js +23 -14
- package/src/client/components/core/Modal.js +544 -219
- package/src/client/components/core/NotificationManager.js +14 -18
- package/src/client/components/core/Panel.js +54 -50
- package/src/client/components/core/PanelForm.js +81 -177
- package/src/client/components/core/Polyhedron.js +110 -214
- package/src/client/components/core/PublicProfile.js +39 -32
- package/src/client/components/core/Recover.js +48 -44
- package/src/client/components/core/Responsive.js +88 -32
- package/src/client/components/core/RichText.js +9 -18
- package/src/client/components/core/Router.js +24 -3
- package/src/client/components/core/SearchBox.js +37 -37
- package/src/client/components/core/SignUp.js +39 -30
- package/src/client/components/core/SocketIo.js +31 -2
- package/src/client/components/core/SocketIoHandler.js +6 -6
- package/src/client/components/core/ToggleSwitch.js +8 -20
- package/src/client/components/core/ToolTip.js +5 -17
- package/src/client/components/core/Translate.js +56 -59
- package/src/client/components/core/Validator.js +26 -16
- package/src/client/components/core/Wallet.js +15 -26
- package/src/client/components/core/Worker.js +211 -276
- package/src/client/components/core/windowGetDimensions.js +7 -7
- package/src/client/components/cryptokoyn/{MenuCryptokoyn.js → AppShellCryptokoyn.js} +57 -57
- package/src/client/components/cryptokoyn/CssCryptokoyn.js +15 -15
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +6 -4
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +6 -4
- package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +4 -4
- package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +6 -4
- package/src/client/components/cyberia/InstanceEngineCyberia.js +141 -60
- package/src/client/components/cyberia/MapEngineCyberia.js +691 -214
- package/src/client/components/cyberia/ObjectLayerEngine.js +19 -0
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +1204 -94
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +196 -298
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +330 -0
- package/src/client/components/cyberia-portal/{MenuCyberiaPortal.js → AppShellCyberiaPortal.js} +102 -102
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +15 -15
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +6 -4
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +6 -4
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +4 -4
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +60 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +4 -4
- package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +6 -4
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -4
- package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
- package/src/client/components/default/CssDefault.js +12 -12
- package/src/client/components/default/LogInDefault.js +6 -4
- package/src/client/components/default/LogOutDefault.js +6 -4
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +4 -4
- package/src/client/components/default/SignUpDefault.js +6 -4
- package/src/client/components/default/TranslateDefault.js +3 -3
- package/src/client/components/itemledger/{MenuItemledger.js → AppShellItemledger.js} +57 -57
- package/src/client/components/itemledger/CssItemledger.js +15 -15
- package/src/client/components/itemledger/LogInItemledger.js +6 -4
- package/src/client/components/itemledger/LogOutItemledger.js +6 -4
- package/src/client/components/itemledger/RouterItemledger.js +38 -0
- package/src/client/components/itemledger/SettingsItemledger.js +4 -4
- package/src/client/components/itemledger/SignUpItemledger.js +6 -4
- package/src/client/components/itemledger/TranslateItemledger.js +3 -3
- package/src/client/components/underpost/{MenuUnderpost.js → AppShellUnderpost.js} +88 -88
- package/src/client/components/underpost/CssUnderpost.js +14 -14
- package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +4 -4
- package/src/client/components/underpost/DocumentSearchProvider.js +1 -1
- package/src/client/components/underpost/LabGalleryUnderpost.js +12 -15
- package/src/client/components/underpost/LogInUnderpost.js +6 -4
- package/src/client/components/underpost/LogOutUnderpost.js +6 -4
- package/src/client/components/underpost/RouterUnderpost.js +45 -0
- package/src/client/components/underpost/SettingsUnderpost.js +4 -4
- package/src/client/components/underpost/SignUpUnderpost.js +6 -4
- package/src/client/components/underpost/TranslateUnderpost.js +4 -4
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +235 -0
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +83 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +204 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +291 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +278 -0
- package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +241 -0
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
- package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +279 -0
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +206 -0
- package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
- package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +732 -0
- package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +14 -20
- package/src/client/services/core/core.service.js +17 -49
- package/src/client/services/crypto/crypto.service.js +8 -13
- package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
- package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
- package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +10 -16
- package/src/client/services/cyberia-entity/cyberia-entity.management.js +5 -5
- package/src/client/services/cyberia-entity/cyberia-entity.service.js +10 -16
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +6 -6
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +12 -18
- package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +10 -16
- package/src/client/services/cyberia-map/cyberia-map.management.js +6 -6
- package/src/client/services/cyberia-map/cyberia-map.service.js +12 -18
- package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
- package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
- package/src/client/services/default/default.management.js +159 -267
- package/src/client/services/default/default.service.js +10 -16
- package/src/client/services/document/document.service.js +14 -19
- package/src/client/services/file/file.service.js +8 -13
- package/src/client/services/instance/instance.management.js +5 -5
- package/src/client/services/instance/instance.service.js +10 -15
- package/src/client/services/ipfs/ipfs.service.js +12 -18
- package/src/client/services/object-layer/object-layer.management.js +12 -12
- package/src/client/services/object-layer/object-layer.service.js +20 -26
- package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +10 -16
- package/src/client/services/test/test.service.js +8 -13
- package/src/client/services/user/guest.service.js +86 -0
- package/src/client/services/user/user.management.js +5 -5
- package/src/client/services/user/user.service.js +14 -20
- package/src/client/ssr/body/404.js +3 -3
- package/src/client/ssr/body/500.js +3 -3
- package/src/client/ssr/body/CacheControl.js +5 -2
- package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +13 -6
- package/src/client/ssr/head/PwaItemledger.js +197 -60
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
- package/src/client/ssr/views/CyberiaServerMetrics.js +982 -0
- package/src/client/ssr/{offline → views}/Maintenance.js +12 -11
- package/src/client/ssr/{offline → views}/NoNetworkConnection.js +3 -3
- package/src/client/ssr/{pages → views}/Test.js +2 -2
- package/src/client/sw/core.sw.js +274 -0
- package/src/db/DataBaseProvider.js +115 -15
- package/src/db/mariadb/MariaDB.js +2 -1
- package/src/db/mongo/MongoBootstrap.js +657 -0
- package/src/db/mongo/MongooseDB.js +129 -21
- package/src/grpc/cyberia/grpc-server.js +185 -105
- package/src/index.js +1 -1
- package/src/runtime/cyberia-client/Dockerfile +101 -0
- package/src/runtime/cyberia-client/Dockerfile.dev +82 -0
- package/src/runtime/cyberia-server/Dockerfile +62 -0
- package/src/runtime/cyberia-server/Dockerfile.dev +71 -0
- package/src/runtime/express/Dockerfile +4 -4
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/lampp/Dockerfile +8 -7
- package/src/runtime/wp/Dockerfile +11 -17
- package/src/runtime/wp/Wp.js +8 -5
- package/src/server/atlas-sprite-sheet-generator.js +4 -2
- package/src/server/auth.js +2 -2
- package/src/server/client-build-docs.js +46 -47
- package/src/server/client-build.js +371 -132
- package/src/server/client-formatted.js +47 -16
- package/src/server/conf.js +91 -87
- package/src/server/data-query.js +32 -20
- package/src/server/dns.js +22 -0
- package/src/server/ipfs-client.js +232 -91
- package/src/server/object-layer.js +1 -6
- package/src/server/process.js +192 -45
- package/src/server/proxy.js +9 -2
- package/src/server/runtime.js +1 -1
- package/src/server/semantic-layer-generator-floor.js +11 -51
- package/src/server/semantic-layer-generator-resource.js +259 -0
- package/src/server/semantic-layer-generator-skin.js +41 -171
- package/src/server/semantic-layer-generator.js +122 -14
- package/src/server/shape-generator.js +108 -0
- package/src/server/start.js +34 -8
- package/src/server/valkey.js +143 -235
- package/src/ws/IoInterface.js +16 -16
- package/src/ws/core/channels/core.ws.chat.js +11 -11
- package/src/ws/core/channels/core.ws.mailer.js +29 -29
- package/src/ws/core/channels/core.ws.stream.js +19 -19
- package/src/ws/core/core.ws.connection.js +8 -8
- package/src/ws/core/core.ws.server.js +6 -5
- package/src/ws/default/channels/default.ws.main.js +10 -10
- package/src/ws/default/default.ws.connection.js +4 -4
- package/src/ws/default/default.ws.server.js +4 -3
- package/tsconfig.docs.json +15 -0
- package/typedoc.dd-cyberia.json +29 -0
- package/typedoc.json +29 -0
- package/WHITE-PAPER.md +0 -1540
- package/bin/file.js +0 -196
- package/bin/vs.js +0 -74
- package/bin/zed.js +0 -84
- package/hardhat/README.md +0 -531
- package/hardhat/WHITE-PAPER.md +0 -1540
- package/jsdoc.dd-cyberia.json +0 -68
- package/jsdoc.json +0 -68
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +0 -413
- package/src/api/object-layer/README.md +0 -672
- package/src/client/components/core/ColorPalette.js +0 -5267
- package/src/client/components/core/JoyStick.js +0 -80
- package/src/client/components/cryptokoyn/RoutesCryptokoyn.js +0 -39
- package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +0 -223
- package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +0 -62
- package/src/client/components/cyberia-portal/ServerCyberiaPortal.js +0 -136
- package/src/client/components/default/RoutesDefault.js +0 -49
- package/src/client/components/itemledger/RoutesItemledger.js +0 -40
- package/src/client/components/underpost/RoutesUnderpost.js +0 -47
- package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
- package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
- package/src/client/ssr/pages/CyberiaServerMetrics.js +0 -461
- package/src/client/sw/default.sw.js +0 -127
- package/src/client/sw/template.sw.js +0 -84
- package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +0 -305
- package/src/grpc/cyberia/README.md +0 -326
package/bin/index.js
CHANGED
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
import dotenv from 'dotenv';
|
|
17
17
|
import { Command } from 'commander';
|
|
18
18
|
import fs from 'fs-extra';
|
|
19
|
+
import stringify from 'fast-json-stable-stringify';
|
|
19
20
|
import { shellExec } from '../src/server/process.js';
|
|
20
21
|
import { loggerFactory } from '../src/server/logger.js';
|
|
21
22
|
import { generateBesuManifests, deployBesu, removeBesu } from '../src/server/besu-genesis-generator.js';
|
|
22
|
-
import {
|
|
23
|
+
import { DataBaseProviderService } from '../src/db/DataBaseProvider.js';
|
|
23
24
|
import { loadConfServerJson } from '../src/server/conf.js';
|
|
24
25
|
import {
|
|
25
26
|
ObjectLayerEngine,
|
|
@@ -28,25 +29,23 @@ import {
|
|
|
28
29
|
getKeyFramesDirectionsFromNumberFolderDirection,
|
|
29
30
|
buildImgFromTile,
|
|
30
31
|
} from '../src/server/object-layer.js';
|
|
31
|
-
import { ITEM_TYPES as itemTypes } from '../src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js';
|
|
32
32
|
import { AtlasSpriteSheetGenerator } from '../src/server/atlas-sprite-sheet-generator.js';
|
|
33
|
-
import {
|
|
34
|
-
generateFrame,
|
|
35
|
-
generateMultiFrame,
|
|
36
|
-
lookupSemantic,
|
|
37
|
-
semanticRegistry,
|
|
38
|
-
} from '../src/server/semantic-layer-generator.js';
|
|
33
|
+
import { generateMultiFrame, lookupSemantic, semanticRegistry } from '../src/server/semantic-layer-generator.js';
|
|
39
34
|
import { IpfsClient } from '../src/server/ipfs-client.js';
|
|
40
35
|
import { createPinRecord } from '../src/api/ipfs/ipfs.service.js';
|
|
41
36
|
import { program as underpostProgram } from '../src/cli/index.js';
|
|
42
37
|
import crypto from 'crypto';
|
|
43
38
|
import nodePath from 'path';
|
|
44
39
|
import Underpost from '../src/index.js';
|
|
40
|
+
import { newInstance } from '../src/client/components/core/CommonJs.js';
|
|
45
41
|
import {
|
|
42
|
+
ITEM_TYPES as itemTypes,
|
|
46
43
|
DefaultCyberiaItems,
|
|
47
44
|
DefaultSkillConfig,
|
|
48
45
|
DefaultCyberiaDialogues,
|
|
49
|
-
|
|
46
|
+
DefaultCyberiaActions,
|
|
47
|
+
DefaultCyberiaQuests,
|
|
48
|
+
} from '../src/api/cyberia-server-defaults/cyberia-server-defaults.js';
|
|
50
49
|
|
|
51
50
|
/**
|
|
52
51
|
* Connect to the project MongoDB instance using the standard env / conf layout.
|
|
@@ -73,14 +72,14 @@ async function connectDbForChain({ envPath, mongoHost }) {
|
|
|
73
72
|
|
|
74
73
|
db.host = mongoHost ? mongoHost : db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
|
|
75
74
|
|
|
76
|
-
await
|
|
75
|
+
await DataBaseProviderService.load({
|
|
77
76
|
apis: ['object-layer'],
|
|
78
77
|
host,
|
|
79
78
|
path,
|
|
80
79
|
db,
|
|
81
80
|
});
|
|
82
81
|
|
|
83
|
-
const ObjectLayer =
|
|
82
|
+
const ObjectLayer = DataBaseProviderService.getModel('object-layer', { host, path });
|
|
84
83
|
return { ObjectLayer, host, path };
|
|
85
84
|
}
|
|
86
85
|
|
|
@@ -214,7 +213,7 @@ try {
|
|
|
214
213
|
db,
|
|
215
214
|
});
|
|
216
215
|
|
|
217
|
-
await
|
|
216
|
+
await DataBaseProviderService.load({
|
|
218
217
|
apis: ['object-layer', 'object-layer-render-frames', 'atlas-sprite-sheet', 'file', 'ipfs'],
|
|
219
218
|
host,
|
|
220
219
|
path,
|
|
@@ -222,16 +221,15 @@ try {
|
|
|
222
221
|
});
|
|
223
222
|
|
|
224
223
|
/** @type {import('mongoose').Model} */
|
|
225
|
-
const ObjectLayer =
|
|
224
|
+
const ObjectLayer = DataBaseProviderService.getModel('object-layer', { host, path });
|
|
226
225
|
/** @type {import('mongoose').Model} */
|
|
227
|
-
const ObjectLayerRenderFrames =
|
|
228
|
-
DataBaseProvider.instance[`${host}${path}`].mongoose.models.ObjectLayerRenderFrames;
|
|
226
|
+
const ObjectLayerRenderFrames = DataBaseProviderService.getModel('object-layer-render-frames', { host, path });
|
|
229
227
|
/** @type {import('mongoose').Model} */
|
|
230
|
-
const AtlasSpriteSheet =
|
|
228
|
+
const AtlasSpriteSheet = DataBaseProviderService.getModel('atlas-sprite-sheet', { host, path });
|
|
231
229
|
/** @type {import('mongoose').Model} */
|
|
232
|
-
const File =
|
|
230
|
+
const File = DataBaseProviderService.getModel('file', { host, path });
|
|
233
231
|
/** @type {import('mongoose').Model} */
|
|
234
|
-
const Ipfs =
|
|
232
|
+
const Ipfs = DataBaseProviderService.getModel('ipfs', { host, path });
|
|
235
233
|
|
|
236
234
|
if (options.drop) {
|
|
237
235
|
// Parse comma-separated item IDs for targeted drop; if none provided, drop everything
|
|
@@ -1695,7 +1693,7 @@ try {
|
|
|
1695
1693
|
}
|
|
1696
1694
|
}
|
|
1697
1695
|
|
|
1698
|
-
await
|
|
1696
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
1699
1697
|
},
|
|
1700
1698
|
)
|
|
1701
1699
|
.description('Object layer management');
|
|
@@ -1705,7 +1703,11 @@ try {
|
|
|
1705
1703
|
.command('instance [instance-code]')
|
|
1706
1704
|
.option('--export [path]', 'Export instance and related documents to a backup directory')
|
|
1707
1705
|
.option('--import [path]', 'Import instance and related documents from a backup directory (preserveUUID, upsert)')
|
|
1708
|
-
.option(
|
|
1706
|
+
.option(
|
|
1707
|
+
'--conf',
|
|
1708
|
+
'When used with --export or --import, only process cyberia-instance.json and cyberia-instance-conf.json',
|
|
1709
|
+
)
|
|
1710
|
+
.option('--drop', 'Drop all documents associated with the instance code before importing or as a standalone action')
|
|
1709
1711
|
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
1710
1712
|
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
1711
1713
|
.option('--dev', 'Force development environment')
|
|
@@ -1746,9 +1748,11 @@ try {
|
|
|
1746
1748
|
|
|
1747
1749
|
logger.info('instance env', { env: options.envPath, deployId, host, path, db });
|
|
1748
1750
|
|
|
1749
|
-
await
|
|
1751
|
+
await DataBaseProviderService.load({
|
|
1750
1752
|
apis: [
|
|
1751
1753
|
'cyberia-instance',
|
|
1754
|
+
'cyberia-instance-conf',
|
|
1755
|
+
'cyberia-dialogue',
|
|
1752
1756
|
'cyberia-map',
|
|
1753
1757
|
'cyberia-entity',
|
|
1754
1758
|
'object-layer',
|
|
@@ -1762,21 +1766,150 @@ try {
|
|
|
1762
1766
|
db,
|
|
1763
1767
|
});
|
|
1764
1768
|
|
|
1765
|
-
const
|
|
1766
|
-
const
|
|
1767
|
-
const
|
|
1768
|
-
const
|
|
1769
|
-
const
|
|
1770
|
-
const
|
|
1771
|
-
const
|
|
1772
|
-
const
|
|
1769
|
+
const CyberiaInstance = DataBaseProviderService.getModel('cyberia-instance', { host, path });
|
|
1770
|
+
const CyberiaInstanceConf = DataBaseProviderService.getModel('cyberia-instance-conf', { host, path });
|
|
1771
|
+
const CyberiaDialogue = DataBaseProviderService.getModel('cyberia-dialogue', { host, path });
|
|
1772
|
+
const CyberiaMap = DataBaseProviderService.getModel('cyberia-map', { host, path });
|
|
1773
|
+
const ObjectLayer = DataBaseProviderService.getModel('object-layer', { host, path });
|
|
1774
|
+
const ObjectLayerRenderFrames = DataBaseProviderService.getModel('object-layer-render-frames', { host, path });
|
|
1775
|
+
const AtlasSpriteSheet = DataBaseProviderService.getModel('atlas-sprite-sheet', { host, path });
|
|
1776
|
+
const File = DataBaseProviderService.getModel('file', { host, path });
|
|
1777
|
+
const Ipfs = DataBaseProviderService.getModel('ipfs', { host, path });
|
|
1778
|
+
|
|
1779
|
+
const toBuffer = (value) => {
|
|
1780
|
+
if (!value) return null;
|
|
1781
|
+
if (Buffer.isBuffer(value)) return value;
|
|
1782
|
+
if (value.type === 'Buffer' && Array.isArray(value.data)) return Buffer.from(value.data);
|
|
1783
|
+
if (value.buffer) return Buffer.from(value.buffer);
|
|
1784
|
+
return Buffer.from(value);
|
|
1785
|
+
};
|
|
1786
|
+
|
|
1787
|
+
const getCanonicalIpfsPaths = (itemKey) => ({
|
|
1788
|
+
objectLayerData: `/object-layer/${itemKey}/${itemKey}_data.json`,
|
|
1789
|
+
atlasSpriteSheet: `/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet.png`,
|
|
1790
|
+
atlasMetadata: `/object-layer/${itemKey}/${itemKey}_atlas_sprite_sheet_metadata.json`,
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
const collectMfsPaths = (doc = {}) => {
|
|
1794
|
+
const paths = new Set();
|
|
1795
|
+
if (doc.mfsPath) paths.add(doc.mfsPath);
|
|
1796
|
+
for (const p of doc.mfsPaths || []) {
|
|
1797
|
+
if (p) paths.add(p);
|
|
1798
|
+
}
|
|
1799
|
+
return [...paths];
|
|
1800
|
+
};
|
|
1801
|
+
|
|
1802
|
+
const inferResourceType = (doc = {}) => {
|
|
1803
|
+
if (doc.resourceType) return doc.resourceType;
|
|
1804
|
+
for (const path of collectMfsPaths(doc)) {
|
|
1805
|
+
if (path.endsWith('_atlas_sprite_sheet.png')) return 'atlas-sprite-sheet';
|
|
1806
|
+
if (path.endsWith('_atlas_sprite_sheet_metadata.json')) return 'atlas-metadata';
|
|
1807
|
+
if (path.endsWith('_data.json')) return 'object-layer-data';
|
|
1808
|
+
}
|
|
1809
|
+
return null;
|
|
1810
|
+
};
|
|
1811
|
+
|
|
1812
|
+
const findInstanceRelatedIpfsDoc = (ipfsDocs, { linkedCid, resourceType, mfsPath }) =>
|
|
1813
|
+
ipfsDocs.find(
|
|
1814
|
+
(doc) =>
|
|
1815
|
+
inferResourceType(doc) === resourceType &&
|
|
1816
|
+
linkedCid &&
|
|
1817
|
+
doc.cid === linkedCid &&
|
|
1818
|
+
collectMfsPaths(doc).includes(mfsPath),
|
|
1819
|
+
) ||
|
|
1820
|
+
ipfsDocs.find((doc) => inferResourceType(doc) === resourceType && linkedCid && doc.cid === linkedCid) ||
|
|
1821
|
+
ipfsDocs.find((doc) => inferResourceType(doc) === resourceType && collectMfsPaths(doc).includes(mfsPath)) ||
|
|
1822
|
+
null;
|
|
1823
|
+
|
|
1824
|
+
const upsertCanonicalPinEntry = (pinMap, { cid, resourceType, mfsPath = '' }) => {
|
|
1825
|
+
if (!cid || !resourceType) return;
|
|
1826
|
+
const key = `${resourceType}:${cid}`;
|
|
1827
|
+
const nextPath = mfsPath || '';
|
|
1828
|
+
if (!pinMap.has(key)) {
|
|
1829
|
+
pinMap.set(key, {
|
|
1830
|
+
cid,
|
|
1831
|
+
resourceType,
|
|
1832
|
+
mfsPath: nextPath,
|
|
1833
|
+
mfsPaths: nextPath ? [nextPath] : [],
|
|
1834
|
+
});
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
const existing = pinMap.get(key);
|
|
1839
|
+
if (nextPath && !existing.mfsPaths.includes(nextPath)) {
|
|
1840
|
+
existing.mfsPaths.push(nextPath);
|
|
1841
|
+
}
|
|
1842
|
+
if (!existing.mfsPath && nextPath) {
|
|
1843
|
+
existing.mfsPath = nextPath;
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
const serialiseCanonicalPins = (pinMap) =>
|
|
1848
|
+
[...pinMap.values()].map((entry) => ({
|
|
1849
|
+
cid: entry.cid,
|
|
1850
|
+
resourceType: entry.resourceType,
|
|
1851
|
+
...(entry.mfsPath ? { mfsPath: entry.mfsPath } : {}),
|
|
1852
|
+
...(entry.mfsPaths.length ? { mfsPaths: entry.mfsPaths } : {}),
|
|
1853
|
+
}));
|
|
1854
|
+
|
|
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
|
+
const rewriteImportedCidReferences = async ({ oldCid, newCid, resourceType }) => {
|
|
1886
|
+
if (!oldCid || !newCid || oldCid === newCid) return;
|
|
1887
|
+
|
|
1888
|
+
if (resourceType === 'object-layer-data') {
|
|
1889
|
+
await ObjectLayer.updateMany({ cid: oldCid }, { $set: { cid: newCid } });
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
if (resourceType === 'atlas-sprite-sheet') {
|
|
1894
|
+
await AtlasSpriteSheet.updateMany({ cid: oldCid }, { $set: { cid: newCid } });
|
|
1895
|
+
await ObjectLayer.updateMany({ 'data.render.cid': oldCid }, { $set: { 'data.render.cid': newCid } });
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
if (resourceType === 'atlas-metadata') {
|
|
1900
|
+
await ObjectLayer.updateMany(
|
|
1901
|
+
{ 'data.render.metadataCid': oldCid },
|
|
1902
|
+
{ $set: { 'data.render.metadataCid': newCid } },
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1773
1906
|
|
|
1774
1907
|
// ── EXPORT ──────────────────────────────────────────────────────
|
|
1775
1908
|
if (options.export !== undefined) {
|
|
1776
1909
|
const instance = await CyberiaInstance.findOne({ code: instanceCode }).lean();
|
|
1777
1910
|
if (!instance) {
|
|
1778
1911
|
logger.error(`CyberiaInstance with code "${instanceCode}" not found`);
|
|
1779
|
-
await
|
|
1912
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
1780
1913
|
process.exit(1);
|
|
1781
1914
|
}
|
|
1782
1915
|
|
|
@@ -1788,13 +1921,12 @@ try {
|
|
|
1788
1921
|
fs.ensureDirSync(backupDir);
|
|
1789
1922
|
logger.info('Exporting instance', { code: instanceCode, backupDir });
|
|
1790
1923
|
|
|
1791
|
-
fs.ensureDirSync(`${backupDir}/files`);
|
|
1792
|
-
|
|
1793
1924
|
// Helper: export a File document to the files/ directory
|
|
1794
1925
|
const exportFileDoc = async (fileId, fileKey) => {
|
|
1795
1926
|
if (!fileId) return;
|
|
1796
1927
|
const file = await File.findById(fileId).lean();
|
|
1797
1928
|
if (!file) return;
|
|
1929
|
+
fs.ensureDirSync(`${backupDir}/files`);
|
|
1798
1930
|
const fileExport = { ...file };
|
|
1799
1931
|
// Handle both Node.js Buffer and BSON Binary types from .lean()
|
|
1800
1932
|
if (fileExport.data) {
|
|
@@ -1808,11 +1940,46 @@ try {
|
|
|
1808
1940
|
|
|
1809
1941
|
// 1. Save instance document + thumbnail
|
|
1810
1942
|
fs.writeJsonSync(`${backupDir}/cyberia-instance.json`, instance, { spaces: 2 });
|
|
1811
|
-
if (instance.thumbnail) {
|
|
1943
|
+
if (!options.conf && instance.thumbnail) {
|
|
1812
1944
|
await exportFileDoc(instance.thumbnail, `thumb-instance-${instanceCode}`);
|
|
1813
1945
|
}
|
|
1814
1946
|
logger.info('Exported CyberiaInstance', { code: instanceCode });
|
|
1815
1947
|
|
|
1948
|
+
// 1b. Export linked CyberiaInstanceConf (skillRules, equipmentRules, entityDefaults, etc.)
|
|
1949
|
+
// If no conf doc exists yet (instance created before auto-upsert logic), create one using
|
|
1950
|
+
// schema defaults — identical to the behaviour in CyberiaInstanceService.post().
|
|
1951
|
+
let instanceConf =
|
|
1952
|
+
(await CyberiaInstanceConf.findOne({ instanceCode }).lean()) ||
|
|
1953
|
+
(instance.conf ? await CyberiaInstanceConf.findById(instance.conf).lean() : null);
|
|
1954
|
+
if (!instanceConf) {
|
|
1955
|
+
logger.info('No CyberiaInstanceConf found — creating default', { instanceCode });
|
|
1956
|
+
const created = await CyberiaInstanceConf.findOneAndUpdate(
|
|
1957
|
+
{ instanceCode },
|
|
1958
|
+
{ $setOnInsert: { instanceCode } },
|
|
1959
|
+
{ upsert: true, returnDocument: 'after' },
|
|
1960
|
+
);
|
|
1961
|
+
// Back-fill the instance.conf ref if it was missing
|
|
1962
|
+
if (created && !instance.conf) {
|
|
1963
|
+
await CyberiaInstance.findByIdAndUpdate(instance._id, { conf: created._id });
|
|
1964
|
+
}
|
|
1965
|
+
instanceConf = created?.toObject ? created.toObject() : created;
|
|
1966
|
+
}
|
|
1967
|
+
if (instanceConf) {
|
|
1968
|
+
fs.writeJsonSync(`${backupDir}/cyberia-instance-conf.json`, instanceConf, { spaces: 2 });
|
|
1969
|
+
logger.info('Exported CyberiaInstanceConf', { instanceCode });
|
|
1970
|
+
} else {
|
|
1971
|
+
logger.warn('Could not create or find CyberiaInstanceConf', { instanceCode });
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
if (options.conf) {
|
|
1975
|
+
logger.info('Instance export completed in --conf mode', {
|
|
1976
|
+
backupDir,
|
|
1977
|
+
exportedFiles: ['cyberia-instance.json', 'cyberia-instance-conf.json'],
|
|
1978
|
+
});
|
|
1979
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1816
1983
|
// 2. Collect all map codes (instance maps + portal targets)
|
|
1817
1984
|
const mapCodes = new Set(instance.cyberiaMapCodes || []);
|
|
1818
1985
|
for (const portal of instance.portals || []) {
|
|
@@ -1841,6 +2008,100 @@ try {
|
|
|
1841
2008
|
}
|
|
1842
2009
|
}
|
|
1843
2010
|
|
|
2011
|
+
// 4b. Add instance-level itemIds
|
|
2012
|
+
for (const id of instance.itemIds || []) {
|
|
2013
|
+
if (id) objectLayerItemIds.add(id);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
// 4c. Add all itemIds referenced by CyberiaInstanceConf (entityDefaults + skillConfig).
|
|
2017
|
+
// This ensures liveItemIds, deadItemIds, dropItemIds, defaultObjectLayers and
|
|
2018
|
+
// skill trigger items are included even if no map entity currently uses them.
|
|
2019
|
+
if (instanceConf) {
|
|
2020
|
+
for (const ed of instanceConf.entityDefaults || []) {
|
|
2021
|
+
for (const id of ed.liveItemIds || []) if (id) objectLayerItemIds.add(id);
|
|
2022
|
+
for (const id of ed.deadItemIds || []) if (id) objectLayerItemIds.add(id);
|
|
2023
|
+
for (const id of ed.dropItemIds || []) if (id) objectLayerItemIds.add(id);
|
|
2024
|
+
for (const slot of ed.defaultObjectLayers || []) {
|
|
2025
|
+
if (slot.itemId) objectLayerItemIds.add(slot.itemId);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
for (const sc of instanceConf.skillConfig || []) {
|
|
2029
|
+
if (sc.triggerItemId) objectLayerItemIds.add(sc.triggerItemId);
|
|
2030
|
+
for (const skill of sc.skills || []) {
|
|
2031
|
+
if (skill.summonedEntityItemId && !skill.summonedEntityItemId.startsWith('$')) {
|
|
2032
|
+
objectLayerItemIds.add(skill.summonedEntityItemId);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
// 4d. Export dialogues for all relevant object-layer items. Codes follow the pattern
|
|
2039
|
+
// "default-<itemId>". If an item has no dialogue docs yet but ships with
|
|
2040
|
+
// DefaultCyberiaDialogues, seed those defaults into Mongo first.
|
|
2041
|
+
if (objectLayerItemIds.size > 0) {
|
|
2042
|
+
const requestedItemIds = [...objectLayerItemIds];
|
|
2043
|
+
const requestedCodes = requestedItemIds.map((id) => `default-${id}`);
|
|
2044
|
+
const defaultDialoguesByItemId = getDefaultDialoguesByItemId(requestedItemIds);
|
|
2045
|
+
const existingDialogueDocs = await CyberiaDialogue.find({
|
|
2046
|
+
code: { $in: requestedCodes },
|
|
2047
|
+
})
|
|
2048
|
+
.sort({ code: 1, order: 1 })
|
|
2049
|
+
.lean();
|
|
2050
|
+
|
|
2051
|
+
const existingDialogueCodes = new Set(existingDialogueDocs.map((dialogue) => dialogue.code).filter(Boolean));
|
|
2052
|
+
let seededDialogueCount = 0;
|
|
2053
|
+
|
|
2054
|
+
for (const [itemId, dialogues] of defaultDialoguesByItemId.entries()) {
|
|
2055
|
+
const firstCode = dialogues[0]?.code;
|
|
2056
|
+
if (firstCode && existingDialogueCodes.has(firstCode)) continue;
|
|
2057
|
+
|
|
2058
|
+
for (const dialogue of dialogues) {
|
|
2059
|
+
await CyberiaDialogue.findOneAndUpdate(
|
|
2060
|
+
{ code: dialogue.code, order: dialogue.order },
|
|
2061
|
+
{
|
|
2062
|
+
$set: {
|
|
2063
|
+
speaker: dialogue.speaker,
|
|
2064
|
+
text: dialogue.text,
|
|
2065
|
+
mood: dialogue.mood,
|
|
2066
|
+
},
|
|
2067
|
+
},
|
|
2068
|
+
{ upsert: true },
|
|
2069
|
+
);
|
|
2070
|
+
seededDialogueCount++;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
const dialogueDocs = await CyberiaDialogue.find({ code: { $in: requestedCodes } })
|
|
2075
|
+
.sort({ code: 1, order: 1 })
|
|
2076
|
+
.lean();
|
|
2077
|
+
|
|
2078
|
+
if (seededDialogueCount > 0) {
|
|
2079
|
+
logger.info(`Seeded ${seededDialogueCount} CyberiaDialogue default record(s) for export`);
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
if (dialogueDocs.length > 0) {
|
|
2083
|
+
fs.ensureDirSync(`${backupDir}/cyberia-dialogues`);
|
|
2084
|
+
const dialoguesByCode = new Map();
|
|
2085
|
+
|
|
2086
|
+
for (const dialogue of dialogueDocs) {
|
|
2087
|
+
if (!dialoguesByCode.has(dialogue.code)) {
|
|
2088
|
+
dialoguesByCode.set(dialogue.code, []);
|
|
2089
|
+
}
|
|
2090
|
+
dialoguesByCode.get(dialogue.code).push(dialogue);
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
for (const [code, dialogues] of dialoguesByCode.entries()) {
|
|
2094
|
+
fs.writeJsonSync(`${backupDir}/cyberia-dialogues/${encodeURIComponent(code)}.json`, dialogues, {
|
|
2095
|
+
spaces: 2,
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
logger.info(`Exported ${dialogueDocs.length} CyberiaDialogue document(s)`, {
|
|
2100
|
+
codes: [...dialoguesByCode.keys()],
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
|
|
1844
2105
|
// 5. Export object layers with related render frames, atlas, files, and IPFS records
|
|
1845
2106
|
if (objectLayerItemIds.size > 0) {
|
|
1846
2107
|
const objectLayers = await ObjectLayer.find({
|
|
@@ -1851,48 +2112,245 @@ try {
|
|
|
1851
2112
|
fs.ensureDirSync(`${backupDir}/render-frames`);
|
|
1852
2113
|
fs.ensureDirSync(`${backupDir}/atlas-sprite-sheets`);
|
|
1853
2114
|
fs.ensureDirSync(`${backupDir}/ipfs`);
|
|
2115
|
+
fs.ensureDirSync(`${backupDir}/ipfs/content`);
|
|
2116
|
+
|
|
2117
|
+
const canonicalPins = new Map();
|
|
2118
|
+
const expectedObjectLayerIpfsRefs = [];
|
|
2119
|
+
const ipfsPayloadFailures = [];
|
|
2120
|
+
let ipfsPayloadExportCount = 0;
|
|
2121
|
+
let ipfsPayloadAliasCount = 0;
|
|
2122
|
+
|
|
2123
|
+
const writeBackupPayload = (cid, payloadBuffer) => {
|
|
2124
|
+
if (!cid) return false;
|
|
2125
|
+
const payloadPath = `${backupDir}/ipfs/content/${cid}.bin`;
|
|
2126
|
+
if (fs.existsSync(payloadPath)) return false;
|
|
2127
|
+
fs.writeFileSync(payloadPath, payloadBuffer);
|
|
2128
|
+
ipfsPayloadExportCount++;
|
|
2129
|
+
return true;
|
|
2130
|
+
};
|
|
2131
|
+
|
|
2132
|
+
const writeBackupPayloadAlias = ({ canonicalCid, linkedCid, payloadBuffer }) => {
|
|
2133
|
+
if (!linkedCid || linkedCid === canonicalCid) return;
|
|
2134
|
+
if (writeBackupPayload(linkedCid, payloadBuffer)) {
|
|
2135
|
+
ipfsPayloadAliasCount++;
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
|
|
2139
|
+
const exportCanonicalPayload = async ({ payloadBuffer, resourceType, mfsPath, filename, itemKey }) => {
|
|
2140
|
+
const hashResult = await IpfsClient.hashBufferForIpfs(payloadBuffer, filename);
|
|
2141
|
+
if (!hashResult?.cid) {
|
|
2142
|
+
ipfsPayloadFailures.push({ itemKey, resourceType, mfsPath, reason: 'Failed to hash payload via Kubo' });
|
|
2143
|
+
return null;
|
|
2144
|
+
}
|
|
1854
2145
|
|
|
1855
|
-
|
|
2146
|
+
writeBackupPayload(hashResult.cid, payloadBuffer);
|
|
2147
|
+
return hashResult.cid;
|
|
2148
|
+
};
|
|
1856
2149
|
|
|
1857
2150
|
for (const ol of objectLayers) {
|
|
1858
|
-
const
|
|
1859
|
-
|
|
2151
|
+
const itemKey = ol.data?.item?.id || ol._id.toString();
|
|
2152
|
+
const itemPaths = getCanonicalIpfsPaths(itemKey);
|
|
2153
|
+
const objectLayerExport = newInstance(ol);
|
|
2154
|
+
|
|
2155
|
+
if (!objectLayerExport.data.render) {
|
|
2156
|
+
objectLayerExport.data.render = {};
|
|
2157
|
+
}
|
|
1860
2158
|
|
|
1861
2159
|
// Export ObjectLayerRenderFrames
|
|
1862
2160
|
if (ol.objectLayerRenderFramesId) {
|
|
1863
2161
|
const rf = await ObjectLayerRenderFrames.findById(ol.objectLayerRenderFramesId).lean();
|
|
1864
2162
|
if (rf) {
|
|
1865
|
-
fs.writeJsonSync(`${backupDir}/render-frames/${
|
|
2163
|
+
fs.writeJsonSync(`${backupDir}/render-frames/${itemKey}.json`, rf, { spaces: 2 });
|
|
1866
2164
|
}
|
|
1867
2165
|
}
|
|
1868
2166
|
|
|
1869
|
-
// Export AtlasSpriteSheet + its File
|
|
2167
|
+
// Export AtlasSpriteSheet + its File using canonical payload bytes from the DB state.
|
|
1870
2168
|
if (ol.atlasSpriteSheetId) {
|
|
1871
2169
|
const atlas = await AtlasSpriteSheet.findById(ol.atlasSpriteSheetId).lean();
|
|
1872
|
-
if (atlas) {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
2170
|
+
if (!atlas) {
|
|
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
|
+
|
|
2180
|
+
const atlasExport = newInstance(atlas);
|
|
2181
|
+
if (atlas.fileId) {
|
|
2182
|
+
await exportFileDoc(atlas.fileId, `atlas-${itemKey}`);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
const atlasFile = atlas.fileId ? await File.findById(atlas.fileId).lean() : null;
|
|
2186
|
+
const atlasBuffer = toBuffer(atlasFile?.data);
|
|
2187
|
+
if (!atlasBuffer) {
|
|
2188
|
+
ipfsPayloadFailures.push({
|
|
2189
|
+
itemKey,
|
|
2190
|
+
resourceType: 'atlas-sprite-sheet',
|
|
2191
|
+
mfsPath: itemPaths.atlasSpriteSheet,
|
|
2192
|
+
reason: 'Atlas File payload not found in MongoDB',
|
|
2193
|
+
});
|
|
2194
|
+
continue;
|
|
1878
2195
|
}
|
|
2196
|
+
|
|
2197
|
+
const atlasCid = await exportCanonicalPayload({
|
|
2198
|
+
payloadBuffer: atlasBuffer,
|
|
2199
|
+
resourceType: 'atlas-sprite-sheet',
|
|
2200
|
+
mfsPath: itemPaths.atlasSpriteSheet,
|
|
2201
|
+
filename: `${itemKey}_atlas_sprite_sheet.png`,
|
|
2202
|
+
itemKey,
|
|
2203
|
+
});
|
|
2204
|
+
if (!atlasCid) continue;
|
|
2205
|
+
|
|
2206
|
+
const linkedAtlasCid = atlas.cid || ol.data?.render?.cid || atlasCid;
|
|
2207
|
+
writeBackupPayloadAlias({
|
|
2208
|
+
canonicalCid: atlasCid,
|
|
2209
|
+
linkedCid: linkedAtlasCid,
|
|
2210
|
+
payloadBuffer: atlasBuffer,
|
|
2211
|
+
});
|
|
2212
|
+
expectedObjectLayerIpfsRefs.push({
|
|
2213
|
+
itemKey,
|
|
2214
|
+
resourceType: 'atlas-sprite-sheet',
|
|
2215
|
+
mfsPath: itemPaths.atlasSpriteSheet,
|
|
2216
|
+
linkedCid: linkedAtlasCid,
|
|
2217
|
+
fallbackCid: atlasCid,
|
|
2218
|
+
});
|
|
2219
|
+
|
|
2220
|
+
const atlasMetadataBuffer = Buffer.from(stringify(atlasExport.metadata || {}), 'utf-8');
|
|
2221
|
+
const atlasMetadataCid = await exportCanonicalPayload({
|
|
2222
|
+
payloadBuffer: atlasMetadataBuffer,
|
|
2223
|
+
resourceType: 'atlas-metadata',
|
|
2224
|
+
mfsPath: itemPaths.atlasMetadata,
|
|
2225
|
+
filename: `${itemKey}_atlas_sprite_sheet_metadata.json`,
|
|
2226
|
+
itemKey,
|
|
2227
|
+
});
|
|
2228
|
+
if (!atlasMetadataCid) continue;
|
|
2229
|
+
|
|
2230
|
+
const linkedAtlasMetadataCid = ol.data?.render?.metadataCid || atlasMetadataCid;
|
|
2231
|
+
writeBackupPayloadAlias({
|
|
2232
|
+
canonicalCid: atlasMetadataCid,
|
|
2233
|
+
linkedCid: linkedAtlasMetadataCid,
|
|
2234
|
+
payloadBuffer: atlasMetadataBuffer,
|
|
2235
|
+
});
|
|
2236
|
+
expectedObjectLayerIpfsRefs.push({
|
|
2237
|
+
itemKey,
|
|
2238
|
+
resourceType: 'atlas-metadata',
|
|
2239
|
+
mfsPath: itemPaths.atlasMetadata,
|
|
2240
|
+
linkedCid: linkedAtlasMetadataCid,
|
|
2241
|
+
fallbackCid: atlasMetadataCid,
|
|
2242
|
+
});
|
|
2243
|
+
|
|
2244
|
+
atlasExport.cid = atlasCid;
|
|
2245
|
+
objectLayerExport.data.render.cid = atlasCid;
|
|
2246
|
+
objectLayerExport.data.render.metadataCid = atlasMetadataCid;
|
|
2247
|
+
fs.writeJsonSync(`${backupDir}/atlas-sprite-sheets/${itemKey}.json`, atlasExport, { spaces: 2 });
|
|
2248
|
+
} else {
|
|
2249
|
+
if (objectLayerExport.data.render?.cid || objectLayerExport.data.render?.metadataCid) {
|
|
2250
|
+
ipfsPayloadFailures.push({
|
|
2251
|
+
itemKey,
|
|
2252
|
+
resourceType: 'atlas-sprite-sheet',
|
|
2253
|
+
mfsPath: itemPaths.atlasSpriteSheet,
|
|
2254
|
+
reason: 'ObjectLayer references atlas CIDs but no AtlasSpriteSheet document exists',
|
|
2255
|
+
});
|
|
2256
|
+
continue;
|
|
2257
|
+
}
|
|
2258
|
+
delete objectLayerExport.data.render.cid;
|
|
2259
|
+
delete objectLayerExport.data.render.metadataCid;
|
|
1879
2260
|
}
|
|
1880
2261
|
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
2262
|
+
const objectLayerBuffer = Buffer.from(stringify(objectLayerExport.data || {}), 'utf-8');
|
|
2263
|
+
const objectLayerCid = await exportCanonicalPayload({
|
|
2264
|
+
payloadBuffer: objectLayerBuffer,
|
|
2265
|
+
resourceType: 'object-layer-data',
|
|
2266
|
+
mfsPath: itemPaths.objectLayerData,
|
|
2267
|
+
filename: `${itemKey}_data.json`,
|
|
2268
|
+
itemKey,
|
|
2269
|
+
});
|
|
2270
|
+
if (!objectLayerCid) continue;
|
|
2271
|
+
|
|
2272
|
+
const linkedObjectLayerCid = ol.cid || objectLayerCid;
|
|
2273
|
+
writeBackupPayloadAlias({
|
|
2274
|
+
canonicalCid: objectLayerCid,
|
|
2275
|
+
linkedCid: linkedObjectLayerCid,
|
|
2276
|
+
payloadBuffer: objectLayerBuffer,
|
|
2277
|
+
});
|
|
2278
|
+
expectedObjectLayerIpfsRefs.push({
|
|
2279
|
+
itemKey,
|
|
2280
|
+
resourceType: 'object-layer-data',
|
|
2281
|
+
mfsPath: itemPaths.objectLayerData,
|
|
2282
|
+
linkedCid: linkedObjectLayerCid,
|
|
2283
|
+
fallbackCid: objectLayerCid,
|
|
2284
|
+
});
|
|
2285
|
+
|
|
2286
|
+
objectLayerExport.cid = objectLayerCid;
|
|
2287
|
+
fs.writeJsonSync(`${backupDir}/object-layers/${itemKey}.json`, objectLayerExport, { spaces: 2 });
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
if (ipfsPayloadFailures.length > 0) {
|
|
2291
|
+
for (const failure of ipfsPayloadFailures) {
|
|
2292
|
+
logger.error('Canonical IPFS payload export failed', failure);
|
|
2293
|
+
}
|
|
2294
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
2295
|
+
process.exit(1);
|
|
1885
2296
|
}
|
|
1886
2297
|
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
2298
|
+
const relatedPinPaths = [
|
|
2299
|
+
...new Set(expectedObjectLayerIpfsRefs.map((entry) => entry.mfsPath).filter(Boolean)),
|
|
2300
|
+
];
|
|
2301
|
+
const relatedPinCids = [
|
|
2302
|
+
...new Set(
|
|
2303
|
+
expectedObjectLayerIpfsRefs.flatMap((entry) => [entry.linkedCid, entry.fallbackCid]).filter(Boolean),
|
|
2304
|
+
),
|
|
2305
|
+
];
|
|
2306
|
+
const relatedIpfsDocs =
|
|
2307
|
+
relatedPinPaths.length > 0 || relatedPinCids.length > 0
|
|
2308
|
+
? await Ipfs.find({
|
|
2309
|
+
$or: [
|
|
2310
|
+
...(relatedPinPaths.length ? [{ mfsPath: { $in: relatedPinPaths } }] : []),
|
|
2311
|
+
...(relatedPinPaths.length ? [{ mfsPaths: { $in: relatedPinPaths } }] : []),
|
|
2312
|
+
...(relatedPinCids.length ? [{ cid: { $in: relatedPinCids } }] : []),
|
|
2313
|
+
],
|
|
2314
|
+
}).lean()
|
|
2315
|
+
: [];
|
|
2316
|
+
|
|
2317
|
+
let ipfsCollectionMatchCount = 0;
|
|
2318
|
+
let ipfsCollectionFallbackCount = 0;
|
|
2319
|
+
|
|
2320
|
+
for (const ref of expectedObjectLayerIpfsRefs) {
|
|
2321
|
+
const matchingDoc = findInstanceRelatedIpfsDoc(relatedIpfsDocs, ref);
|
|
2322
|
+
const exportCid = matchingDoc?.cid || ref.linkedCid || ref.fallbackCid;
|
|
2323
|
+
|
|
2324
|
+
if (!exportCid) {
|
|
2325
|
+
logger.warn('Skipping instance IPFS pin export because the ObjectLayer ref has no linked CID', {
|
|
2326
|
+
itemKey: ref.itemKey,
|
|
2327
|
+
resourceType: ref.resourceType,
|
|
2328
|
+
mfsPath: ref.mfsPath,
|
|
2329
|
+
});
|
|
2330
|
+
continue;
|
|
1893
2331
|
}
|
|
2332
|
+
|
|
2333
|
+
upsertCanonicalPinEntry(canonicalPins, {
|
|
2334
|
+
cid: exportCid,
|
|
2335
|
+
resourceType: ref.resourceType,
|
|
2336
|
+
mfsPath: ref.mfsPath,
|
|
2337
|
+
});
|
|
2338
|
+
|
|
2339
|
+
if (matchingDoc) ipfsCollectionMatchCount++;
|
|
2340
|
+
else ipfsCollectionFallbackCount++;
|
|
1894
2341
|
}
|
|
1895
2342
|
|
|
2343
|
+
const sanitised = serialiseCanonicalPins(canonicalPins);
|
|
2344
|
+
fs.writeJsonSync(`${backupDir}/ipfs/pins.json`, sanitised, { spaces: 2 });
|
|
2345
|
+
logger.info(
|
|
2346
|
+
`Exported ${sanitised.length} instance-related Ipfs pin record(s) and ${ipfsPayloadExportCount} raw payload file(s)`,
|
|
2347
|
+
{
|
|
2348
|
+
matchedFromIpfsCollection: ipfsCollectionMatchCount,
|
|
2349
|
+
fallbackFromObjectLayerRefs: ipfsCollectionFallbackCount,
|
|
2350
|
+
rawPayloadAliases: ipfsPayloadAliasCount,
|
|
2351
|
+
},
|
|
2352
|
+
);
|
|
2353
|
+
|
|
1896
2354
|
logger.info(`Exported ${objectLayers.length} ObjectLayer document(s)`, {
|
|
1897
2355
|
itemIds: [...objectLayerItemIds],
|
|
1898
2356
|
});
|
|
@@ -1912,14 +2370,14 @@ try {
|
|
|
1912
2370
|
|
|
1913
2371
|
if (!fs.existsSync(backupDir)) {
|
|
1914
2372
|
logger.error(`Backup directory not found: ${backupDir}`);
|
|
1915
|
-
await
|
|
2373
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
1916
2374
|
process.exit(1);
|
|
1917
2375
|
}
|
|
1918
2376
|
|
|
1919
2377
|
logger.info('Importing instance', { code: instanceCode, backupDir });
|
|
1920
2378
|
|
|
1921
2379
|
// 0. Drop existing documents if --drop is set
|
|
1922
|
-
if (options.drop) {
|
|
2380
|
+
if (options.drop && !options.conf) {
|
|
1923
2381
|
const existingInstance = await CyberiaInstance.findOne({ code: instanceCode }).lean();
|
|
1924
2382
|
if (existingInstance) {
|
|
1925
2383
|
const dropMapCodes = new Set(existingInstance.cyberiaMapCodes || []);
|
|
@@ -1931,13 +2389,41 @@ try {
|
|
|
1931
2389
|
// Collect thumbnail File IDs to drop
|
|
1932
2390
|
const thumbFileIds = [];
|
|
1933
2391
|
if (existingInstance.thumbnail) thumbFileIds.push(existingInstance.thumbnail);
|
|
2392
|
+
const dropOlItemIds = new Set();
|
|
1934
2393
|
|
|
1935
2394
|
// Query other instances/maps for shared thumbnail exclusion
|
|
1936
2395
|
const otherInstances = await CyberiaInstance.find({ code: { $ne: instanceCode } }, { thumbnail: 1 }).lean();
|
|
1937
2396
|
|
|
2397
|
+
// Add instance-level itemIds (may not appear in any map entity)
|
|
2398
|
+
for (const id of existingInstance.itemIds || []) if (id) dropOlItemIds.add(id);
|
|
2399
|
+
|
|
2400
|
+
// Add conf entityDefaults and skillConfig itemIds (liveItemIds, deadItemIds, dropItemIds, defaultObjectLayers)
|
|
2401
|
+
const existingConf =
|
|
2402
|
+
(await CyberiaInstanceConf.findOne({ instanceCode }).lean()) ||
|
|
2403
|
+
(existingInstance.conf ? await CyberiaInstanceConf.findById(existingInstance.conf).lean() : null);
|
|
2404
|
+
if (existingConf) {
|
|
2405
|
+
for (const ed of existingConf.entityDefaults || []) {
|
|
2406
|
+
for (const id of ed.liveItemIds || []) if (id) dropOlItemIds.add(id);
|
|
2407
|
+
for (const id of ed.deadItemIds || []) if (id) dropOlItemIds.add(id);
|
|
2408
|
+
for (const id of ed.dropItemIds || []) if (id) dropOlItemIds.add(id);
|
|
2409
|
+
for (const slot of ed.defaultObjectLayers || []) if (slot.itemId) dropOlItemIds.add(slot.itemId);
|
|
2410
|
+
}
|
|
2411
|
+
for (const sc of existingConf.skillConfig || []) {
|
|
2412
|
+
if (sc.triggerItemId) dropOlItemIds.add(sc.triggerItemId);
|
|
2413
|
+
for (const skill of sc.skills || []) {
|
|
2414
|
+
if (skill.summonedEntityItemId && !skill.summonedEntityItemId.startsWith('$'))
|
|
2415
|
+
dropOlItemIds.add(skill.summonedEntityItemId);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
const otherMaps = await CyberiaMap.find(
|
|
2421
|
+
{ code: { $nin: [...dropMapCodes] } },
|
|
2422
|
+
{ 'entities.objectLayerItemIds': 1, thumbnail: 1 },
|
|
2423
|
+
).lean();
|
|
2424
|
+
|
|
1938
2425
|
if (dropMapCodes.size > 0) {
|
|
1939
2426
|
const dropMaps = await CyberiaMap.find({ code: { $in: [...dropMapCodes] } }).lean();
|
|
1940
|
-
const dropOlItemIds = new Set();
|
|
1941
2427
|
for (const map of dropMaps) {
|
|
1942
2428
|
if (map.thumbnail) thumbFileIds.push(map.thumbnail);
|
|
1943
2429
|
for (const entity of map.entities || []) {
|
|
@@ -1947,110 +2433,105 @@ try {
|
|
|
1947
2433
|
}
|
|
1948
2434
|
}
|
|
1949
2435
|
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
{ 'entities.objectLayerItemIds': 1, thumbnail: 1 },
|
|
1954
|
-
).lean();
|
|
1955
|
-
const sharedOlItemIds = new Set();
|
|
1956
|
-
for (const m of otherMaps) {
|
|
1957
|
-
for (const entity of m.entities || []) {
|
|
1958
|
-
for (const itemId of entity.objectLayerItemIds || []) {
|
|
1959
|
-
if (dropOlItemIds.has(itemId)) sharedOlItemIds.add(itemId);
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
for (const shared of sharedOlItemIds) dropOlItemIds.delete(shared);
|
|
1964
|
-
if (sharedOlItemIds.size > 0) {
|
|
1965
|
-
logger.info(`Preserved ${sharedOlItemIds.size} ObjectLayer(s) shared with other maps`);
|
|
1966
|
-
}
|
|
2436
|
+
const mapResult = await CyberiaMap.deleteMany({ code: { $in: [...dropMapCodes] } });
|
|
2437
|
+
logger.info(`Dropped ${mapResult.deletedCount} CyberiaMap document(s)`);
|
|
2438
|
+
}
|
|
1967
2439
|
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
const
|
|
1972
|
-
|
|
1973
|
-
|
|
2440
|
+
// Exclude OL item IDs referenced by maps outside this instance
|
|
2441
|
+
const sharedOlItemIds = new Set();
|
|
2442
|
+
for (const m of otherMaps) {
|
|
2443
|
+
for (const entity of m.entities || []) {
|
|
2444
|
+
for (const itemId of entity.objectLayerItemIds || []) {
|
|
2445
|
+
if (dropOlItemIds.has(itemId)) sharedOlItemIds.add(itemId);
|
|
2446
|
+
}
|
|
1974
2447
|
}
|
|
2448
|
+
}
|
|
2449
|
+
for (const shared of sharedOlItemIds) dropOlItemIds.delete(shared);
|
|
2450
|
+
if (sharedOlItemIds.size > 0) {
|
|
2451
|
+
logger.info(`Preserved ${sharedOlItemIds.size} ObjectLayer(s) shared with other maps`);
|
|
2452
|
+
}
|
|
1975
2453
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
'data.render': 1,
|
|
1984
|
-
objectLayerRenderFramesId: 1,
|
|
1985
|
-
atlasSpriteSheetId: 1,
|
|
1986
|
-
},
|
|
1987
|
-
).lean();
|
|
1988
|
-
|
|
1989
|
-
const cidsToUnpin = new Set();
|
|
1990
|
-
const renderFrameIds = [];
|
|
1991
|
-
const atlasIds = [];
|
|
1992
|
-
const itemKeysToClean = new Set();
|
|
1993
|
-
|
|
1994
|
-
for (const doc of olDocs) {
|
|
1995
|
-
if (doc.cid) cidsToUnpin.add(doc.cid);
|
|
1996
|
-
if (doc.data?.render?.cid) cidsToUnpin.add(doc.data.render.cid);
|
|
1997
|
-
if (doc.data?.render?.metadataCid) cidsToUnpin.add(doc.data.render.metadataCid);
|
|
1998
|
-
if (doc.data?.item?.id) itemKeysToClean.add(doc.data.item.id);
|
|
1999
|
-
if (doc.objectLayerRenderFramesId) renderFrameIds.push(doc.objectLayerRenderFramesId);
|
|
2000
|
-
if (doc.atlasSpriteSheetId) atlasIds.push(doc.atlasSpriteSheetId);
|
|
2001
|
-
}
|
|
2454
|
+
// Exclude thumbnail File IDs referenced by other instances or maps
|
|
2455
|
+
const otherMapThumbs = otherMaps.map((m) => m.thumbnail?.toString()).filter(Boolean);
|
|
2456
|
+
const otherInstThumbs = otherInstances.map((i) => i.thumbnail?.toString()).filter(Boolean);
|
|
2457
|
+
const sharedThumbIds = new Set([...otherMapThumbs, ...otherInstThumbs]);
|
|
2458
|
+
for (let i = thumbFileIds.length - 1; i >= 0; i--) {
|
|
2459
|
+
if (sharedThumbIds.has(thumbFileIds[i].toString())) thumbFileIds.splice(i, 1);
|
|
2460
|
+
}
|
|
2002
2461
|
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
const atlasResult = await AtlasSpriteSheet.deleteMany({ _id: { $in: atlasIds } });
|
|
2018
|
-
logger.info(`Dropped ${atlasResult.deletedCount} AtlasSpriteSheet document(s)`);
|
|
2019
|
-
}
|
|
2462
|
+
if (dropOlItemIds.size > 0) {
|
|
2463
|
+
const dropDialogueCodes = [...dropOlItemIds].map((id) => `default-${id}`);
|
|
2464
|
+
const dialogueResult = await CyberiaDialogue.deleteMany({ code: { $in: dropDialogueCodes } });
|
|
2465
|
+
logger.info(`Dropped ${dialogueResult.deletedCount} CyberiaDialogue document(s)`);
|
|
2466
|
+
const olDocs = await ObjectLayer.find(
|
|
2467
|
+
{ 'data.item.id': { $in: [...dropOlItemIds] } },
|
|
2468
|
+
{
|
|
2469
|
+
cid: 1,
|
|
2470
|
+
'data.item.id': 1,
|
|
2471
|
+
'data.render': 1,
|
|
2472
|
+
objectLayerRenderFramesId: 1,
|
|
2473
|
+
atlasSpriteSheetId: 1,
|
|
2474
|
+
},
|
|
2475
|
+
).lean();
|
|
2020
2476
|
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
}
|
|
2477
|
+
const cidsToUnpin = new Set();
|
|
2478
|
+
const renderFrameIds = [];
|
|
2479
|
+
const atlasIds = [];
|
|
2480
|
+
const itemKeysToClean = new Set();
|
|
2026
2481
|
|
|
2027
|
-
|
|
2028
|
-
if (cidsToUnpin.
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2482
|
+
for (const doc of olDocs) {
|
|
2483
|
+
if (doc.cid) cidsToUnpin.add(doc.cid);
|
|
2484
|
+
if (doc.data?.render?.cid) cidsToUnpin.add(doc.data.render.cid);
|
|
2485
|
+
if (doc.data?.render?.metadataCid) cidsToUnpin.add(doc.data.render.metadataCid);
|
|
2486
|
+
if (doc.data?.item?.id) itemKeysToClean.add(doc.data.item.id);
|
|
2487
|
+
if (doc.objectLayerRenderFramesId) renderFrameIds.push(doc.objectLayerRenderFramesId);
|
|
2488
|
+
if (doc.atlasSpriteSheetId) atlasIds.push(doc.atlasSpriteSheetId);
|
|
2489
|
+
}
|
|
2032
2490
|
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2491
|
+
// Delete AtlasSpriteSheet + referenced File docs
|
|
2492
|
+
if (atlasIds.length > 0) {
|
|
2493
|
+
const atlasDocs = await AtlasSpriteSheet.find({ _id: { $in: atlasIds } }, { fileId: 1, cid: 1 }).lean();
|
|
2494
|
+
const atlasFileIds = atlasDocs.map((a) => a.fileId).filter(Boolean);
|
|
2495
|
+
for (const atlas of atlasDocs) {
|
|
2496
|
+
if (atlas.cid) cidsToUnpin.add(atlas.cid);
|
|
2038
2497
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
if (ok) mfsCount++;
|
|
2498
|
+
if (atlasFileIds.length > 0) {
|
|
2499
|
+
const fileResult = await File.deleteMany({ _id: { $in: atlasFileIds } });
|
|
2500
|
+
logger.info(`Dropped ${fileResult.deletedCount} File document(s) (atlas)`);
|
|
2043
2501
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2502
|
+
const atlasResult = await AtlasSpriteSheet.deleteMany({ _id: { $in: atlasIds } });
|
|
2503
|
+
logger.info(`Dropped ${atlasResult.deletedCount} AtlasSpriteSheet document(s)`);
|
|
2504
|
+
}
|
|
2047
2505
|
|
|
2048
|
-
|
|
2049
|
-
|
|
2506
|
+
// Delete RenderFrames
|
|
2507
|
+
if (renderFrameIds.length > 0) {
|
|
2508
|
+
const rfResult = await ObjectLayerRenderFrames.deleteMany({ _id: { $in: renderFrameIds } });
|
|
2509
|
+
logger.info(`Dropped ${rfResult.deletedCount} ObjectLayerRenderFrames document(s)`);
|
|
2050
2510
|
}
|
|
2051
2511
|
|
|
2052
|
-
|
|
2053
|
-
|
|
2512
|
+
// Delete IPFS pin records
|
|
2513
|
+
if (cidsToUnpin.size > 0) {
|
|
2514
|
+
const ipfsResult = await Ipfs.deleteMany({ cid: { $in: [...cidsToUnpin] } });
|
|
2515
|
+
logger.info(`Dropped ${ipfsResult.deletedCount} Ipfs pin record(s)`);
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
// Unpin CIDs from IPFS Kubo + Cluster and remove MFS paths
|
|
2519
|
+
let unpinCount = 0;
|
|
2520
|
+
for (const cid of cidsToUnpin) {
|
|
2521
|
+
const ok = await IpfsClient.unpinCid(cid);
|
|
2522
|
+
if (ok) unpinCount++;
|
|
2523
|
+
}
|
|
2524
|
+
let mfsCount = 0;
|
|
2525
|
+
for (const itemKey of itemKeysToClean) {
|
|
2526
|
+
const ok = await IpfsClient.removeMfsPath(`/object-layer/${itemKey}`);
|
|
2527
|
+
if (ok) mfsCount++;
|
|
2528
|
+
}
|
|
2529
|
+
logger.info(
|
|
2530
|
+
`IPFS cleanup: ${unpinCount}/${cidsToUnpin.size} CIDs unpinned, ${mfsCount}/${itemKeysToClean.size} MFS paths removed`,
|
|
2531
|
+
);
|
|
2532
|
+
|
|
2533
|
+
const olResult = await ObjectLayer.deleteMany({ 'data.item.id': { $in: [...dropOlItemIds] } });
|
|
2534
|
+
logger.info(`Dropped ${olResult.deletedCount} ObjectLayer document(s)`);
|
|
2054
2535
|
}
|
|
2055
2536
|
|
|
2056
2537
|
// Drop thumbnail File documents (instance + maps), excluding shared ones
|
|
@@ -2061,9 +2542,58 @@ try {
|
|
|
2061
2542
|
|
|
2062
2543
|
await CyberiaInstance.deleteOne({ code: instanceCode });
|
|
2063
2544
|
logger.info('Dropped CyberiaInstance', { code: instanceCode });
|
|
2545
|
+
await CyberiaInstanceConf.deleteOne({ instanceCode });
|
|
2546
|
+
logger.info('Dropped CyberiaInstanceConf', { instanceCode });
|
|
2064
2547
|
} else {
|
|
2065
2548
|
logger.info('No existing instance to drop', { code: instanceCode });
|
|
2066
2549
|
}
|
|
2550
|
+
} else if (options.drop && options.conf) {
|
|
2551
|
+
logger.info(
|
|
2552
|
+
'Skipping full instance drop because --conf only imports cyberia-instance.json and cyberia-instance-conf.json',
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
if (options.conf) {
|
|
2557
|
+
const confImportPath = `${backupDir}/cyberia-instance-conf.json`;
|
|
2558
|
+
let importedConf = null;
|
|
2559
|
+
if (fs.existsSync(confImportPath)) {
|
|
2560
|
+
const confData = fs.readJsonSync(confImportPath);
|
|
2561
|
+
if (confData._id) await CyberiaInstanceConf.deleteOne({ _id: confData._id });
|
|
2562
|
+
await CyberiaInstanceConf.deleteOne({ instanceCode: confData.instanceCode });
|
|
2563
|
+
// Always bump updatedAt so the Go server's version hash changes and
|
|
2564
|
+
// ReloadWorld re-applies the config without requiring a full restart.
|
|
2565
|
+
confData.updatedAt = new Date();
|
|
2566
|
+
importedConf = await CyberiaInstanceConf.create(confData);
|
|
2567
|
+
logger.info('Imported CyberiaInstanceConf', { instanceCode: confData.instanceCode });
|
|
2568
|
+
} else {
|
|
2569
|
+
logger.warn(`CyberiaInstanceConf backup not found: ${confImportPath}`);
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
// In --conf mode we must NOT delete + recreate the CyberiaInstance because
|
|
2573
|
+
// that would overwrite cyberiaMapCodes / portals / itemIds with whatever was
|
|
2574
|
+
// in the (possibly stale) backup, effectively removing the live maps and OLs
|
|
2575
|
+
// from the instance. Only update the conf ref and bump updatedAt so the Go
|
|
2576
|
+
// server's version hash changes and ReloadWorld re-applies the config.
|
|
2577
|
+
if (importedConf) {
|
|
2578
|
+
const result = await CyberiaInstance.updateOne(
|
|
2579
|
+
{ code: instanceCode },
|
|
2580
|
+
{ $set: { conf: importedConf._id, updatedAt: new Date() } },
|
|
2581
|
+
);
|
|
2582
|
+
if (result.matchedCount > 0) {
|
|
2583
|
+
logger.info('Updated CyberiaInstance conf ref', { code: instanceCode });
|
|
2584
|
+
} else {
|
|
2585
|
+
logger.warn(`CyberiaInstance not found in DB for code "${instanceCode}" — cannot update conf ref`);
|
|
2586
|
+
}
|
|
2587
|
+
} else {
|
|
2588
|
+
logger.warn(`Skipping CyberiaInstance conf ref update — no conf was imported`);
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
logger.info('Instance import completed in --conf mode', {
|
|
2592
|
+
backupDir,
|
|
2593
|
+
importedFiles: ['cyberia-instance.json', 'cyberia-instance-conf.json'],
|
|
2594
|
+
});
|
|
2595
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
2596
|
+
return;
|
|
2067
2597
|
}
|
|
2068
2598
|
|
|
2069
2599
|
// 1. Import File documents first (atlas PNG + thumbnail dependencies)
|
|
@@ -2113,6 +2643,9 @@ try {
|
|
|
2113
2643
|
for (const f of atlasFiles) {
|
|
2114
2644
|
const atlasData = fs.readJsonSync(`${atlasDir}/${f}`);
|
|
2115
2645
|
await AtlasSpriteSheet.deleteOne({ _id: atlasData._id });
|
|
2646
|
+
if (atlasData.metadata?.itemKey) {
|
|
2647
|
+
await AtlasSpriteSheet.deleteOne({ 'metadata.itemKey': atlasData.metadata.itemKey });
|
|
2648
|
+
}
|
|
2116
2649
|
await AtlasSpriteSheet.create(atlasData);
|
|
2117
2650
|
atlasCount++;
|
|
2118
2651
|
}
|
|
@@ -2127,45 +2660,71 @@ try {
|
|
|
2127
2660
|
for (const file of olFiles) {
|
|
2128
2661
|
const olData = fs.readJsonSync(`${olDir}/${file}`);
|
|
2129
2662
|
await ObjectLayer.deleteOne({ _id: olData._id });
|
|
2663
|
+
if (olData.sha256) {
|
|
2664
|
+
await ObjectLayer.deleteOne({ sha256: olData.sha256 });
|
|
2665
|
+
}
|
|
2130
2666
|
await ObjectLayer.create(olData);
|
|
2131
2667
|
olCount++;
|
|
2132
2668
|
}
|
|
2133
2669
|
logger.info(`Imported ${olCount} ObjectLayer document(s)`);
|
|
2134
2670
|
}
|
|
2135
2671
|
|
|
2136
|
-
//
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2672
|
+
// 4b. Regenerate static frame PNGs from imported render-frames + object-layer documents.
|
|
2673
|
+
// Mirrors the writeStaticFrameAssets call in `ol --import` so src/client/public/cyberia
|
|
2674
|
+
// and the public/<host><path> deployment dir are populated even when the cyberia
|
|
2675
|
+
// asset directory was wiped (e.g. git clean / rm -rf).
|
|
2676
|
+
const rfImportDir = `${backupDir}/render-frames`;
|
|
2677
|
+
const olImportDir = `${backupDir}/object-layers`;
|
|
2678
|
+
if (fs.existsSync(rfImportDir) && fs.existsSync(olImportDir)) {
|
|
2679
|
+
const srcBasePath = './src/client/public/cyberia/';
|
|
2680
|
+
const publicBasePath = `./public/${host}${path}`;
|
|
2681
|
+
let staticWriteCount = 0;
|
|
2682
|
+
const rfFileList = fs.readdirSync(rfImportDir).filter((f) => f.endsWith('.json'));
|
|
2683
|
+
for (const rfFile of rfFileList) {
|
|
2684
|
+
const rfData = fs.readJsonSync(`${rfImportDir}/${rfFile}`);
|
|
2685
|
+
const itemId = nodePath.basename(rfFile, '.json');
|
|
2686
|
+
const olFile = `${olImportDir}/${itemId}.json`;
|
|
2687
|
+
if (!fs.existsSync(olFile)) {
|
|
2688
|
+
logger.warn(`Skipping static asset generation for '${itemId}' — no matching object-layer file`);
|
|
2689
|
+
continue;
|
|
2690
|
+
}
|
|
2691
|
+
const olData = fs.readJsonSync(olFile);
|
|
2692
|
+
const itemType = olData.data?.item?.type;
|
|
2693
|
+
if (!itemType) {
|
|
2694
|
+
logger.warn(`Skipping static asset generation for '${itemId}' — missing data.item.type`);
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2697
|
+
// rfData matches the ObjectLayerRenderFrames schema: { frames, colors, frame_duration }
|
|
2698
|
+
const objectLayerRenderFramesData = {
|
|
2699
|
+
frames: rfData.frames || {},
|
|
2700
|
+
colors: rfData.colors || [],
|
|
2701
|
+
frame_duration: rfData.frame_duration ?? 100,
|
|
2702
|
+
};
|
|
2703
|
+
try {
|
|
2704
|
+
const written = await ObjectLayerEngine.writeStaticFrameAssets({
|
|
2705
|
+
basePaths: [srcBasePath, publicBasePath],
|
|
2706
|
+
itemType,
|
|
2707
|
+
itemId,
|
|
2708
|
+
objectLayerRenderFramesData,
|
|
2709
|
+
objectLayerData: olData,
|
|
2710
|
+
cellPixelDim: 20,
|
|
2711
|
+
});
|
|
2712
|
+
staticWriteCount += written.length;
|
|
2713
|
+
} catch (err) {
|
|
2714
|
+
logger.warn(`Failed to write static assets for '${itemId}': ${err.message}`);
|
|
2715
|
+
}
|
|
2155
2716
|
}
|
|
2156
|
-
logger.info(`
|
|
2717
|
+
logger.info(`Static frame PNGs written: ${staticWriteCount} file(s) across src/client/public and public/`);
|
|
2157
2718
|
}
|
|
2158
2719
|
|
|
2159
|
-
//
|
|
2720
|
+
// 5. Import maps (preserveUUID: delete by code then create with exact _id)
|
|
2160
2721
|
const mapsDir = `${backupDir}/maps`;
|
|
2161
2722
|
if (fs.existsSync(mapsDir)) {
|
|
2162
2723
|
const mapFiles = fs.readdirSync(mapsDir).filter((f) => f.endsWith('.json'));
|
|
2163
2724
|
let mapCount = 0;
|
|
2164
2725
|
for (const file of mapFiles) {
|
|
2165
2726
|
const mapData = fs.readJsonSync(`${mapsDir}/${file}`);
|
|
2166
|
-
// Remove any existing map with this code (may have different _id)
|
|
2167
2727
|
await CyberiaMap.deleteOne({ code: mapData.code });
|
|
2168
|
-
// Also remove if an old doc with this _id exists
|
|
2169
2728
|
await CyberiaMap.deleteOne({ _id: mapData._id });
|
|
2170
2729
|
await CyberiaMap.create(mapData);
|
|
2171
2730
|
mapCount++;
|
|
@@ -2173,6 +2732,18 @@ try {
|
|
|
2173
2732
|
logger.info(`Imported ${mapCount} CyberiaMap document(s)`);
|
|
2174
2733
|
}
|
|
2175
2734
|
|
|
2735
|
+
// 6. Import CyberiaInstanceConf (skillRules, equipmentRules, entityDefaults, etc.)
|
|
2736
|
+
const confImportPath = `${backupDir}/cyberia-instance-conf.json`;
|
|
2737
|
+
if (fs.existsSync(confImportPath)) {
|
|
2738
|
+
const confData = fs.readJsonSync(confImportPath);
|
|
2739
|
+
if (confData._id) await CyberiaInstanceConf.deleteOne({ _id: confData._id });
|
|
2740
|
+
await CyberiaInstanceConf.deleteOne({ instanceCode: confData.instanceCode });
|
|
2741
|
+
await CyberiaInstanceConf.create(confData);
|
|
2742
|
+
logger.info('Imported CyberiaInstanceConf', { instanceCode: confData.instanceCode });
|
|
2743
|
+
} else {
|
|
2744
|
+
logger.warn(`CyberiaInstanceConf backup not found: ${confImportPath}`);
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2176
2747
|
// 7. Import instance (preserveUUID: delete by code then create with exact _id)
|
|
2177
2748
|
const instancePath = `${backupDir}/cyberia-instance.json`;
|
|
2178
2749
|
if (fs.existsSync(instancePath)) {
|
|
@@ -2185,6 +2756,283 @@ try {
|
|
|
2185
2756
|
logger.warn(`Instance file not found: ${instancePath}`);
|
|
2186
2757
|
}
|
|
2187
2758
|
|
|
2759
|
+
// 8. Import CyberiaDialogue documents
|
|
2760
|
+
const dialoguesDir = `${backupDir}/cyberia-dialogues`;
|
|
2761
|
+
if (fs.existsSync(dialoguesDir)) {
|
|
2762
|
+
const dialogueFiles = fs.readdirSync(dialoguesDir).filter((f) => f.endsWith('.json'));
|
|
2763
|
+
let dialogueCount = 0;
|
|
2764
|
+
|
|
2765
|
+
for (const file of dialogueFiles) {
|
|
2766
|
+
const rawDialogueData = fs.readJsonSync(`${dialoguesDir}/${file}`);
|
|
2767
|
+
const dialogues = Array.isArray(rawDialogueData) ? rawDialogueData : [rawDialogueData];
|
|
2768
|
+
const dialogueCodes = [...new Set(dialogues.map((dialogue) => dialogue.code).filter(Boolean))];
|
|
2769
|
+
if (dialogueCodes.length === 0) {
|
|
2770
|
+
logger.warn(`Skipping CyberiaDialogue backup without code: ${file}`);
|
|
2771
|
+
continue;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
await CyberiaDialogue.deleteMany({ code: { $in: dialogueCodes } });
|
|
2775
|
+
|
|
2776
|
+
const dialogueIds = dialogues.map((dialogue) => dialogue._id).filter(Boolean);
|
|
2777
|
+
if (dialogueIds.length > 0) {
|
|
2778
|
+
await CyberiaDialogue.deleteMany({ _id: { $in: dialogueIds } });
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
await CyberiaDialogue.create(dialogues);
|
|
2782
|
+
dialogueCount += dialogues.length;
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
logger.info(`Imported ${dialogueCount} CyberiaDialogue document(s)`);
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
// 9. Restore IPFS pin records and payloads
|
|
2789
|
+
const ipfsFile = `${backupDir}/ipfs/pins.json`;
|
|
2790
|
+
if (fs.existsSync(ipfsFile)) {
|
|
2791
|
+
const ipfsDocs = fs.readJsonSync(ipfsFile);
|
|
2792
|
+
const ipfsContentDir = `${backupDir}/ipfs/content`;
|
|
2793
|
+
let ipfsCount = 0;
|
|
2794
|
+
let ipfsSkipped = 0;
|
|
2795
|
+
|
|
2796
|
+
const backupPins = new Map();
|
|
2797
|
+
for (const doc of ipfsDocs) {
|
|
2798
|
+
const resourceType = inferResourceType(doc);
|
|
2799
|
+
if (!resourceType) {
|
|
2800
|
+
logger.warn(
|
|
2801
|
+
`Ipfs record is missing resourceType and cannot be inferred (cid: ${doc.cid}, mfsPath: ${doc.mfsPath ?? '(none)'}) — skipping`,
|
|
2802
|
+
);
|
|
2803
|
+
ipfsSkipped++;
|
|
2804
|
+
continue;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
const mfsPaths = collectMfsPaths(doc);
|
|
2808
|
+
if (mfsPaths.length === 0) {
|
|
2809
|
+
upsertCanonicalPinEntry(backupPins, { cid: doc.cid, resourceType, mfsPath: '' });
|
|
2810
|
+
} else {
|
|
2811
|
+
for (const mfsPath of mfsPaths) {
|
|
2812
|
+
upsertCanonicalPinEntry(backupPins, { cid: doc.cid, resourceType, mfsPath });
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
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
|
+
|
|
2823
|
+
const restoreAdditionalMfsPaths = async (cid, mfsPaths, primaryPath) => {
|
|
2824
|
+
let restoredCount = 0;
|
|
2825
|
+
for (const mfsPath of mfsPaths) {
|
|
2826
|
+
if (!mfsPath || mfsPath === primaryPath) continue;
|
|
2827
|
+
const ok = await IpfsClient.restoreMfsPath(cid, mfsPath);
|
|
2828
|
+
if (ok) restoredCount++;
|
|
2829
|
+
}
|
|
2830
|
+
return restoredCount;
|
|
2831
|
+
};
|
|
2832
|
+
|
|
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
|
+
if (fs.existsSync(ipfsContentDir)) {
|
|
2840
|
+
let cidRewriteCount = 0;
|
|
2841
|
+
let extraMfsRestoreCount = 0;
|
|
2842
|
+
|
|
2843
|
+
for (const [index, doc] of backupPinEntries.entries()) {
|
|
2844
|
+
const mfsPaths = collectMfsPaths(doc);
|
|
2845
|
+
const primaryPath = mfsPaths[0] || '';
|
|
2846
|
+
const payloadPath = `${ipfsContentDir}/${doc.cid}.bin`;
|
|
2847
|
+
|
|
2848
|
+
logger.info('IPFS raw payload restore start', {
|
|
2849
|
+
index: index + 1,
|
|
2850
|
+
total: backupPinEntries.length,
|
|
2851
|
+
cid: doc.cid,
|
|
2852
|
+
resourceType: doc.resourceType,
|
|
2853
|
+
mfsPath: primaryPath || null,
|
|
2854
|
+
});
|
|
2855
|
+
|
|
2856
|
+
if (!fs.existsSync(payloadPath)) {
|
|
2857
|
+
logger.warn('IPFS raw payload file missing from backup', {
|
|
2858
|
+
cid: doc.cid,
|
|
2859
|
+
resourceType: doc.resourceType,
|
|
2860
|
+
mfsPath: primaryPath || null,
|
|
2861
|
+
});
|
|
2862
|
+
ipfsSkipped++;
|
|
2863
|
+
continue;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
const addResult = await IpfsClient.addToIpfs(
|
|
2867
|
+
fs.readFileSync(payloadPath),
|
|
2868
|
+
nodePath.basename(primaryPath || doc.cid),
|
|
2869
|
+
primaryPath || undefined,
|
|
2870
|
+
);
|
|
2871
|
+
|
|
2872
|
+
if (!addResult?.cid) {
|
|
2873
|
+
logger.warn('IPFS raw payload restore failed', {
|
|
2874
|
+
cid: doc.cid,
|
|
2875
|
+
resourceType: doc.resourceType,
|
|
2876
|
+
mfsPath: primaryPath || null,
|
|
2877
|
+
});
|
|
2878
|
+
ipfsSkipped++;
|
|
2879
|
+
continue;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
const finalCid = addResult.cid;
|
|
2883
|
+
if (doc.cid !== finalCid) {
|
|
2884
|
+
await rewriteImportedCidReferences({
|
|
2885
|
+
oldCid: doc.cid,
|
|
2886
|
+
newCid: finalCid,
|
|
2887
|
+
resourceType: doc.resourceType,
|
|
2888
|
+
});
|
|
2889
|
+
cidRewriteCount++;
|
|
2890
|
+
logger.warn('IPFS raw payload CID mismatch during import; rewriting imported references', {
|
|
2891
|
+
oldCid: doc.cid,
|
|
2892
|
+
newCid: finalCid,
|
|
2893
|
+
resourceType: doc.resourceType,
|
|
2894
|
+
mfsPath: primaryPath || null,
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
extraMfsRestoreCount += await restoreAdditionalMfsPaths(finalCid, mfsPaths, primaryPath);
|
|
2899
|
+
await upsertImportedPin({ cid: finalCid, resourceType: doc.resourceType, mfsPath: primaryPath });
|
|
2900
|
+
ipfsCount++;
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
logger.info(
|
|
2904
|
+
`Imported ${ipfsCount} Ipfs pin record(s) from exact backup payloads${ipfsSkipped ? `, skipped ${ipfsSkipped}` : ''}`,
|
|
2905
|
+
);
|
|
2906
|
+
logger.info(
|
|
2907
|
+
`IPFS raw payload restore: ${ipfsCount}/${backupPinEntries.length} record(s) restored, ${extraMfsRestoreCount} additional MFS path(s) restored${cidRewriteCount ? `, ${cidRewriteCount} CID rewrite(s)` : ''}`,
|
|
2908
|
+
);
|
|
2909
|
+
} else {
|
|
2910
|
+
logger.warn(
|
|
2911
|
+
'Backup has no raw IPFS payload files under ipfs/content/. Rebuilding a canonical IPFS layout from imported ObjectLayer, AtlasSpriteSheet, and File documents.',
|
|
2912
|
+
);
|
|
2913
|
+
|
|
2914
|
+
const importedItemIds = fs.existsSync(olDir)
|
|
2915
|
+
? fs
|
|
2916
|
+
.readdirSync(olDir)
|
|
2917
|
+
.filter((f) => f.endsWith('.json'))
|
|
2918
|
+
.map((f) => nodePath.basename(f, '.json'))
|
|
2919
|
+
: [];
|
|
2920
|
+
const importedObjectLayers = importedItemIds.length
|
|
2921
|
+
? await ObjectLayer.find({ 'data.item.id': { $in: importedItemIds } }).lean()
|
|
2922
|
+
: [];
|
|
2923
|
+
|
|
2924
|
+
let rebuiltObjectLayers = 0;
|
|
2925
|
+
|
|
2926
|
+
for (const [index, objectLayerDoc] of importedObjectLayers.entries()) {
|
|
2927
|
+
const itemKey = objectLayerDoc.data?.item?.id || objectLayerDoc._id.toString();
|
|
2928
|
+
const itemPaths = getCanonicalIpfsPaths(itemKey);
|
|
2929
|
+
const updatedData = newInstance(objectLayerDoc.data || {});
|
|
2930
|
+
if (!updatedData.render) updatedData.render = {};
|
|
2931
|
+
|
|
2932
|
+
logger.info('IPFS legacy canonical rebuild start', {
|
|
2933
|
+
index: index + 1,
|
|
2934
|
+
total: importedObjectLayers.length,
|
|
2935
|
+
itemKey,
|
|
2936
|
+
});
|
|
2937
|
+
|
|
2938
|
+
let atlasCid = '';
|
|
2939
|
+
let atlasMetadataCid = '';
|
|
2940
|
+
|
|
2941
|
+
if (objectLayerDoc.atlasSpriteSheetId) {
|
|
2942
|
+
const atlasDoc = await AtlasSpriteSheet.findById(objectLayerDoc.atlasSpriteSheetId).lean();
|
|
2943
|
+
if (atlasDoc) {
|
|
2944
|
+
const atlasFile = atlasDoc.fileId ? await File.findById(atlasDoc.fileId).lean() : null;
|
|
2945
|
+
const atlasBuffer = toBuffer(atlasFile?.data);
|
|
2946
|
+
|
|
2947
|
+
if (atlasBuffer) {
|
|
2948
|
+
const atlasAddResult = await IpfsClient.addBufferToIpfs(
|
|
2949
|
+
atlasBuffer,
|
|
2950
|
+
`${itemKey}_atlas_sprite_sheet.png`,
|
|
2951
|
+
itemPaths.atlasSpriteSheet,
|
|
2952
|
+
);
|
|
2953
|
+
if (atlasAddResult?.cid) {
|
|
2954
|
+
atlasCid = atlasAddResult.cid;
|
|
2955
|
+
await AtlasSpriteSheet.updateOne({ _id: atlasDoc._id }, { $set: { cid: atlasCid } });
|
|
2956
|
+
await createPinRecord({
|
|
2957
|
+
cid: atlasCid,
|
|
2958
|
+
resourceType: 'atlas-sprite-sheet',
|
|
2959
|
+
mfsPath: itemPaths.atlasSpriteSheet,
|
|
2960
|
+
options: { host, path },
|
|
2961
|
+
});
|
|
2962
|
+
ipfsCount++;
|
|
2963
|
+
} else {
|
|
2964
|
+
logger.warn(`Failed to rebuild atlas sprite sheet payload for '${itemKey}'`);
|
|
2965
|
+
}
|
|
2966
|
+
} else if (atlasDoc.fileId) {
|
|
2967
|
+
logger.warn(`Atlas File payload missing for '${itemKey}'`);
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
const atlasMetadataResult = await IpfsClient.addJsonToIpfs(
|
|
2971
|
+
atlasDoc.metadata || {},
|
|
2972
|
+
`${itemKey}_atlas_sprite_sheet_metadata.json`,
|
|
2973
|
+
itemPaths.atlasMetadata,
|
|
2974
|
+
);
|
|
2975
|
+
if (atlasMetadataResult?.cid) {
|
|
2976
|
+
atlasMetadataCid = atlasMetadataResult.cid;
|
|
2977
|
+
await createPinRecord({
|
|
2978
|
+
cid: atlasMetadataCid,
|
|
2979
|
+
resourceType: 'atlas-metadata',
|
|
2980
|
+
mfsPath: itemPaths.atlasMetadata,
|
|
2981
|
+
options: { host, path },
|
|
2982
|
+
});
|
|
2983
|
+
ipfsCount++;
|
|
2984
|
+
} else {
|
|
2985
|
+
logger.warn(`Failed to rebuild atlas metadata payload for '${itemKey}'`);
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
if (atlasCid) {
|
|
2991
|
+
updatedData.render.cid = atlasCid;
|
|
2992
|
+
} else {
|
|
2993
|
+
delete updatedData.render.cid;
|
|
2994
|
+
}
|
|
2995
|
+
if (atlasMetadataCid) {
|
|
2996
|
+
updatedData.render.metadataCid = atlasMetadataCid;
|
|
2997
|
+
} else {
|
|
2998
|
+
delete updatedData.render.metadataCid;
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
const objectLayerAddResult = await IpfsClient.addJsonToIpfs(
|
|
3002
|
+
updatedData,
|
|
3003
|
+
`${itemKey}_data.json`,
|
|
3004
|
+
itemPaths.objectLayerData,
|
|
3005
|
+
);
|
|
3006
|
+
if (objectLayerAddResult?.cid) {
|
|
3007
|
+
await ObjectLayer.updateOne(
|
|
3008
|
+
{ _id: objectLayerDoc._id },
|
|
3009
|
+
{
|
|
3010
|
+
$set: {
|
|
3011
|
+
cid: objectLayerAddResult.cid,
|
|
3012
|
+
data: updatedData,
|
|
3013
|
+
},
|
|
3014
|
+
},
|
|
3015
|
+
);
|
|
3016
|
+
await createPinRecord({
|
|
3017
|
+
cid: objectLayerAddResult.cid,
|
|
3018
|
+
resourceType: 'object-layer-data',
|
|
3019
|
+
mfsPath: itemPaths.objectLayerData,
|
|
3020
|
+
options: { host, path },
|
|
3021
|
+
});
|
|
3022
|
+
ipfsCount++;
|
|
3023
|
+
rebuiltObjectLayers++;
|
|
3024
|
+
} else {
|
|
3025
|
+
logger.warn(`Failed to rebuild object-layer-data payload for '${itemKey}'`);
|
|
3026
|
+
ipfsSkipped++;
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
logger.info(
|
|
3031
|
+
`Legacy IPFS rebuild: ${rebuiltObjectLayers}/${importedObjectLayers.length} ObjectLayer payload(s) rebuilt, ${ipfsCount} canonical pin record(s) upserted${ipfsSkipped ? `, skipped ${ipfsSkipped}` : ''}`,
|
|
3032
|
+
);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
|
|
2188
3036
|
logger.info('Instance import completed', { backupDir });
|
|
2189
3037
|
}
|
|
2190
3038
|
|
|
@@ -2201,13 +3049,41 @@ try {
|
|
|
2201
3049
|
// Collect thumbnail File IDs to drop
|
|
2202
3050
|
const thumbFileIds = [];
|
|
2203
3051
|
if (existingInstance.thumbnail) thumbFileIds.push(existingInstance.thumbnail);
|
|
3052
|
+
const dropOlItemIds = new Set();
|
|
2204
3053
|
|
|
2205
3054
|
// Query other instances for shared thumbnail exclusion
|
|
2206
3055
|
const otherInstances = await CyberiaInstance.find({ code: { $ne: instanceCode } }, { thumbnail: 1 }).lean();
|
|
2207
3056
|
|
|
3057
|
+
// Add instance-level itemIds (may not appear in any map entity)
|
|
3058
|
+
for (const id of existingInstance.itemIds || []) if (id) dropOlItemIds.add(id);
|
|
3059
|
+
|
|
3060
|
+
// Add conf entityDefaults and skillConfig itemIds (liveItemIds, deadItemIds, dropItemIds, defaultObjectLayers)
|
|
3061
|
+
const existingConf =
|
|
3062
|
+
(await CyberiaInstanceConf.findOne({ instanceCode }).lean()) ||
|
|
3063
|
+
(existingInstance.conf ? await CyberiaInstanceConf.findById(existingInstance.conf).lean() : null);
|
|
3064
|
+
if (existingConf) {
|
|
3065
|
+
for (const ed of existingConf.entityDefaults || []) {
|
|
3066
|
+
for (const id of ed.liveItemIds || []) if (id) dropOlItemIds.add(id);
|
|
3067
|
+
for (const id of ed.deadItemIds || []) if (id) dropOlItemIds.add(id);
|
|
3068
|
+
for (const id of ed.dropItemIds || []) if (id) dropOlItemIds.add(id);
|
|
3069
|
+
for (const slot of ed.defaultObjectLayers || []) if (slot.itemId) dropOlItemIds.add(slot.itemId);
|
|
3070
|
+
}
|
|
3071
|
+
for (const sc of existingConf.skillConfig || []) {
|
|
3072
|
+
if (sc.triggerItemId) dropOlItemIds.add(sc.triggerItemId);
|
|
3073
|
+
for (const skill of sc.skills || []) {
|
|
3074
|
+
if (skill.summonedEntityItemId && !skill.summonedEntityItemId.startsWith('$'))
|
|
3075
|
+
dropOlItemIds.add(skill.summonedEntityItemId);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
const otherMaps = await CyberiaMap.find(
|
|
3081
|
+
{ code: { $nin: [...dropMapCodes] } },
|
|
3082
|
+
{ 'entities.objectLayerItemIds': 1, thumbnail: 1 },
|
|
3083
|
+
).lean();
|
|
3084
|
+
|
|
2208
3085
|
if (dropMapCodes.size > 0) {
|
|
2209
3086
|
const dropMaps = await CyberiaMap.find({ code: { $in: [...dropMapCodes] } }).lean();
|
|
2210
|
-
const dropOlItemIds = new Set();
|
|
2211
3087
|
for (const map of dropMaps) {
|
|
2212
3088
|
if (map.thumbnail) thumbFileIds.push(map.thumbnail);
|
|
2213
3089
|
for (const entity of map.entities || []) {
|
|
@@ -2216,103 +3092,102 @@ try {
|
|
|
2216
3092
|
}
|
|
2217
3093
|
}
|
|
2218
3094
|
}
|
|
3095
|
+
const mapResult = await CyberiaMap.deleteMany({ code: { $in: [...dropMapCodes] } });
|
|
3096
|
+
logger.info(`Dropped ${mapResult.deletedCount} CyberiaMap document(s)`);
|
|
3097
|
+
}
|
|
2219
3098
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
for (const m of otherMaps) {
|
|
2227
|
-
for (const entity of m.entities || []) {
|
|
2228
|
-
for (const itemId of entity.objectLayerItemIds || []) {
|
|
2229
|
-
if (dropOlItemIds.has(itemId)) sharedOlItemIds.add(itemId);
|
|
2230
|
-
}
|
|
3099
|
+
// Exclude OL item IDs referenced by maps outside this instance
|
|
3100
|
+
const sharedOlItemIds = new Set();
|
|
3101
|
+
for (const m of otherMaps) {
|
|
3102
|
+
for (const entity of m.entities || []) {
|
|
3103
|
+
for (const itemId of entity.objectLayerItemIds || []) {
|
|
3104
|
+
if (dropOlItemIds.has(itemId)) sharedOlItemIds.add(itemId);
|
|
2231
3105
|
}
|
|
2232
3106
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
}
|
|
3107
|
+
}
|
|
3108
|
+
for (const shared of sharedOlItemIds) dropOlItemIds.delete(shared);
|
|
3109
|
+
if (sharedOlItemIds.size > 0) {
|
|
3110
|
+
logger.info(`Preserved ${sharedOlItemIds.size} ObjectLayer(s) shared with other maps`);
|
|
3111
|
+
}
|
|
2237
3112
|
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
3113
|
+
// Exclude thumbnail File IDs referenced by other instances or maps
|
|
3114
|
+
const otherMapThumbs = otherMaps.map((m) => m.thumbnail?.toString()).filter(Boolean);
|
|
3115
|
+
const otherInstThumbs = otherInstances.map((i) => i.thumbnail?.toString()).filter(Boolean);
|
|
3116
|
+
const sharedThumbIds = new Set([...otherMapThumbs, ...otherInstThumbs]);
|
|
3117
|
+
for (let i = thumbFileIds.length - 1; i >= 0; i--) {
|
|
3118
|
+
if (sharedThumbIds.has(thumbFileIds[i].toString())) thumbFileIds.splice(i, 1);
|
|
3119
|
+
}
|
|
2245
3120
|
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
3121
|
+
if (dropOlItemIds.size > 0) {
|
|
3122
|
+
const dropDialogueCodes = [...dropOlItemIds].map((id) => `default-${id}`);
|
|
3123
|
+
const dialogueResult = await CyberiaDialogue.deleteMany({ code: { $in: dropDialogueCodes } });
|
|
3124
|
+
logger.info(`Dropped ${dialogueResult.deletedCount} CyberiaDialogue document(s)`);
|
|
3125
|
+
|
|
3126
|
+
const olDocs = await ObjectLayer.find(
|
|
3127
|
+
{ 'data.item.id': { $in: [...dropOlItemIds] } },
|
|
3128
|
+
{
|
|
3129
|
+
cid: 1,
|
|
3130
|
+
'data.item.id': 1,
|
|
3131
|
+
'data.render': 1,
|
|
3132
|
+
objectLayerRenderFramesId: 1,
|
|
3133
|
+
atlasSpriteSheetId: 1,
|
|
3134
|
+
},
|
|
3135
|
+
).lean();
|
|
2257
3136
|
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
3137
|
+
const cidsToUnpin = new Set();
|
|
3138
|
+
const renderFrameIds = [];
|
|
3139
|
+
const atlasIds = [];
|
|
3140
|
+
const itemKeysToClean = new Set();
|
|
2262
3141
|
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
3142
|
+
for (const doc of olDocs) {
|
|
3143
|
+
if (doc.cid) cidsToUnpin.add(doc.cid);
|
|
3144
|
+
if (doc.data?.render?.cid) cidsToUnpin.add(doc.data.render.cid);
|
|
3145
|
+
if (doc.data?.render?.metadataCid) cidsToUnpin.add(doc.data.render.metadataCid);
|
|
3146
|
+
if (doc.data?.item?.id) itemKeysToClean.add(doc.data.item.id);
|
|
3147
|
+
if (doc.objectLayerRenderFramesId) renderFrameIds.push(doc.objectLayerRenderFramesId);
|
|
3148
|
+
if (doc.atlasSpriteSheetId) atlasIds.push(doc.atlasSpriteSheetId);
|
|
3149
|
+
}
|
|
2271
3150
|
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
}
|
|
2278
|
-
if (atlasFileIds.length > 0) {
|
|
2279
|
-
const fileResult = await File.deleteMany({ _id: { $in: atlasFileIds } });
|
|
2280
|
-
logger.info(`Dropped ${fileResult.deletedCount} File document(s) (atlas)`);
|
|
2281
|
-
}
|
|
2282
|
-
const atlasResult = await AtlasSpriteSheet.deleteMany({ _id: { $in: atlasIds } });
|
|
2283
|
-
logger.info(`Dropped ${atlasResult.deletedCount} AtlasSpriteSheet document(s)`);
|
|
3151
|
+
if (atlasIds.length > 0) {
|
|
3152
|
+
const atlasDocs = await AtlasSpriteSheet.find({ _id: { $in: atlasIds } }, { fileId: 1, cid: 1 }).lean();
|
|
3153
|
+
const atlasFileIds = atlasDocs.map((a) => a.fileId).filter(Boolean);
|
|
3154
|
+
for (const atlas of atlasDocs) {
|
|
3155
|
+
if (atlas.cid) cidsToUnpin.add(atlas.cid);
|
|
2284
3156
|
}
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
logger.info(`Dropped ${rfResult.deletedCount} ObjectLayerRenderFrames document(s)`);
|
|
3157
|
+
if (atlasFileIds.length > 0) {
|
|
3158
|
+
const fileResult = await File.deleteMany({ _id: { $in: atlasFileIds } });
|
|
3159
|
+
logger.info(`Dropped ${fileResult.deletedCount} File document(s) (atlas)`);
|
|
2289
3160
|
}
|
|
3161
|
+
const atlasResult = await AtlasSpriteSheet.deleteMany({ _id: { $in: atlasIds } });
|
|
3162
|
+
logger.info(`Dropped ${atlasResult.deletedCount} AtlasSpriteSheet document(s)`);
|
|
3163
|
+
}
|
|
2290
3164
|
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
3165
|
+
if (renderFrameIds.length > 0) {
|
|
3166
|
+
const rfResult = await ObjectLayerRenderFrames.deleteMany({ _id: { $in: renderFrameIds } });
|
|
3167
|
+
logger.info(`Dropped ${rfResult.deletedCount} ObjectLayerRenderFrames document(s)`);
|
|
3168
|
+
}
|
|
2295
3169
|
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
}
|
|
2301
|
-
let mfsCount = 0;
|
|
2302
|
-
for (const itemKey of itemKeysToClean) {
|
|
2303
|
-
const ok = await IpfsClient.removeMfsPath(`/object-layer/${itemKey}`);
|
|
2304
|
-
if (ok) mfsCount++;
|
|
2305
|
-
}
|
|
2306
|
-
logger.info(
|
|
2307
|
-
`IPFS cleanup: ${unpinCount}/${cidsToUnpin.size} CIDs unpinned, ${mfsCount}/${itemKeysToClean.size} MFS paths removed`,
|
|
2308
|
-
);
|
|
3170
|
+
if (cidsToUnpin.size > 0) {
|
|
3171
|
+
const ipfsResult = await Ipfs.deleteMany({ cid: { $in: [...cidsToUnpin] } });
|
|
3172
|
+
logger.info(`Dropped ${ipfsResult.deletedCount} Ipfs pin record(s)`);
|
|
3173
|
+
}
|
|
2309
3174
|
|
|
2310
|
-
|
|
2311
|
-
|
|
3175
|
+
let unpinCount = 0;
|
|
3176
|
+
for (const cid of cidsToUnpin) {
|
|
3177
|
+
const ok = await IpfsClient.unpinCid(cid);
|
|
3178
|
+
if (ok) unpinCount++;
|
|
2312
3179
|
}
|
|
3180
|
+
let mfsCount = 0;
|
|
3181
|
+
for (const itemKey of itemKeysToClean) {
|
|
3182
|
+
const ok = await IpfsClient.removeMfsPath(`/object-layer/${itemKey}`);
|
|
3183
|
+
if (ok) mfsCount++;
|
|
3184
|
+
}
|
|
3185
|
+
logger.info(
|
|
3186
|
+
`IPFS cleanup: ${unpinCount}/${cidsToUnpin.size} CIDs unpinned, ${mfsCount}/${itemKeysToClean.size} MFS paths removed`,
|
|
3187
|
+
);
|
|
2313
3188
|
|
|
2314
|
-
const
|
|
2315
|
-
logger.info(`Dropped ${
|
|
3189
|
+
const olResult = await ObjectLayer.deleteMany({ 'data.item.id': { $in: [...dropOlItemIds] } });
|
|
3190
|
+
logger.info(`Dropped ${olResult.deletedCount} ObjectLayer document(s)`);
|
|
2316
3191
|
}
|
|
2317
3192
|
|
|
2318
3193
|
// Drop thumbnail File documents (instance + maps), excluding shared ones
|
|
@@ -2323,6 +3198,8 @@ try {
|
|
|
2323
3198
|
|
|
2324
3199
|
await CyberiaInstance.deleteOne({ code: instanceCode });
|
|
2325
3200
|
logger.info('Dropped CyberiaInstance', { code: instanceCode });
|
|
3201
|
+
await CyberiaInstanceConf.deleteOne({ instanceCode });
|
|
3202
|
+
logger.info('Dropped CyberiaInstanceConf', { instanceCode });
|
|
2326
3203
|
} else {
|
|
2327
3204
|
logger.info('No existing instance to drop', { code: instanceCode });
|
|
2328
3205
|
}
|
|
@@ -2332,7 +3209,106 @@ try {
|
|
|
2332
3209
|
logger.error('Specify --export, --import, or --drop flag');
|
|
2333
3210
|
}
|
|
2334
3211
|
|
|
2335
|
-
await
|
|
3212
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
3213
|
+
});
|
|
3214
|
+
|
|
3215
|
+
// ── client-hints: presentation hints management ──────────────────────────
|
|
3216
|
+
program
|
|
3217
|
+
.command('client-hints [instance-code]')
|
|
3218
|
+
.option('--export [path]', 'Export CyberiaClientHints document to JSON (default: ./client-hints-<code>.json)')
|
|
3219
|
+
.option('--import [path]', 'Upsert CyberiaClientHints from a JSON file')
|
|
3220
|
+
.option('--seed-defaults', 'Upsert canonical presentation-hint defaults for the given instance code')
|
|
3221
|
+
.option('--drop', 'Remove the CyberiaClientHints document for the given instance code')
|
|
3222
|
+
.option('--env-path <env-path>', 'Env path e.g. ./engine-private/conf/dd-cyberia/.env.development')
|
|
3223
|
+
.option('--mongo-host <mongo-host>', 'Mongo host override')
|
|
3224
|
+
.option('--dev', 'Force development environment')
|
|
3225
|
+
.description('Manage per-instance client presentation hints (palette, camera, status icons, interpolation)')
|
|
3226
|
+
.action(async (instanceCode, options = {}) => {
|
|
3227
|
+
try {
|
|
3228
|
+
const envPath =
|
|
3229
|
+
options.envPath || `./engine-private/conf/dd-cyberia/.env.${options.dev ? 'development' : 'production'}`;
|
|
3230
|
+
if (fs.existsSync(envPath)) dotenv.config({ path: envPath, override: true });
|
|
3231
|
+
|
|
3232
|
+
const { CYBERIA_CLIENT_HINTS_DEFAULTS, buildClientHints } =
|
|
3233
|
+
await import('../src/client/components/cyberia/SharedDefaultsCyberia.js');
|
|
3234
|
+
|
|
3235
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
3236
|
+
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
3237
|
+
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
3238
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
3239
|
+
if (!fs.existsSync(confServerPath)) throw new Error(`Config not found: ${confServerPath}`);
|
|
3240
|
+
const confServer = loadConfServerJson(confServerPath, { resolve: true });
|
|
3241
|
+
const { db } = confServer[host][path];
|
|
3242
|
+
db.host = options.mongoHost ? options.mongoHost : db.host.replace('127.0.0.1', 'mongodb-0.mongodb-service');
|
|
3243
|
+
|
|
3244
|
+
await DataBaseProviderService.load({ apis: ['cyberia-client-hints'], host, path, db });
|
|
3245
|
+
const CyberiaClientHints = DataBaseProviderService.getModel('cyberia-client-hints', { host, path });
|
|
3246
|
+
|
|
3247
|
+
if (!instanceCode && !options.seedDefaults) {
|
|
3248
|
+
logger.error('instance-code required for client-hints operations (omit only with --seed-defaults on all)');
|
|
3249
|
+
process.exit(1);
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
if (options.drop) {
|
|
3253
|
+
if (!instanceCode) {
|
|
3254
|
+
logger.error('instance-code required for --drop');
|
|
3255
|
+
process.exit(1);
|
|
3256
|
+
}
|
|
3257
|
+
const result = await CyberiaClientHints.deleteOne({ code: instanceCode });
|
|
3258
|
+
logger.info(`client-hints --drop: removed ${result.deletedCount} document(s) for code="${instanceCode}"`);
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
if (options.seedDefaults) {
|
|
3262
|
+
const codes = instanceCode ? [instanceCode] : [];
|
|
3263
|
+
if (codes.length === 0) {
|
|
3264
|
+
logger.error('instance-code required for --seed-defaults');
|
|
3265
|
+
process.exit(1);
|
|
3266
|
+
}
|
|
3267
|
+
for (const code of codes) {
|
|
3268
|
+
await CyberiaClientHints.findOneAndUpdate(
|
|
3269
|
+
{ code },
|
|
3270
|
+
{ $setOnInsert: { code, ...CYBERIA_CLIENT_HINTS_DEFAULTS } },
|
|
3271
|
+
{ upsert: true, returnDocument: 'after' },
|
|
3272
|
+
);
|
|
3273
|
+
logger.info(`client-hints --seed-defaults: seeded defaults for code="${code}"`);
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
if (options.import) {
|
|
3278
|
+
const filePath = typeof options.import === 'string' ? options.import : `./client-hints-${instanceCode}.json`;
|
|
3279
|
+
if (!fs.existsSync(filePath)) throw new Error(`Import file not found: ${filePath}`);
|
|
3280
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
3281
|
+
const code = data.code || instanceCode;
|
|
3282
|
+
if (!code) {
|
|
3283
|
+
logger.error('instance-code required (from file.code or CLI argument)');
|
|
3284
|
+
process.exit(1);
|
|
3285
|
+
}
|
|
3286
|
+
await CyberiaClientHints.findOneAndUpdate({ code }, { $set: { code, ...data } }, { upsert: true, new: true });
|
|
3287
|
+
logger.info(`client-hints --import: upserted code="${code}" from ${filePath}`);
|
|
3288
|
+
}
|
|
3289
|
+
|
|
3290
|
+
if (options.export) {
|
|
3291
|
+
if (!instanceCode) {
|
|
3292
|
+
logger.error('instance-code required for --export');
|
|
3293
|
+
process.exit(1);
|
|
3294
|
+
}
|
|
3295
|
+
const doc = await CyberiaClientHints.findOne({ code: instanceCode }).lean();
|
|
3296
|
+
if (!doc) {
|
|
3297
|
+
logger.warn(`No client-hints document found for code="${instanceCode}", exporting defaults`);
|
|
3298
|
+
}
|
|
3299
|
+
const outPath = typeof options.export === 'string' ? options.export : `./client-hints-${instanceCode}.json`;
|
|
3300
|
+
fs.writeFileSync(
|
|
3301
|
+
outPath,
|
|
3302
|
+
JSON.stringify(doc || { code: instanceCode, ...CYBERIA_CLIENT_HINTS_DEFAULTS }, null, 2),
|
|
3303
|
+
);
|
|
3304
|
+
logger.info(`client-hints --export: wrote ${outPath}`);
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
3308
|
+
} catch (err) {
|
|
3309
|
+
logger.error('client-hints command error:', err);
|
|
3310
|
+
process.exit(1);
|
|
3311
|
+
}
|
|
2336
3312
|
});
|
|
2337
3313
|
|
|
2338
3314
|
// ── chain: Hyperledger Besu / ERC-1155 lifecycle commands ────────────────
|
|
@@ -2532,7 +3508,7 @@ try {
|
|
|
2532
3508
|
logger.info(` SHA-256: ${resolved.sha256}`);
|
|
2533
3509
|
|
|
2534
3510
|
// Close the DB connection after resolving
|
|
2535
|
-
await
|
|
3511
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
2536
3512
|
} catch (dbErr) {
|
|
2537
3513
|
logger.error(`Failed to resolve canonical CID from database: ${dbErr.message}`);
|
|
2538
3514
|
process.exit(1);
|
|
@@ -3100,7 +4076,7 @@ try {
|
|
|
3100
4076
|
}
|
|
3101
4077
|
|
|
3102
4078
|
try {
|
|
3103
|
-
await
|
|
4079
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
3104
4080
|
} catch (_) {
|
|
3105
4081
|
/* ignore close errors */
|
|
3106
4082
|
}
|
|
@@ -3155,12 +4131,28 @@ try {
|
|
|
3155
4131
|
runner
|
|
3156
4132
|
.command('import-default-items')
|
|
3157
4133
|
.option('--dev', 'Force development environment (loads .env.development for IPFS localhost, etc.)')
|
|
3158
|
-
.description('Import default Object Layer items, skill config, and
|
|
4134
|
+
.description('Import default Object Layer items, skill config, dialogues, and client-hints into MongoDB')
|
|
3159
4135
|
.action(async (options) => {
|
|
4136
|
+
// Pre-flight: every item id referenced by the fallback world must
|
|
4137
|
+
// exist in DefaultCyberiaItems. Drift here causes silent missing
|
|
4138
|
+
// sprites at runtime, so fail loudly before we touch MongoDB.
|
|
4139
|
+
const { auditFallbackItemIds } = await import('../src/api/cyberia-instance/cyberia-fallback-world.js');
|
|
4140
|
+
const missing = auditFallbackItemIds();
|
|
4141
|
+
if (missing.length > 0) {
|
|
4142
|
+
logger.error(
|
|
4143
|
+
'import-default-items aborted: item ids referenced by defaults are missing from DefaultCyberiaItems:',
|
|
4144
|
+
missing.join(', '),
|
|
4145
|
+
'— add them to cyberia-server-defaults.js before seeding.',
|
|
4146
|
+
);
|
|
4147
|
+
process.exit(1);
|
|
4148
|
+
}
|
|
4149
|
+
|
|
3160
4150
|
const devFlag = options.dev ? ' --dev' : '';
|
|
4151
|
+
const instanceCode = process.env.INSTANCE_CODE || 'cyberia-main';
|
|
3161
4152
|
shellExec(`node bin/cyberia ol ${DefaultCyberiaItems.map((e) => e.item.id)} --import${devFlag}`);
|
|
3162
4153
|
shellExec(`node bin/cyberia run-workflow seed-skill-config${devFlag}`);
|
|
3163
4154
|
shellExec(`node bin/cyberia run-workflow seed-dialogues${devFlag}`);
|
|
4155
|
+
shellExec(`node bin/cyberia client-hints ${instanceCode} --seed-defaults${devFlag}`);
|
|
3164
4156
|
});
|
|
3165
4157
|
|
|
3166
4158
|
runner
|
|
@@ -3200,10 +4192,10 @@ try {
|
|
|
3200
4192
|
|
|
3201
4193
|
logger.info('seed-skill-config', { instanceCode, deployId, host, path, db });
|
|
3202
4194
|
|
|
3203
|
-
await
|
|
4195
|
+
await DataBaseProviderService.load({ apis: ['cyberia-instance', 'cyberia-instance-conf'], host, path, db });
|
|
3204
4196
|
|
|
3205
|
-
const CyberiaInstance =
|
|
3206
|
-
const CyberiaInstanceConf =
|
|
4197
|
+
const CyberiaInstance = DataBaseProviderService.getModel('cyberia-instance', { host, path });
|
|
4198
|
+
const CyberiaInstanceConf = DataBaseProviderService.getModel('cyberia-instance-conf', { host, path });
|
|
3207
4199
|
|
|
3208
4200
|
const instance = await CyberiaInstance.findOne({ code: instanceCode }).lean();
|
|
3209
4201
|
|
|
@@ -3231,7 +4223,7 @@ try {
|
|
|
3231
4223
|
DefaultSkillConfig.map((e) => `${e.triggerItemId} → [${e.logicEventIds.join(', ')}]`),
|
|
3232
4224
|
);
|
|
3233
4225
|
|
|
3234
|
-
await
|
|
4226
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
3235
4227
|
});
|
|
3236
4228
|
|
|
3237
4229
|
runner
|
|
@@ -3269,15 +4261,15 @@ try {
|
|
|
3269
4261
|
|
|
3270
4262
|
logger.info('seed-dialogues', { deployId, host, path, db });
|
|
3271
4263
|
|
|
3272
|
-
await
|
|
4264
|
+
await DataBaseProviderService.load({ apis: ['cyberia-dialogue'], host, path, db });
|
|
3273
4265
|
|
|
3274
|
-
const CyberiaDialogue =
|
|
4266
|
+
const CyberiaDialogue = DataBaseProviderService.getModel('cyberia-dialogue', { host, path });
|
|
3275
4267
|
|
|
3276
|
-
// Upsert each dialogue record keyed by (
|
|
4268
|
+
// Upsert each dialogue record keyed by (code, order) — idempotent.
|
|
3277
4269
|
let upserted = 0;
|
|
3278
4270
|
for (const dlg of DefaultCyberiaDialogues) {
|
|
3279
4271
|
await CyberiaDialogue.findOneAndUpdate(
|
|
3280
|
-
{
|
|
4272
|
+
{ code: dlg.code, order: dlg.order },
|
|
3281
4273
|
{ $set: { speaker: dlg.speaker, text: dlg.text, mood: dlg.mood } },
|
|
3282
4274
|
{ upsert: true },
|
|
3283
4275
|
);
|
|
@@ -3286,7 +4278,7 @@ try {
|
|
|
3286
4278
|
|
|
3287
4279
|
logger.info(`seed-dialogues: ${upserted} dialogue records upserted`);
|
|
3288
4280
|
|
|
3289
|
-
await
|
|
4281
|
+
await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
|
|
3290
4282
|
});
|
|
3291
4283
|
|
|
3292
4284
|
runner
|
|
@@ -3298,17 +4290,37 @@ try {
|
|
|
3298
4290
|
.description('Generate one procedural example of every registered semantic prefix')
|
|
3299
4291
|
.action(async (options) => {
|
|
3300
4292
|
const SEMANTIC_TYPES = [
|
|
3301
|
-
'floor-desert',
|
|
3302
|
-
'floor-grass',
|
|
4293
|
+
// 'floor-desert',
|
|
4294
|
+
// 'floor-grass',
|
|
3303
4295
|
// 'floor-water',
|
|
3304
4296
|
// 'floor-stone',
|
|
3305
4297
|
// 'floor-lava',
|
|
3306
4298
|
'skin-random',
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
4299
|
+
'skin-dark',
|
|
4300
|
+
'skin-light',
|
|
4301
|
+
'skin-vivid',
|
|
4302
|
+
'skin-natural',
|
|
3311
4303
|
'skin-shaved',
|
|
4304
|
+
// 'resource-desert-petal',
|
|
4305
|
+
// 'resource-desert-stone',
|
|
4306
|
+
// 'resource-desert-polygon',
|
|
4307
|
+
// 'resource-desert-thread',
|
|
4308
|
+
// 'resource-grass-petal',
|
|
4309
|
+
// 'resource-grass-stone',
|
|
4310
|
+
// 'resource-grass-polygon',
|
|
4311
|
+
// 'resource-grass-thread',
|
|
4312
|
+
// 'resource-water-petal',
|
|
4313
|
+
// 'resource-water-stone',
|
|
4314
|
+
// 'resource-water-polygon',
|
|
4315
|
+
// 'resource-water-thread',
|
|
4316
|
+
// 'resource-stone-petal',
|
|
4317
|
+
// 'resource-stone-stone',
|
|
4318
|
+
// 'resource-stone-polygon',
|
|
4319
|
+
// 'resource-stone-thread',
|
|
4320
|
+
// 'resource-lava-petal',
|
|
4321
|
+
// 'resource-lava-stone',
|
|
4322
|
+
// 'resource-lava-polygon',
|
|
4323
|
+
// 'resource-lava-thread',
|
|
3312
4324
|
];
|
|
3313
4325
|
|
|
3314
4326
|
const baseSeed = options.seed || 'example';
|
|
@@ -3330,17 +4342,91 @@ try {
|
|
|
3330
4342
|
logger.info('All semantic examples generated.');
|
|
3331
4343
|
});
|
|
3332
4344
|
|
|
3333
|
-
|
|
4345
|
+
runner
|
|
4346
|
+
.command('build-manifest')
|
|
4347
|
+
.option(
|
|
4348
|
+
'--dev',
|
|
4349
|
+
'Build dev-variant manifests (kind cluster, Dockerfile.dev). Default builds prod (kubeadm, Dockerfile).',
|
|
4350
|
+
)
|
|
4351
|
+
.description(
|
|
4352
|
+
'Build k8s resource manifests for the Cyberia mmo-server + mmo-client instances. ' +
|
|
4353
|
+
'Without --dev: production manifests (Dockerfile, kubeadm). With --dev: dev manifests (Dockerfile.dev, kind).',
|
|
4354
|
+
)
|
|
4355
|
+
.action((options) => {
|
|
4356
|
+
const isDev = !!options.dev;
|
|
4357
|
+
const flags = isDev ? '--kind --dev' : '--kubeadm';
|
|
4358
|
+
// shellExec is fail-fast by default: any non-zero exit throws
|
|
4359
|
+
// ShellExecError, which propagates to the outer catch and exits
|
|
4360
|
+
// the CLI non-zero — observable by GitHub Actions.
|
|
4361
|
+
shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-client,./cyberia-client' ${flags}`);
|
|
4362
|
+
shellExec(`node bin run instance-build-manifest 'dd-cyberia,mmo-server,./cyberia-server' ${flags}`);
|
|
4363
|
+
// Copy canonical doc sources into the generated project READMEs.
|
|
4364
|
+
// Edit the canonical sources; never hand-edit these generated outputs.
|
|
4365
|
+
fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-CLIENT.md', './cyberia-client/README.md');
|
|
4366
|
+
fs.copyFileSync('./src/client/public/cyberia-docs/CYBERIA-SERVER.md', './cyberia-server/README.md');
|
|
4367
|
+
logger.info(`run-workflow build-manifest complete (${isDev ? 'dev' : 'prod'})`);
|
|
4368
|
+
});
|
|
4369
|
+
|
|
4370
|
+
runner
|
|
4371
|
+
.command('build-server-dashboard')
|
|
4372
|
+
.option(
|
|
4373
|
+
'--dev',
|
|
4374
|
+
'Build a development variant of the dashboard with dev-specific env vars (e.g. localhost API endpoints).',
|
|
4375
|
+
)
|
|
4376
|
+
.option(
|
|
4377
|
+
'--output-path <path>',
|
|
4378
|
+
'Override output path for the rendered HTML (default: ./cyberia-server/public/index.html). ' +
|
|
4379
|
+
'Used by CI when this command is invoked from inside an engine checkout that lives ' +
|
|
4380
|
+
'alongside (not inside) the cyberia-server repo — pass e.g. ../public/index.html.',
|
|
4381
|
+
)
|
|
4382
|
+
.description('Build a static HTML dashboard for cyberia-server metrics and operational status. ')
|
|
4383
|
+
.action((options) => {
|
|
4384
|
+
const outputPath = options.outputPath || './cyberia-server/public/index.html';
|
|
4385
|
+
shellExec(
|
|
4386
|
+
`node bin static --page ./src/client/ssr/views/CyberiaServerMetrics.js` +
|
|
4387
|
+
` --output-path ${outputPath}` +
|
|
4388
|
+
` --title 'Cyberia Server Metrics'` +
|
|
4389
|
+
` --favicon /favicon.ico` +
|
|
4390
|
+
` --description 'Operational dashboard for the cyberia-server MMO runtime.'` +
|
|
4391
|
+
` --lang en` +
|
|
4392
|
+
` --env ${options.dev ? 'development' : 'production'}`,
|
|
4393
|
+
);
|
|
4394
|
+
});
|
|
4395
|
+
|
|
4396
|
+
// Passthrough check: if the user invoked a command that is OWNED by the
|
|
4397
|
+
// underpost CLI (not the cyberia overlay), throw the sentinel error so
|
|
4398
|
+
// the catch block below can re-run argv through underpost. The match is
|
|
4399
|
+
// strict on process.argv[2] (the first positional after `node bin/cyberia`)
|
|
4400
|
+
// so we only passthrough when the top-level command name actually
|
|
4401
|
+
// belongs to underpost.
|
|
4402
|
+
if (
|
|
4403
|
+
process.argv[2] &&
|
|
4404
|
+
underpostProgram.commands.find((c) => c._name === process.argv[2]) &&
|
|
4405
|
+
!program.commands.find((c) => c._name === process.argv[2])
|
|
4406
|
+
) {
|
|
3334
4407
|
throw new Error('Trigger underpost passthrough');
|
|
4408
|
+
}
|
|
3335
4409
|
|
|
3336
4410
|
program.parse();
|
|
3337
4411
|
} catch (error) {
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
4412
|
+
// ONLY reroute on the explicit passthrough sentinel. Any other thrown
|
|
4413
|
+
// error (subprocess non-zero from shellExec's fail-fast default, CLI
|
|
4414
|
+
// parse errors, missing modules) must propagate as a non-zero process
|
|
4415
|
+
// exit so GitHub Actions / CI parents observe the failure. Without this
|
|
4416
|
+
// guard, a genuine build failure was being silently rerouted into the
|
|
4417
|
+
// underpost CLI and then masked behind a misleading "unknown command"
|
|
4418
|
+
// line.
|
|
4419
|
+
if (error && error.message === 'Trigger underpost passthrough') {
|
|
4420
|
+
process.argv = process.argv.filter((c) => c !== 'underpost');
|
|
4421
|
+
logger.warn('Rerouting to underpost cli...');
|
|
4422
|
+
try {
|
|
4423
|
+
underpostProgram.parse();
|
|
4424
|
+
} catch (err) {
|
|
4425
|
+
logger.error(err);
|
|
4426
|
+
process.exit(1);
|
|
4427
|
+
}
|
|
4428
|
+
} else {
|
|
3344
4429
|
logger.error(error);
|
|
4430
|
+
process.exit(1);
|
|
3345
4431
|
}
|
|
3346
4432
|
}
|