@underpostnet/cyberia 3.3.0 → 3.3.76
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/.dockerignore +1 -0
- package/.env.example +17 -5
- package/.github/workflows/coverall.cyberia.ci.yml +39 -14
- package/.github/workflows/cyberia-client.cd.yml +5 -0
- package/.github/workflows/cyberia-server.cd.yml +5 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
- package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
- package/.github/workflows/engine-cyberia.cd.yml +3 -0
- package/.github/workflows/engine-cyberia.ci.yml +4 -5
- package/.github/workflows/ghpkg.ci.yml +35 -20
- package/.github/workflows/hardhat.ci.yml +12 -11
- package/.github/workflows/npmpkg.ci.yml +17 -15
- package/.github/workflows/publish.cyberia.ci.yml +44 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
- package/.github/workflows/release.cd.yml +1 -0
- package/.prettierignore +1 -0
- package/AGENTS.md +96 -0
- package/CHANGELOG.md +653 -2513
- package/CLI-HELP.md +323 -157
- package/Dockerfile +25 -9
- package/Dockerfile.dev +25 -9
- package/Dockerfile.test +5 -5
- package/README.md +48 -28
- package/bin/build.js +86 -41
- package/bin/build.template.js +8 -3
- package/bin/cyberia.js +1494 -1403
- package/bin/deploy.js +18 -104
- package/bin/index.js +1494 -1403
- package/compose.env +17 -5
- package/conf.js +98 -7
- package/deploy/cyberia-client/deploy.sh +46 -0
- package/deploy/cyberia-client/package.sh +17 -0
- package/deploy/cyberia-client/state.sh +15 -0
- package/deploy/cyberia-server/deploy.sh +46 -0
- package/deploy/cyberia-server/package.sh +17 -0
- package/deploy/cyberia-server/state.sh +15 -0
- package/deploy/dd-cyberia/deploy.sh +9 -29
- package/deploy/dd-cyberia/init.sh +38 -45
- package/deploy/dd-cyberia/package.sh +16 -0
- package/deploy/dd-cyberia/state.sh +14 -0
- package/deploy/dd-cyberia/sync-deploy.sh +133 -147
- package/deploy/lib/config.sh +29 -0
- package/deploy/lib/github-actions-logging.sh +610 -0
- package/deploy/lib/host.sh +194 -0
- package/deploy/lib/state.sh +65 -0
- package/deploy/pwa-microservices-template/deploy.sh +17 -42
- package/deploy/release/deploy.sh +9 -41
- package/deployment.yaml +7 -2
- package/docker-compose.yml +23 -17
- package/examples/static-page/README.md +19 -19
- package/examples/static-page/ssr-components/CustomPage.js +2 -2
- package/hardhat/hardhat.config.js +1 -6
- package/hardhat/package-lock.json +101 -40
- package/hardhat/package.json +2 -2
- package/manifests/alertmanager/deployment.yaml +81 -0
- package/manifests/blackbox-exporter/deployment.yaml +75 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +32 -7
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +32 -7
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +46 -27
- package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
- package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
- package/manifests/deployment/mongo-express/service.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
- package/manifests/grafana/deployment.yaml +26 -7
- package/manifests/grafana/kustomization.yaml +1 -0
- package/manifests/grafana/service.yaml +6 -1
- package/manifests/mariadb/pv.yaml +1 -0
- package/manifests/mongodb/statefulset.yaml +9 -2
- package/manifests/mysql/pv-pvc.yaml +14 -1
- package/manifests/postgresql/pv.yaml +12 -1
- package/manifests/postgresql/pvc.yaml +1 -0
- package/manifests/prometheus/deployment.yaml +18 -22
- package/package.json +87 -12
- package/scripts/event-monitor.sh +56 -0
- package/scripts/k3s-node-setup.sh +27 -14
- package/scripts/kubeadm-node-setup.sh +31 -11
- package/scripts/maas-setup.sh +8 -8
- package/scripts/selinux-normalize.sh +907 -0
- package/scripts/shutdown-machine.sh +843 -0
- package/scripts/test-monitor.sh +5 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +15 -10
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +7 -2
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +90 -186
- package/src/api/core/core.controller.js +1 -1
- package/src/api/core/core.router.js +2 -2
- package/src/api/core/core.service.js +2 -2
- package/src/api/crypto/crypto.controller.js +1 -1
- package/src/api/crypto/crypto.service.js +1 -1
- package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
- package/src/api/cyberia-action/cyberia-action.router.js +3 -3
- package/src/api/cyberia-action/cyberia-action.service.js +5 -6
- package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
- package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
- package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
- package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +2 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +13 -35
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +17 -34
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +2 -2
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
- package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.service.js +3 -3
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +468 -5
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +502 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +29 -40
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
- package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +35 -142
- package/src/api/cyberia-instance/cyberia-instance.controller.js +2 -5
- package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
- package/src/api/cyberia-instance/cyberia-instance.router.js +5 -18
- package/src/api/cyberia-instance/cyberia-instance.service.js +5 -45
- package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +76 -3
- package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
- package/src/api/cyberia-map/cyberia-map.router.js +1 -1
- package/src/api/cyberia-map/cyberia-map.service.js +8 -4
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
- package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.router.js +4 -4
- package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
- package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -214
- package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
- package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
- package/src/api/default/default.controller.js +1 -1
- package/src/api/default/default.router.js +1 -1
- package/src/api/default/default.service.js +2 -2
- package/src/api/document/document.controller.js +1 -1
- package/src/api/document/document.router.js +1 -1
- package/src/api/document/document.service.js +2 -2
- package/src/api/file/file.controller.js +1 -1
- package/src/api/file/file.ref.js +153 -0
- package/src/api/file/file.ref.json +11 -2
- package/src/api/file/file.router.js +1 -1
- package/src/api/file/file.service.js +2 -2
- package/src/api/instance/instance.controller.js +1 -1
- package/src/api/instance/instance.router.js +1 -1
- package/src/api/instance/instance.service.js +2 -2
- package/src/api/ipfs/ipfs.controller.js +1 -1
- package/src/api/ipfs/ipfs.router.js +2 -2
- package/src/api/ipfs/ipfs.service.js +2 -2
- package/src/api/object-layer/object-layer.controller.js +1 -1
- package/src/api/object-layer/object-layer.model.js +47 -12
- package/src/api/object-layer/object-layer.router.js +12 -5
- package/src/api/object-layer/object-layer.service.js +116 -123
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
- package/src/api/test/test.controller.js +1 -1
- package/src/api/test/test.service.js +1 -1
- package/src/api/user/guest.service.js +3 -3
- package/src/api/user/user.controller.js +1 -1
- package/src/api/user/user.router.js +2 -2
- package/src/api/user/user.service.js +4 -4
- package/src/api.js +5 -9
- package/src/cli/app.js +402 -0
- package/src/cli/baremetal.js +30 -35
- package/src/cli/client.js +306 -0
- package/src/cli/cloud-init.js +3 -3
- package/src/cli/cluster.js +191 -82
- package/src/cli/db.js +120 -68
- package/src/cli/deploy.js +157 -75
- package/src/cli/docker-compose.js +5 -5
- package/src/cli/domains.js +184 -0
- package/src/cli/dotenv-store.js +143 -0
- package/src/cli/event.js +2281 -0
- package/src/cli/fs-selection.js +115 -0
- package/src/cli/fs.js +159 -343
- package/src/cli/host.js +672 -0
- package/src/cli/image.js +4 -6
- package/src/cli/index.js +343 -151
- package/src/cli/ipfs.js +34 -30
- package/src/cli/kickstart.js +1 -1
- package/src/cli/kubectl.js +218 -41
- package/src/cli/lxd.js +3 -3
- package/src/cli/monitor.js +1359 -26
- package/src/cli/package.js +90 -0
- package/src/cli/release.js +16 -12
- package/src/cli/repository.js +586 -363
- package/src/cli/run.js +398 -843
- package/src/cli/secrets.js +2158 -964
- package/src/cli/ssh.js +478 -279
- package/src/cli/state.js +394 -0
- package/src/cli/static.js +2 -2
- package/src/cli/system.js +1 -1
- package/src/cli/test.js +203 -125
- package/src/cli/vultr.js +61 -21
- package/src/cli/wireguard.js +1112 -306
- package/src/client/components/core/CalendarCore.js +53 -96
- package/src/client/components/core/Docs.js +327 -303
- package/src/client/components/core/FileExplorer.js +0 -21
- package/src/client/components/core/Modal.js +56 -1
- package/src/client/components/core/PanelForm.js +4 -4
- package/src/client/components/core/Repository.js +56 -0
- package/src/client/components/core/Translate.js +7 -0
- package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
- package/src/client/components/core/Wallet.js +0 -11
- package/src/client/components/core/Worker.js +7 -1
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
- package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
- package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
- package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
- package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
- package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +2 -35
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -1
- package/src/client/components/default/SettingsDefault.js +2 -2
- package/src/client/components/itemledger/SettingsItemledger.js +2 -2
- package/src/client/components/underpost/AppShellUnderpost.js +3 -2
- package/src/client/components/underpost/SettingsUnderpost.js +2 -2
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +313 -30
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
- package/src/client/public/cyberia-docs/CYBERIA.md +48 -28
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
- package/src/client/public/default/android-chrome-144x144.png +0 -0
- package/src/client/public/default/android-chrome-192x192.png +0 -0
- package/src/client/public/default/android-chrome-256x256.png +0 -0
- package/src/client/public/default/android-chrome-36x36.png +0 -0
- package/src/client/public/default/android-chrome-384x384.png +0 -0
- package/src/client/public/default/android-chrome-48x48.png +0 -0
- package/src/client/public/default/android-chrome-512x512.png +0 -0
- package/src/client/public/default/android-chrome-72x72.png +0 -0
- package/src/client/public/default/android-chrome-96x96.png +0 -0
- package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/default/assets/background/white.jpg +0 -0
- package/src/client/public/default/browserconfig.xml +1 -1
- package/src/client/public/default/favicon-16x16.png +0 -0
- package/src/client/public/default/favicon-32x32.png +0 -0
- package/src/client/public/default/favicon-48x48.png +0 -0
- package/src/client/public/default/favicon.ico +0 -0
- package/src/client/public/default/manifest.webmanifest +5 -5
- package/src/client/public/default/mstile-144x144.png +0 -0
- package/src/client/public/default/mstile-150x150.png +0 -0
- package/src/client/public/default/mstile-310x150.png +0 -0
- package/src/client/public/default/mstile-310x310.png +0 -0
- package/src/client/public/default/mstile-70x70.png +0 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +1 -1
- package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
- package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
- package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -10
- package/src/client/services/object-layer/object-layer.service.js +3 -1
- package/src/client/ssr/RootDocument.js +1 -1
- package/src/client/ssr/body/404.js +1 -1
- package/src/client/ssr/body/500.js +1 -1
- package/src/client/ssr/body/CacheControl.js +1 -1
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
- package/src/client/ssr/head/CryptokoynScripts.js +1 -1
- package/src/client/ssr/head/Css.js +1 -1
- package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
- package/src/client/ssr/head/DefaultScripts.js +1 -1
- package/src/client/ssr/head/ItemledgerScripts.js +1 -1
- package/src/client/ssr/head/Microdata.js +1 -1
- package/src/client/ssr/head/Production.js +1 -1
- package/src/client/ssr/head/Pwa.js +1 -1
- package/src/client/ssr/head/PwaItemledger.js +1 -1
- package/src/client/ssr/head/Seo.js +1 -1
- package/src/client/ssr/head/UnderpostScripts.js +1 -1
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
- package/src/client/ssr/views/Cyberia404.js +7 -5
- package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
- package/src/client/ssr/views/Maintenance.js +1 -1
- package/src/client/ssr/views/NoNetworkConnection.js +1 -1
- package/src/client/ssr/views/Test.js +1 -1
- package/src/client/sw/core.sw.js +20 -14
- package/src/client-builder/client-build-docs.js +92 -77
- package/src/client-builder/client-build-live.js +2 -2
- package/src/client-builder/client-build.js +105 -74
- package/src/client-builder/client-bundle.js +347 -0
- package/src/client-builder/client-dev-server.js +3 -3
- package/src/client-builder/client-icons.js +2 -2
- package/src/client-builder/ssr.js +3 -3
- package/src/client.build.js +3 -6
- package/src/client.dev.js +3 -6
- package/src/db/DataBaseProvider.js +186 -3
- package/src/db/mariadb/MariaDB.js +3 -2
- package/src/db/mongo/MongoBootstrap.js +273 -101
- package/src/db/mongo/MongoExpress.js +187 -0
- package/src/db/mongo/MongooseDB.js +7 -8
- package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
- package/src/grpc/cyberia/grpc-server.js +6 -8
- package/src/index.js +79 -23
- package/src/mailer/EmailRender.js +2 -2
- package/src/mailer/MailerInterceptor.js +87 -0
- package/src/mailer/MailerProvider.js +50 -6
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +144 -147
- package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
- package/src/projects/cyberia/besu-genesis-generator.js +8 -17
- package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
- package/src/projects/cyberia/catalog-cyberia.js +54 -10
- package/src/projects/cyberia/gemini-client.js +4 -4
- package/src/projects/cyberia/generate-saga.js +5 -12
- package/src/projects/cyberia/hot-reload-trigger.js +18 -29
- package/src/projects/cyberia/instance-backup.js +210 -0
- package/src/projects/cyberia/instance-data.js +178 -258
- package/src/projects/cyberia/ipfs-client.js +4 -4
- package/src/projects/cyberia/map-preview-generator.js +33 -69
- package/src/projects/cyberia/object-layer.js +153 -320
- package/src/projects/cyberia/seed-audio.js +139 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
- package/src/projects/cyberia/semantic-layer-generator.js +3 -19
- package/src/projects/cyberia/stat-balance.js +81 -0
- package/src/projects/cyberia/stat-commands.js +23 -0
- package/src/projects/cyberia/stat-contract-generator.js +101 -0
- package/src/projects/underpost/catalog-underpost.js +9 -1
- package/src/proxy.js +4 -7
- package/src/runtime/cyberia-client/Dockerfile +17 -8
- package/src/runtime/cyberia-client/Dockerfile.dev +13 -4
- package/src/runtime/cyberia-server/Dockerfile +18 -6
- package/src/runtime/cyberia-server/Dockerfile.dev +16 -3
- package/src/runtime/engine-cyberia/Dockerfile +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.test +5 -5
- package/src/runtime/engine-cyberia/compose.env +17 -5
- package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
- package/src/runtime/express/Express.js +32 -27
- package/src/runtime/lampp/Lampp.js +22 -70
- package/src/runtime/nginx/Nginx.js +1 -1
- package/src/runtime/wp/Wp.js +16 -48
- package/src/server/{catalog.js → build/catalog.js} +15 -5
- package/src/server/build/coverage.js +188 -0
- package/src/server/build/execution.js +402 -0
- package/src/server/build/package.js +508 -0
- package/src/server/build/testing.js +578 -0
- package/src/server/{dns.js → network/dns.js} +144 -11
- package/src/server/{forward-proxy.js → network/forward-proxy.js} +10 -18
- package/src/server/{middlewares.js → network/middlewares.js} +11 -5
- package/src/server/network/node-capability.js +98 -0
- package/src/server/{peer.js → network/peer.js} +3 -3
- package/src/server/{proxy.js → network/proxy.js} +7 -13
- package/src/server/network/router.js +356 -0
- package/src/server/{tls.js → network/tls.js} +2 -2
- package/src/server/{underpost-compression.js → network/underpost-compression.js} +1 -1
- package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +7 -7
- package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +2 -2
- package/src/server/{cri.js → ops/cri.js} +2 -2
- package/src/server/{cron.js → ops/cron.js} +349 -74
- package/src/server/ops/event-notification.js +284 -0
- package/src/server/{logger.js → ops/logger.js} +143 -23
- package/src/server/ops/monitoring.js +1724 -0
- package/src/server/{systemd.js → ops/systemd.js} +55 -1
- package/src/server/{conf.js → runtime/conf.js} +516 -423
- package/src/server/runtime/config-scope.js +215 -0
- package/src/server/runtime/environment.js +149 -0
- package/src/server/{process.js → runtime/process.js} +153 -12
- package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
- package/src/server/{runtime.js → runtime/runtime.js} +12 -8
- package/src/server/{start.js → runtime/start.js} +128 -39
- package/src/server/{auth.js → security/auth.js} +7 -8
- package/src/server/security/container-storage.js +382 -0
- package/src/server/{crypto.js → security/crypto.js} +1 -1
- package/src/server/{selinux.js → security/selinux.js} +1 -1
- package/src/server/{backup.js → storage/backup.js} +17 -8
- package/src/server/{data-query.js → storage/data-query.js} +17 -1
- package/src/server/storage/downloader.js +121 -0
- package/src/server/storage/repository.js +68 -0
- package/src/server/storage/zip.js +153 -0
- package/src/server.js +4 -7
- package/src/ws/IoInterface.js +1 -1
- package/src/ws/IoServer.js +1 -1
- package/src/ws/core/core.ws.connection.js +1 -1
- package/src/ws/core/core.ws.emit.js +1 -1
- package/src/ws/core/core.ws.server.js +1 -1
- package/src/ws/default/default.ws.connection.js +1 -1
- package/src/ws/default/default.ws.emit.js +1 -1
- package/src/ws/default/default.ws.server.js +1 -1
- package/test/e2e/event-e2e-public-ingress-down.js +45 -0
- package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
- package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
- package/test/{api.test.js → integration/app/api.test.js} +33 -14
- package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +64 -0
- package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
- package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +17 -23
- package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
- package/test/integration/app/cyberia/cyberia-load.test.js +466 -0
- package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
- package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +338 -0
- package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
- package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
- package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
- package/test/integration/app/cyberia/seed-audio.test.js +275 -0
- package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
- package/test/integration/infra/1-security/config-scope.test.js +305 -0
- package/test/integration/infra/1-security/container-storage.test.js +356 -0
- package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
- package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
- package/test/{selinux.test.js → integration/infra/1-security/selinux.test.js} +2 -4
- package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
- package/test/integration/infra/1-security/systemd-service.test.js +70 -0
- package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
- package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
- package/test/integration/infra/2-network/node-capability.test.js +182 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +1187 -0
- package/test/{wireguard-edge.test.js → integration/infra/2-network/wireguard-edge.test.js} +649 -55
- package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
- package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
- package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
- package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
- package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
- package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
- package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
- package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
- package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +6 -6
- package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +4 -4
- package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
- package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
- package/test/integration/infra/5-observability/event-notification.test.js +197 -0
- package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
- package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
- package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
- package/test/support/shell-harness.js +73 -0
- package/test/unit/build-template.test.js +97 -0
- package/test/unit/build-workflow-offline.test.js +132 -0
- package/test/unit/catalog.test.js +71 -0
- package/test/unit/client-build-docs.test.js +361 -0
- package/test/unit/client-bundle.test.js +313 -0
- package/test/unit/conf-loading.test.js +303 -0
- package/test/unit/conf-resolution.test.js +996 -0
- package/test/unit/coverage-artifact.test.js +221 -0
- package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
- package/test/unit/cyberia/instance-backup.test.js +183 -0
- package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
- package/test/unit/cyberia/publish-workflow.test.js +22 -0
- package/test/unit/cyberia/stat-balance.test.js +69 -0
- package/test/unit/deploy-env-resolution.test.js +38 -0
- package/test/unit/deploy-log-table.test.js +518 -0
- package/test/unit/downloader-integrity.test.js +98 -0
- package/test/unit/execution-profiles.test.js +219 -0
- package/test/unit/file-reference-registry.test.js +141 -0
- package/test/unit/fleet-sync-source.test.js +149 -0
- package/test/unit/fs-storage-paths.test.js +596 -0
- package/test/unit/logger-redaction.test.js +141 -0
- package/test/unit/package.test.js +457 -0
- package/test/unit/prepare-host.test.js +382 -0
- package/test/unit/process-environment.test.js +18 -0
- package/test/unit/propagation-message.test.js +78 -0
- package/test/unit/redeploy-plan.test.js +24 -0
- package/test/unit/release-bump.test.js +474 -0
- package/test/unit/ssh-output-redaction.test.js +28 -0
- package/test/unit/start-options.test.js +46 -0
- package/test/unit/test-tiers.test.js +220 -0
- package/test/unit/zip-archive.test.js +142 -0
- package/vitest.config.js +66 -0
- package/.nycrc +0 -9
- package/deploy/lib/logging.sh +0 -96
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
- package/manifests/mongodb/pv-pvc.yaml +0 -59
- package/scripts/coverall-test-cyberia.sh +0 -24
- package/scripts/coverall-test.sh +0 -24
- package/scripts/link-local-underpost-cli.sh +0 -6
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
- package/src/api/user/user.build.js +0 -16
- package/src/cli/env.js +0 -177
- package/src/client/components/core/Blockchain.js +0 -41
- package/src/client/components/core/Blog.js +0 -9
- package/src/client/components/core/KeyboardAvoidance.js +0 -145
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
- package/src/client/public/default/assets/background/dark.jpg +0 -0
- package/src/client/public/default/assets/background/white0-min.jpg +0 -0
- package/src/client/public/default/assets/background/white0.jpg +0 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +0 -148
- package/src/server/downloader.js +0 -73
- package/src/server/environment.js +0 -98
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module cyberia-load.test
|
|
5
|
+
* @description Controlled WebSocket load against a running `cyberia-server`, so a real
|
|
6
|
+
* `cyberia-client` connected to the same instance can be observed for prediction and
|
|
7
|
+
* reconciliation while the authoritative runtime is busy.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { expect } from 'chai';
|
|
11
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
12
|
+
|
|
13
|
+
// Opt-in, with no default endpoint: every simulated client shares one address,
|
|
14
|
+
// so a fleet this size only fits an instance started for load — one running with
|
|
15
|
+
// CYBERIA_DISABLE_CONNECTION_LIMITS=1, or with its per-IP admission limits raised
|
|
16
|
+
// past CYBERIA_LOAD_CONNECTIONS. Pointed at an untuned instance the run is capped
|
|
17
|
+
// by the server's per-IP admission limit and reports a refusal, so the target is
|
|
18
|
+
// named deliberately rather than defaulted to.
|
|
19
|
+
// `underpost cyberia test` sets this, along with the fleet size and the duration.
|
|
20
|
+
const WS_URL = process.env.CYBERIA_LOAD_WS_URL ?? '';
|
|
21
|
+
const CONNECTIONS = Number(process.env.CYBERIA_LOAD_CONNECTIONS ?? 5);
|
|
22
|
+
const TAP_FREQUENCY = Number(process.env.CYBERIA_LOAD_TAP_FREQUENCY ?? 5);
|
|
23
|
+
const DURATION_MS = Number(process.env.CYBERIA_LOAD_DURATION_MS ?? 3000);
|
|
24
|
+
|
|
25
|
+
const CONNECT_TIMEOUT_MS = 30000;
|
|
26
|
+
// Time to keep reading after the last tap, so the final acks arrive before the
|
|
27
|
+
// run is judged. Two snapshot periods at the slowest sane rate.
|
|
28
|
+
const SETTLE_MS = 1500;
|
|
29
|
+
const CLOSE_TIMEOUT_MS = 10000;
|
|
30
|
+
// Cells per tap. Far enough that the server re-plans a path, short enough that
|
|
31
|
+
// the walk stays inside the AOI the real client is watching.
|
|
32
|
+
const TAP_STEP_CELLS = 4;
|
|
33
|
+
// Fraction of the nominal tap count a client must reach for the run to count as
|
|
34
|
+
// sustained load rather than a stalled connection.
|
|
35
|
+
const TAP_YIELD_FLOOR = 0.8;
|
|
36
|
+
// Taps that may still be in flight when the run ends. The ack in a snapshot is
|
|
37
|
+
// the highest sequence the server queued at the moment that snapshot was built.
|
|
38
|
+
const ACK_LAG_TOLERANCE = 3;
|
|
39
|
+
|
|
40
|
+
const TAP_PERIOD_MS = 1000 / TAP_FREQUENCY;
|
|
41
|
+
|
|
42
|
+
// Joins are spread over a ramp instead of arriving together. The instance has a
|
|
43
|
+
// single fixed spawn cell, so a fleet that lands together stacks on one cell —
|
|
44
|
+
// and because every tap fires a skill, the stack kills itself. A dead player
|
|
45
|
+
// cannot walk out and respawns on the same cell, so the pile never clears.
|
|
46
|
+
// The gap gives each joiner time to walk clear before the next one lands.
|
|
47
|
+
const SPAWN_DRAIN_MS = 600;
|
|
48
|
+
const RAMP_WINDOW_MS = Math.min(CONNECTIONS * SPAWN_DRAIN_MS, Math.max(DURATION_MS / 2, 500), 120000);
|
|
49
|
+
const JOIN_GAP_MS = CONNECTIONS > 1 ? RAMP_WINDOW_MS / (CONNECTIONS - 1) : 0;
|
|
50
|
+
|
|
51
|
+
// A fresh player waits before its first tap: it has just spawned and is still
|
|
52
|
+
// loading. The random part spreads first taps across one tap period so the
|
|
53
|
+
// fleet never taps in lockstep.
|
|
54
|
+
const TAP_WARMUP_MS = Math.min(1000, DURATION_MS / 10);
|
|
55
|
+
|
|
56
|
+
// How near its assigned region a client must be before it stops walking toward
|
|
57
|
+
// it and wanders inside it instead.
|
|
58
|
+
const ARRIVE_RADIUS_CELLS = 6;
|
|
59
|
+
// Keep dispersal targets off the map edge, where paths often fail.
|
|
60
|
+
const DISPERSAL_MARGIN_CELLS = 3;
|
|
61
|
+
// No client may settle within this distance of the spawn cell. Anything that
|
|
62
|
+
// lingers there sits in the crossfire of every joining player.
|
|
63
|
+
const SPAWN_CLEARANCE_CELLS = 14;
|
|
64
|
+
|
|
65
|
+
// Unit vectors for the supported directions, in Direction enum order. NONE is
|
|
66
|
+
// excluded: a tap carries movement intent.
|
|
67
|
+
const DIRECTIONS = [
|
|
68
|
+
[0, -1], // UP
|
|
69
|
+
[1, -1], // UP_RIGHT
|
|
70
|
+
[1, 0], // RIGHT
|
|
71
|
+
[1, 1], // DOWN_RIGHT
|
|
72
|
+
[0, 1], // DOWN
|
|
73
|
+
[-1, 1], // DOWN_LEFT
|
|
74
|
+
[-1, 0], // LEFT
|
|
75
|
+
[-1, -1], // UP_LEFT
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const pack = (type, payload) => Buffer.from(JSON.stringify({ type, payload }));
|
|
79
|
+
|
|
80
|
+
const unpack = (data) => {
|
|
81
|
+
try {
|
|
82
|
+
return JSON.parse(typeof data === 'string' ? data : Buffer.from(data).toString('utf8'));
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const clamp = (value, max) => (value < 0 ? 0 : value > max ? max : value);
|
|
89
|
+
|
|
90
|
+
const randomDirection = () => DIRECTIONS[Math.floor(Math.random() * DIRECTIONS.length)];
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Picks the supported direction closest to a vector, the same mapping the
|
|
94
|
+
* server uses. The enum starts at UP, so the angle index shifts two quadrants.
|
|
95
|
+
*/
|
|
96
|
+
function directionToward(dx, dy) {
|
|
97
|
+
const index = (((Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) + 2) % 8) + 8) % 8;
|
|
98
|
+
return DIRECTIONS[index];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Gives every client its own destination on a lattice over the map, so the
|
|
103
|
+
* fleet fans out instead of piling on the spawn cell. Players spawn top-left,
|
|
104
|
+
* so the walk runs right, down, and bottom-right — which is what spreads the
|
|
105
|
+
* taps, and the AOI load with them, across the whole map.
|
|
106
|
+
*
|
|
107
|
+
* A lattice point that lands on the spawn is pushed out onto a quarter-turn fan
|
|
108
|
+
* from right to down. Leaving one client parked on the spawn would keep it in
|
|
109
|
+
* the crossfire of every later joiner, dying and respawning for the whole run.
|
|
110
|
+
*/
|
|
111
|
+
function dispersalTarget(index, total, gridW, gridH, spawn) {
|
|
112
|
+
const columns = Math.max(1, Math.ceil(Math.sqrt(total)));
|
|
113
|
+
const rows = Math.max(1, Math.ceil(total / columns));
|
|
114
|
+
const column = index % columns;
|
|
115
|
+
const row = Math.floor(index / columns) % rows;
|
|
116
|
+
|
|
117
|
+
const spread = (slot, slots, extent) => {
|
|
118
|
+
const usable = Math.max(1, extent - 1 - 2 * DISPERSAL_MARGIN_CELLS);
|
|
119
|
+
if (slots <= 1) return DISPERSAL_MARGIN_CELLS + Math.round(usable / 2);
|
|
120
|
+
return DISPERSAL_MARGIN_CELLS + Math.round((usable * slot) / (slots - 1));
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
let x = spread(column, columns, gridW);
|
|
124
|
+
let y = spread(row, rows, gridH);
|
|
125
|
+
|
|
126
|
+
if (Math.hypot(x - spawn.x, y - spawn.y) < SPAWN_CLEARANCE_CELLS) {
|
|
127
|
+
const angle = (index / Math.max(1, total)) * (Math.PI / 2); // right → down-right → down
|
|
128
|
+
x = spawn.x + Math.cos(angle) * SPAWN_CLEARANCE_CELLS;
|
|
129
|
+
y = spawn.y + Math.sin(angle) * SPAWN_CLEARANCE_CELLS;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
x: clamp(Math.round(x), gridW - 1),
|
|
134
|
+
y: clamp(Math.round(y), gridH - 1),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Probes the endpoint with a plain GET before the run. A WebSocket error event
|
|
140
|
+
* carries no status code, so this is the only way to tell a server that refused
|
|
141
|
+
* the connection from a server that is not there.
|
|
142
|
+
*
|
|
143
|
+
* A healthy /ws answers 400 to a non-upgrade request; the admission guard
|
|
144
|
+
* answers 503.
|
|
145
|
+
*/
|
|
146
|
+
async function preflight(url) {
|
|
147
|
+
const httpURL = url.replace(/^ws/, 'http');
|
|
148
|
+
try {
|
|
149
|
+
const res = await fetch(httpURL, { method: 'GET' });
|
|
150
|
+
if (res.status === 503) return { state: 'refused', detail: (await res.text()).trim() };
|
|
151
|
+
return { state: 'reachable', detail: `HTTP ${res.status}` };
|
|
152
|
+
} catch (err) {
|
|
153
|
+
return { state: 'unreachable', detail: err?.cause?.code ?? err?.message ?? 'connect failed' };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Opens one simulated client. `index` of `total` picks its region of the map.
|
|
159
|
+
*
|
|
160
|
+
* `ready` settles once the client has joined, or once the connection failed.
|
|
161
|
+
* `tapping` settles on its first tap. `closed` settles when the socket is fully
|
|
162
|
+
* closed. None of them ever hangs, so the caller can await the whole fleet.
|
|
163
|
+
*/
|
|
164
|
+
function startSimulatedClient(url, index, total) {
|
|
165
|
+
const stats = {
|
|
166
|
+
joined: false,
|
|
167
|
+
tapping: false,
|
|
168
|
+
taps: 0,
|
|
169
|
+
snapshots: 0,
|
|
170
|
+
sequence: 0, // highest sequence sent
|
|
171
|
+
lastAck: 0, // highest sequence the server reported back
|
|
172
|
+
serverTick: 0, // tick of the newest snapshot
|
|
173
|
+
tickRate: 60,
|
|
174
|
+
tickAt: 0, // wall clock when that snapshot arrived
|
|
175
|
+
gridW: 0,
|
|
176
|
+
gridH: 0,
|
|
177
|
+
posX: 0,
|
|
178
|
+
posY: 0,
|
|
179
|
+
selfSamples: 0, // snapshots carrying a self block
|
|
180
|
+
deadSamples: 0, // ... of which the player was a ghost awaiting respawn
|
|
181
|
+
closeCode: null,
|
|
182
|
+
failure: null,
|
|
183
|
+
handshakeFailed: false,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
let tapTimer = null;
|
|
187
|
+
let warmupTimer = null;
|
|
188
|
+
let spawn = null; // learned from the first snapshot
|
|
189
|
+
let target = null; // this client's region of the map
|
|
190
|
+
let settleReady;
|
|
191
|
+
let settleTapping;
|
|
192
|
+
let settleClosed;
|
|
193
|
+
const ready = new Promise((resolve) => (settleReady = resolve));
|
|
194
|
+
const tapping = new Promise((resolve) => (settleTapping = resolve));
|
|
195
|
+
const closed = new Promise((resolve) => (settleClosed = resolve));
|
|
196
|
+
const settleAll = () => {
|
|
197
|
+
settleReady();
|
|
198
|
+
settleTapping();
|
|
199
|
+
};
|
|
200
|
+
const connectTimer = setTimeout(() => {
|
|
201
|
+
stats.failure ??= 'init_data timeout';
|
|
202
|
+
settleAll();
|
|
203
|
+
}, CONNECT_TIMEOUT_MS);
|
|
204
|
+
|
|
205
|
+
const label = `client ${index + 1}/${total}`;
|
|
206
|
+
const ws = new WebSocket(url);
|
|
207
|
+
ws.binaryType = 'arraybuffer';
|
|
208
|
+
console.log(`[cyberia-load] ${label} dialing`);
|
|
209
|
+
|
|
210
|
+
const send = (type, payload) => {
|
|
211
|
+
if (ws.readyState === WebSocket.OPEN) ws.send(pack(type, payload));
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// Mirrors session_server_tick_estimate: extrapolate from the newest snapshot
|
|
215
|
+
// so the tick stamped on a tap is the client's best guess at the server tick.
|
|
216
|
+
const clientTick = () => {
|
|
217
|
+
if (stats.serverTick === 0) return 0;
|
|
218
|
+
const elapsedMs = Date.now() - stats.tickAt;
|
|
219
|
+
return stats.serverTick + Math.max(0, Math.floor((elapsedMs * stats.tickRate) / 1000));
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Walk toward this client's region until it arrives, then wander inside it.
|
|
223
|
+
// Heading for a spread-out region is what disperses the fleet; wandering once
|
|
224
|
+
// there keeps its own patch of the map busy for the rest of the run.
|
|
225
|
+
//
|
|
226
|
+
// Near the spawn it never wanders. Death returns a player to the spawn cell,
|
|
227
|
+
// so this is also what makes it walk back out after every respawn.
|
|
228
|
+
const tapDirection = () => {
|
|
229
|
+
if (!target) return randomDirection();
|
|
230
|
+
const dx = target.x - stats.posX;
|
|
231
|
+
const dy = target.y - stats.posY;
|
|
232
|
+
const onSpawn = spawn && Math.hypot(stats.posX - spawn.x, stats.posY - spawn.y) < SPAWN_CLEARANCE_CELLS;
|
|
233
|
+
if (!onSpawn && Math.hypot(dx, dy) <= ARRIVE_RADIUS_CELLS) return randomDirection();
|
|
234
|
+
return directionToward(dx, dy);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const tap = () => {
|
|
238
|
+
const [dx, dy] = tapDirection();
|
|
239
|
+
send('player_action', {
|
|
240
|
+
x: clamp(Math.round(stats.posX) + dx * TAP_STEP_CELLS, stats.gridW - 1),
|
|
241
|
+
y: clamp(Math.round(stats.posY) + dy * TAP_STEP_CELLS, stats.gridH - 1),
|
|
242
|
+
tick: clientTick(),
|
|
243
|
+
seq: ++stats.sequence,
|
|
244
|
+
});
|
|
245
|
+
stats.taps++;
|
|
246
|
+
stats.tapping = true;
|
|
247
|
+
settleTapping();
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const stopTapping = () => {
|
|
251
|
+
if (warmupTimer) clearTimeout(warmupTimer);
|
|
252
|
+
warmupTimer = null;
|
|
253
|
+
if (tapTimer) clearInterval(tapTimer);
|
|
254
|
+
tapTimer = null;
|
|
255
|
+
stats.tapping = false;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const onInitData = (payload) => {
|
|
259
|
+
console.log(`[cyberia-load] ${label} joined`);
|
|
260
|
+
stats.gridW = payload.gridW;
|
|
261
|
+
stats.gridH = payload.gridH;
|
|
262
|
+
if (payload.tickRate > 0) stats.tickRate = payload.tickRate;
|
|
263
|
+
// Every join spawns frozen under the server's "loading" protection; taps are
|
|
264
|
+
// dropped by phaseInput until the matching freeze_end releases it.
|
|
265
|
+
send('freeze_end', { reason: 'loading' });
|
|
266
|
+
stats.joined = true;
|
|
267
|
+
clearTimeout(connectTimer);
|
|
268
|
+
settleReady();
|
|
269
|
+
|
|
270
|
+
// Hold before the first tap, then start on a random phase of the tap period.
|
|
271
|
+
warmupTimer = setTimeout(
|
|
272
|
+
() => {
|
|
273
|
+
warmupTimer = null;
|
|
274
|
+
tap();
|
|
275
|
+
tapTimer = setInterval(tap, TAP_PERIOD_MS);
|
|
276
|
+
},
|
|
277
|
+
TAP_WARMUP_MS + Math.random() * TAP_PERIOD_MS,
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const onSnapshot = (payload) => {
|
|
282
|
+
stats.snapshots++;
|
|
283
|
+
if (payload.tick !== undefined) {
|
|
284
|
+
stats.serverTick = payload.tick;
|
|
285
|
+
stats.tickAt = Date.now();
|
|
286
|
+
}
|
|
287
|
+
if (payload.ack !== undefined && payload.ack > stats.lastAck) stats.lastAck = payload.ack;
|
|
288
|
+
if (payload.self) {
|
|
289
|
+
stats.posX = payload.self.posX;
|
|
290
|
+
stats.posY = payload.self.posY;
|
|
291
|
+
// The spawn cell is only knowable from the server. Learn it once, then
|
|
292
|
+
// pick the region this client walks to.
|
|
293
|
+
if (!spawn) {
|
|
294
|
+
spawn = { x: stats.posX, y: stats.posY };
|
|
295
|
+
target = dispersalTarget(index, total, stats.gridW, stats.gridH, spawn);
|
|
296
|
+
}
|
|
297
|
+
if (payload.self.respawnIn > 0) stats.deadSamples++;
|
|
298
|
+
stats.selfSamples++;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
ws.addEventListener('open', () => send('handshake', { name: 'cyberia-load', version: '1.0.0' }));
|
|
303
|
+
|
|
304
|
+
ws.addEventListener('message', (event) => {
|
|
305
|
+
const msg = unpack(event.data);
|
|
306
|
+
if (!msg) return;
|
|
307
|
+
if (msg.type === 'init_data') onInitData(msg.payload);
|
|
308
|
+
else if (msg.type === 'snapshot') onSnapshot(msg.payload);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
ws.addEventListener('error', () => {
|
|
312
|
+
// The event carries no status code. Whether this was an admission refusal
|
|
313
|
+
// or an unreachable server is settled by the preflight probe.
|
|
314
|
+
if (!stats.joined && stats.snapshots === 0) {
|
|
315
|
+
stats.handshakeFailed = true;
|
|
316
|
+
console.log(`[cyberia-load] ${label} REFUSED by the server`);
|
|
317
|
+
}
|
|
318
|
+
stats.failure ??= 'socket error';
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
ws.addEventListener('close', (event) => {
|
|
322
|
+
stopTapping();
|
|
323
|
+
stats.closeCode = event.code;
|
|
324
|
+
if (event.code !== 1000) stats.failure ??= `closed with code ${event.code}`;
|
|
325
|
+
clearTimeout(connectTimer);
|
|
326
|
+
settleAll();
|
|
327
|
+
settleClosed();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Ask for a normal closure and resolve once the socket is really closed.
|
|
331
|
+
const disconnect = () => {
|
|
332
|
+
stopTapping();
|
|
333
|
+
clearTimeout(connectTimer);
|
|
334
|
+
settleAll();
|
|
335
|
+
if (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN) ws.close(1000);
|
|
336
|
+
else settleClosed();
|
|
337
|
+
return closed;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const isClosed = () => ws.readyState === WebSocket.CLOSED;
|
|
341
|
+
|
|
342
|
+
return { stats, ready, tapping, closed, disconnect, stopTapping, isClosed };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const sum = (clients, field) => clients.reduce((total, client) => total + client.stats[field], 0);
|
|
346
|
+
|
|
347
|
+
const LOAD_TIMEOUT_MS =
|
|
348
|
+
RAMP_WINDOW_MS +
|
|
349
|
+
TAP_WARMUP_MS +
|
|
350
|
+
TAP_PERIOD_MS +
|
|
351
|
+
DURATION_MS +
|
|
352
|
+
CONNECT_TIMEOUT_MS +
|
|
353
|
+
SETTLE_MS +
|
|
354
|
+
CLOSE_TIMEOUT_MS +
|
|
355
|
+
60000;
|
|
356
|
+
|
|
357
|
+
// Without a named cyberia-server there is no subject to load, so the suite is
|
|
358
|
+
// skipped at collection rather than failing on connect.
|
|
359
|
+
describe.skipIf(!WS_URL)(
|
|
360
|
+
'Cyberia load — simulated tap clients against cyberia-server',
|
|
361
|
+
{ timeout: LOAD_TIMEOUT_MS },
|
|
362
|
+
() => {
|
|
363
|
+
const clients = [];
|
|
364
|
+
|
|
365
|
+
beforeAll(() => {
|
|
366
|
+
if (typeof WebSocket === 'undefined') throw new Error('global WebSocket requires Node >= 22');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Nothing this test opened may outlive it, on any exit path.
|
|
370
|
+
afterAll(async () => {
|
|
371
|
+
await Promise.all(clients.map((client) => Promise.race([client.disconnect(), sleep(CLOSE_TIMEOUT_MS)])));
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it(`sustains ${CONNECTIONS} clients tapping at ${TAP_FREQUENCY} Hz for ${DURATION_MS} ms`, async () => {
|
|
375
|
+
// Ramp the joins. Arriving together puts the whole fleet on the spawn cell
|
|
376
|
+
// at once, and since every tap fires a skill, the pile kills itself before
|
|
377
|
+
// the run reaches steady state.
|
|
378
|
+
for (let i = 0; i < CONNECTIONS; i++) {
|
|
379
|
+
clients.push(startSimulatedClient(WS_URL, i, CONNECTIONS));
|
|
380
|
+
if (JOIN_GAP_MS > 0 && i < CONNECTIONS - 1) await sleep(JOIN_GAP_MS);
|
|
381
|
+
// Stop as soon as the server refuses one. The concurrent per-address cap
|
|
382
|
+
// cannot clear while the run holds its connections, so every later join
|
|
383
|
+
// would be refused too — dialing them out just delays the diagnosis.
|
|
384
|
+
if (clients.some((client) => client.stats.handshakeFailed)) break;
|
|
385
|
+
}
|
|
386
|
+
await Promise.all(clients.map((client) => client.ready));
|
|
387
|
+
|
|
388
|
+
const joinedCount = clients.filter((client) => client.stats.joined).length;
|
|
389
|
+
const refusedCount = clients.filter((client) => client.stats.handshakeFailed).length;
|
|
390
|
+
if (refusedCount > 0) {
|
|
391
|
+
// A partial success is decisive on its own: the server accepted some and
|
|
392
|
+
// turned the rest away, which only an admission limit does. Probing again
|
|
393
|
+
// would be misleading, because the connect-rate bucket refills within a
|
|
394
|
+
// second and then answers as if nothing was ever refused.
|
|
395
|
+
let cause;
|
|
396
|
+
if (joinedCount > 0) {
|
|
397
|
+
cause =
|
|
398
|
+
`the server admitted ${joinedCount} and refused the next one, so an admission limit is ` +
|
|
399
|
+
`capping the run — ${joinedCount} is almost certainly the effective ` +
|
|
400
|
+
'CYBERIA_MAX_CONNECTIONS_PER_IP, because every simulated client shares one address. ' +
|
|
401
|
+
'Start cyberia-server with CYBERIA_DISABLE_CONNECTION_LIMITS=1 for a load run (note: ' +
|
|
402
|
+
'0 leaves the limits ON), or raise CYBERIA_MAX_CONNECTIONS_PER_IP, ' +
|
|
403
|
+
'CYBERIA_CONNECT_BURST_PER_IP and CYBERIA_CONNECT_RATE_PER_IP';
|
|
404
|
+
} else {
|
|
405
|
+
const probe = await preflight(WS_URL);
|
|
406
|
+
cause =
|
|
407
|
+
probe.state === 'unreachable'
|
|
408
|
+
? `the endpoint is unreachable (${probe.detail}) — is cyberia-server running?`
|
|
409
|
+
: probe.state === 'refused'
|
|
410
|
+
? `the endpoint refuses connections (503 "${probe.detail}"). Start cyberia-server ` +
|
|
411
|
+
'with CYBERIA_DISABLE_CONNECTION_LIMITS=1 for a load run (0 leaves them ON)'
|
|
412
|
+
: `the endpoint answers (${probe.detail}) but every upgrade failed`;
|
|
413
|
+
}
|
|
414
|
+
expect.fail(`only ${joinedCount} of ${CONNECTIONS} clients joined: ${cause}`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const joinFailed = clients.filter((client) => !client.stats.joined);
|
|
418
|
+
expect(joinFailed.length, `clients that never joined: ${joinFailed[0]?.stats.failure ?? ''}`).to.equal(0);
|
|
419
|
+
|
|
420
|
+
// Every client is through its warm-up and tapping. Only now does the run
|
|
421
|
+
// window open, so durationMs measures steady-state load and the expected
|
|
422
|
+
// tap count is not diluted by the ramp.
|
|
423
|
+
await Promise.all(clients.map((client) => client.tapping));
|
|
424
|
+
const tapsAtSteadyState = sum(clients, 'taps');
|
|
425
|
+
|
|
426
|
+
await sleep(DURATION_MS);
|
|
427
|
+
|
|
428
|
+
// Stop the load, then keep reading so the final acks land before judging.
|
|
429
|
+
for (const client of clients) client.stopTapping();
|
|
430
|
+
const droppedMidRun = clients.filter((client) => client.stats.failure);
|
|
431
|
+
await sleep(SETTLE_MS);
|
|
432
|
+
|
|
433
|
+
const nominalTaps = CONNECTIONS * TAP_FREQUENCY * (DURATION_MS / 1000);
|
|
434
|
+
const steadyStateTaps = sum(clients, 'taps') - tapsAtSteadyState;
|
|
435
|
+
expect(steadyStateTaps, 'player_action commands sent during the run window').to.be.at.least(
|
|
436
|
+
nominalTaps * TAP_YIELD_FLOOR,
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
const starved = clients.filter((client) => client.stats.snapshots === 0);
|
|
440
|
+
expect(starved.length, 'clients that received no AOI snapshot').to.equal(0);
|
|
441
|
+
|
|
442
|
+
// The ack is the highest input sequence the server took for this player —
|
|
443
|
+
// the same header field the real cyberia-client reconciles against. It must
|
|
444
|
+
// have caught up with what each client sent, not merely be non-zero.
|
|
445
|
+
const behind = clients.filter((client) => client.stats.lastAck < client.stats.sequence - ACK_LAG_TOLERANCE);
|
|
446
|
+
expect(
|
|
447
|
+
behind.length,
|
|
448
|
+
`clients whose input the server did not process: e.g. acked ${behind[0]?.stats.lastAck} of ` +
|
|
449
|
+
`${behind[0]?.stats.sequence} sent`,
|
|
450
|
+
).to.equal(0);
|
|
451
|
+
|
|
452
|
+
expect(droppedMidRun.length, `clients that failed mid-run: ${droppedMidRun[0]?.stats.failure ?? ''}`).to.equal(0);
|
|
453
|
+
|
|
454
|
+
// Every client closes normally, and the run does not finish until they have.
|
|
455
|
+
await Promise.all(clients.map((client) => Promise.race([client.disconnect(), sleep(CLOSE_TIMEOUT_MS)])));
|
|
456
|
+
|
|
457
|
+
const stillOpen = clients.filter((client) => !client.isClosed());
|
|
458
|
+
expect(stillOpen.length, 'connections still open after the run').to.equal(0);
|
|
459
|
+
|
|
460
|
+
const unclean = clients.filter((client) => client.stats.closeCode !== 1000);
|
|
461
|
+
expect(unclean.length, `clients that did not close cleanly: first code ${unclean[0]?.stats.closeCode}`).to.equal(
|
|
462
|
+
0,
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
},
|
|
466
|
+
);
|