cyberia 3.0.3 → 3.2.5

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.
Files changed (296) hide show
  1. package/{.env.production → .env.example} +20 -4
  2. package/.github/workflows/engine-cyberia.cd.yml +43 -10
  3. package/.github/workflows/engine-cyberia.ci.yml +48 -26
  4. package/.github/workflows/ghpkg.ci.yml +5 -5
  5. package/.github/workflows/gitlab.ci.yml +1 -1
  6. package/.github/workflows/hardhat.ci.yml +82 -0
  7. package/.github/workflows/npmpkg.ci.yml +60 -14
  8. package/.github/workflows/publish.ci.yml +26 -7
  9. package/.github/workflows/publish.cyberia.ci.yml +5 -5
  10. package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -7
  11. package/.github/workflows/pwa-microservices-template-test.ci.yml +4 -4
  12. package/.github/workflows/release.cd.yml +14 -8
  13. package/.vscode/extensions.json +9 -8
  14. package/.vscode/settings.json +3 -2
  15. package/CHANGELOG.md +643 -1
  16. package/CLI-HELP.md +132 -57
  17. package/Dockerfile +4 -2
  18. package/README.md +347 -22
  19. package/WHITE-PAPER.md +1540 -0
  20. package/bin/build.js +21 -12
  21. package/bin/cyberia.js +2640 -106
  22. package/bin/deploy.js +258 -372
  23. package/bin/file.js +5 -1
  24. package/bin/index.js +2640 -106
  25. package/bin/vs.js +3 -3
  26. package/conf.js +169 -105
  27. package/deployment.yaml +236 -20
  28. package/hardhat/.env.example +31 -0
  29. package/hardhat/README.md +531 -0
  30. package/hardhat/WHITE-PAPER.md +1540 -0
  31. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  32. package/hardhat/deployments/.gitkeep +0 -0
  33. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  34. package/hardhat/hardhat.config.js +136 -0
  35. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  36. package/hardhat/networks/besu-object-layer.network.json +138 -0
  37. package/hardhat/package-lock.json +4323 -0
  38. package/hardhat/package.json +36 -0
  39. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  40. package/hardhat/test/ObjectLayerToken.js +592 -0
  41. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  42. package/hardhat/types/ethers-contracts/common.ts +92 -0
  43. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  44. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  45. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  46. package/hardhat/types/ethers-contracts/index.ts +6 -0
  47. package/jsdoc.dd-cyberia.json +68 -0
  48. package/jsdoc.json +65 -49
  49. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
  50. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
  51. package/manifests/deployment/dd-cyberia-development/deployment.yaml +562 -0
  52. package/manifests/deployment/dd-cyberia-development/proxy.yaml +297 -0
  53. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +132 -0
  54. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  55. package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
  56. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  57. package/manifests/deployment/playwright/deployment.yaml +1 -1
  58. package/manifests/pv-pvc-dd.yaml +1 -1
  59. package/nodemon.json +1 -1
  60. package/package.json +60 -48
  61. package/proxy.yaml +118 -10
  62. package/pv-pvc.yaml +132 -0
  63. package/scripts/k3s-node-setup.sh +1 -1
  64. package/scripts/ports-ls.sh +2 -0
  65. package/scripts/rhel-grpc-setup.sh +56 -0
  66. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +47 -1
  67. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +17 -2
  68. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  69. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
  70. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  71. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  72. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  73. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  74. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  75. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  76. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  77. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  78. package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
  79. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  80. package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
  81. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  82. package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
  83. package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
  84. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  85. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
  86. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
  87. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  88. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
  89. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  90. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  91. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  92. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  93. package/src/api/document/document.service.js +1 -1
  94. package/src/api/file/file.controller.js +3 -1
  95. package/src/api/file/file.ref.json +18 -0
  96. package/src/api/file/file.service.js +28 -5
  97. package/src/api/ipfs/ipfs.controller.js +4 -25
  98. package/src/api/ipfs/ipfs.model.js +43 -34
  99. package/src/api/ipfs/ipfs.router.js +8 -13
  100. package/src/api/ipfs/ipfs.service.js +56 -104
  101. package/src/api/object-layer/README.md +347 -22
  102. package/src/api/object-layer/object-layer.controller.js +6 -2
  103. package/src/api/object-layer/object-layer.model.js +12 -8
  104. package/src/api/object-layer/object-layer.router.js +698 -42
  105. package/src/api/object-layer/object-layer.service.js +119 -37
  106. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +1 -2
  107. package/src/api/user/user.router.js +10 -5
  108. package/src/api/user/user.service.js +15 -14
  109. package/src/cli/baremetal.js +6 -10
  110. package/src/cli/cloud-init.js +0 -3
  111. package/src/cli/cluster.js +7 -7
  112. package/src/cli/db.js +723 -857
  113. package/src/cli/deploy.js +215 -105
  114. package/src/cli/env.js +34 -5
  115. package/src/cli/fs.js +5 -4
  116. package/src/cli/image.js +0 -3
  117. package/src/cli/index.js +83 -15
  118. package/src/cli/kubectl.js +211 -0
  119. package/src/cli/monitor.js +5 -6
  120. package/src/cli/release.js +284 -0
  121. package/src/cli/repository.js +708 -62
  122. package/src/cli/run.js +371 -151
  123. package/src/cli/secrets.js +73 -2
  124. package/src/cli/ssh.js +1 -1
  125. package/src/cli/test.js +3 -3
  126. package/src/client/Cryptokoyn.index.js +3 -4
  127. package/src/client/CyberiaPortal.index.js +3 -4
  128. package/src/client/Default.index.js +3 -4
  129. package/src/client/Itemledger.index.js +4 -963
  130. package/src/client/Underpost.index.js +3 -4
  131. package/src/client/components/core/AgGrid.js +20 -5
  132. package/src/client/components/core/Alert.js +2 -2
  133. package/src/client/components/core/AppStore.js +69 -0
  134. package/src/client/components/core/CalendarCore.js +2 -2
  135. package/src/client/components/core/Content.js +22 -3
  136. package/src/client/components/core/Docs.js +30 -6
  137. package/src/client/components/core/DropDown.js +137 -17
  138. package/src/client/components/core/FileExplorer.js +71 -4
  139. package/src/client/components/core/Input.js +1 -1
  140. package/src/client/components/core/Keyboard.js +2 -2
  141. package/src/client/components/core/LogIn.js +2 -2
  142. package/src/client/components/core/LogOut.js +2 -2
  143. package/src/client/components/core/Modal.js +20 -7
  144. package/src/client/components/core/Panel.js +0 -1
  145. package/src/client/components/core/PanelForm.js +19 -19
  146. package/src/client/components/core/RichText.js +1 -2
  147. package/src/client/components/core/SocketIo.js +82 -29
  148. package/src/client/components/core/SocketIoHandler.js +75 -0
  149. package/src/client/components/core/Stream.js +143 -95
  150. package/src/client/components/core/Webhook.js +40 -7
  151. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  152. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
  153. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
  154. package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
  155. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  156. package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
  157. package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
  158. package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
  159. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
  160. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  161. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +217 -30
  162. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +44 -2
  163. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -4
  164. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
  165. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +104 -9
  166. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +5 -0
  167. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  168. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  169. package/src/client/components/default/AppStoreDefault.js +5 -0
  170. package/src/client/components/default/LogInDefault.js +3 -3
  171. package/src/client/components/default/LogOutDefault.js +2 -2
  172. package/src/client/components/default/MenuDefault.js +5 -5
  173. package/src/client/components/default/SocketIoDefault.js +3 -51
  174. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  175. package/src/client/components/itemledger/LogInItemledger.js +3 -3
  176. package/src/client/components/itemledger/LogOutItemledger.js +2 -2
  177. package/src/client/components/itemledger/MenuItemledger.js +3 -3
  178. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  179. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  180. package/src/client/components/underpost/CssUnderpost.js +59 -0
  181. package/src/client/components/underpost/LogInUnderpost.js +6 -3
  182. package/src/client/components/underpost/LogOutUnderpost.js +4 -2
  183. package/src/client/components/underpost/MenuUnderpost.js +104 -18
  184. package/src/client/components/underpost/RoutesUnderpost.js +2 -0
  185. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  186. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  187. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  188. package/src/client/public/cryptokoyn/microdata.json +85 -0
  189. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  190. package/src/client/public/cryptokoyn/sitemap +3 -3
  191. package/src/client/public/default/sitemap +3 -3
  192. package/src/client/public/itemledger/browserconfig.xml +2 -2
  193. package/src/client/public/itemledger/manifest.webmanifest +4 -4
  194. package/src/client/public/itemledger/microdata.json +71 -0
  195. package/src/client/public/itemledger/sitemap +3 -3
  196. package/src/client/public/itemledger/yandex-browser-manifest.json +2 -2
  197. package/src/client/public/test/sitemap +3 -3
  198. package/src/client/services/core/core.service.js +20 -8
  199. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
  200. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  201. package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
  202. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  203. package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
  204. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
  205. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  206. package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
  207. package/src/client/services/instance/instance.management.js +2 -2
  208. package/src/client/services/ipfs/ipfs.service.js +3 -23
  209. package/src/client/services/object-layer/object-layer.management.js +3 -3
  210. package/src/client/services/object-layer/object-layer.service.js +21 -0
  211. package/src/client/services/user/user.management.js +2 -2
  212. package/src/client/ssr/body/404.js +15 -11
  213. package/src/client/ssr/body/500.js +15 -11
  214. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  215. package/src/client/ssr/head/PwaItemledger.js +60 -0
  216. package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
  217. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  218. package/src/client/ssr/pages/Test.js +11 -10
  219. package/src/client.build.js +0 -3
  220. package/src/client.dev.js +0 -3
  221. package/src/db/DataBaseProvider.js +17 -2
  222. package/src/db/mariadb/MariaDB.js +14 -9
  223. package/src/db/mongo/MongooseDB.js +17 -1
  224. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
  225. package/src/grpc/cyberia/README.md +326 -0
  226. package/src/grpc/cyberia/grpc-server.js +530 -0
  227. package/src/index.js +24 -1
  228. package/src/proxy.js +0 -3
  229. package/src/runtime/express/Dockerfile +4 -0
  230. package/src/runtime/express/Express.js +33 -10
  231. package/src/runtime/lampp/Dockerfile +13 -2
  232. package/src/runtime/lampp/Lampp.js +33 -17
  233. package/src/runtime/wp/Dockerfile +68 -0
  234. package/src/runtime/wp/Wp.js +639 -0
  235. package/src/server/auth.js +36 -15
  236. package/src/server/backup.js +39 -12
  237. package/src/server/besu-genesis-generator.js +1630 -0
  238. package/src/server/client-build-docs.js +133 -17
  239. package/src/server/client-build-live.js +9 -18
  240. package/src/server/client-build.js +229 -101
  241. package/src/server/client-dev-server.js +14 -13
  242. package/src/server/client-formatted.js +109 -57
  243. package/src/server/conf.js +391 -164
  244. package/src/server/cron.js +27 -24
  245. package/src/server/dns.js +29 -12
  246. package/src/server/downloader.js +0 -2
  247. package/src/server/ipfs-client.js +24 -1
  248. package/src/server/logger.js +27 -9
  249. package/src/server/object-layer.js +217 -103
  250. package/src/server/peer.js +8 -2
  251. package/src/server/process.js +1 -50
  252. package/src/server/proxy.js +4 -8
  253. package/src/server/runtime.js +30 -9
  254. package/src/server/semantic-layer-generator-floor.js +359 -0
  255. package/src/server/semantic-layer-generator-skin.js +1294 -0
  256. package/src/server/semantic-layer-generator.js +116 -555
  257. package/src/server/ssr.js +0 -3
  258. package/src/server/start.js +19 -12
  259. package/src/server/tls.js +0 -2
  260. package/src/server.js +0 -4
  261. package/src/ws/IoInterface.js +1 -10
  262. package/src/ws/IoServer.js +14 -33
  263. package/src/ws/core/channels/core.ws.chat.js +65 -20
  264. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  265. package/src/ws/core/channels/core.ws.stream.js +90 -31
  266. package/src/ws/core/core.ws.connection.js +12 -33
  267. package/src/ws/core/core.ws.emit.js +10 -26
  268. package/src/ws/core/core.ws.server.js +25 -58
  269. package/src/ws/default/channels/default.ws.main.js +53 -12
  270. package/src/ws/default/default.ws.connection.js +26 -13
  271. package/src/ws/default/default.ws.server.js +30 -12
  272. package/.env.development +0 -43
  273. package/.env.test +0 -43
  274. package/hardhat/contracts/CryptoKoyn.sol +0 -59
  275. package/hardhat/contracts/ItemLedger.sol +0 -73
  276. package/hardhat/contracts/Lock.sol +0 -34
  277. package/hardhat/hardhat.config.cjs +0 -45
  278. package/hardhat/ignition/modules/Lock.js +0 -18
  279. package/hardhat/networks/cryptokoyn-itemledger.network.json +0 -29
  280. package/hardhat/scripts/deployCryptokoyn.cjs +0 -25
  281. package/hardhat/scripts/deployItemledger.cjs +0 -25
  282. package/hardhat/test/Lock.js +0 -126
  283. package/hardhat/white-paper.md +0 -581
  284. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  285. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  286. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  287. package/src/client/components/default/ElementsDefault.js +0 -38
  288. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  289. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  290. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  291. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  292. package/src/ws/core/management/core.ws.chat.js +0 -8
  293. package/src/ws/core/management/core.ws.mailer.js +0 -16
  294. package/src/ws/core/management/core.ws.stream.js +0 -8
  295. package/src/ws/default/management/default.ws.main.js +0 -8
  296. package/white-paper.md +0 -581
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Fallback World Generator — pure-function module.
3
+ *
4
+ * Produces a complete in-memory multi-map world with portal topology,
5
+ * floor tiles, obstacles, foreground, and bots. Nothing is persisted
6
+ * to MongoDB — the result is regenerated on every call.
7
+ *
8
+ * Everything that can be random IS random (counts within ranges,
9
+ * positions, dimensions). The floor is the exception: it deterministically
10
+ * covers the entire map grid so there are no gaps.
11
+ *
12
+ * Shared by:
13
+ * - gRPC server (getFullInstance fallback path)
14
+ * - CyberiaInstanceService (API / GUI fallback endpoint)
15
+ *
16
+ * All exported functions are stateless and synchronous.
17
+ *
18
+ * @module src/api/cyberia-instance/cyberia-fallback-world
19
+ */
20
+
21
+ import {
22
+ connectPortals,
23
+ generateObstacles,
24
+ generateForeground,
25
+ colorToRgba,
26
+ findColor,
27
+ randInt,
28
+ OccupancyGrid,
29
+ BOT_RANGE,
30
+ BOT_WEAPON_CHANCE,
31
+ PORTAL_DIM_RANGE,
32
+ PORTAL_COUNT_RANGE,
33
+ PORTAL_MODE_LIST,
34
+ PORTAL_MODE_COLOR_KEY,
35
+ EXTRA_PORTAL_MODES,
36
+ PORTAL_MODES,
37
+ } from './cyberia-portal-connector.js';
38
+
39
+ import {
40
+ CYBERIA_INSTANCE_CONF_DEFAULTS,
41
+ ENTITY_TYPE_DEFAULTS,
42
+ } from '../cyberia-instance-conf/cyberia-instance-conf.defaults.js';
43
+
44
+ import { DefaultCyberiaItems } from '../../client/components/cyberia-portal/CommonCyberiaPortal.js';
45
+
46
+ // ── Defaults ─────────────────────────────────────────────────────────────────
47
+
48
+ const DEFAULT_MAP_COUNT = 4;
49
+ const DEFAULT_GRID_SIZE = 64;
50
+ const DEFAULT_FLOOR_TILE_DIM = 4;
51
+ const DEFAULT_BOT_DIM_RANGE = [2, 3];
52
+
53
+ /** NPC skin pool — all items with type 'skin' from DefaultCyberiaItems. */
54
+ const BOT_SKIN_POOL = DefaultCyberiaItems.filter((e) => e.item.type === 'skin').map((e) => e.item.id);
55
+
56
+ // ── Floor generator ──────────────────────────────────────────────────────────
57
+
58
+ /**
59
+ * Generate floor tiles that cover the entire map grid.
60
+ * Floor is NOT random — it tiles deterministically so every cell is covered.
61
+ *
62
+ * @param {{ gridX: number, gridY: number }} mapDims
63
+ * @param {Array<{ key: string, r: number, g: number, b: number, a: number }>} colors
64
+ * @param {object} [opts]
65
+ * @param {number} [opts.tileDim=4] Floor tile size in cells.
66
+ * @returns {object[]}
67
+ */
68
+ function generateFloorEntities(mapDims, colors, opts = {}) {
69
+ const { tileDim = DEFAULT_FLOOR_TILE_DIM } = opts;
70
+ const floorDefault = ENTITY_TYPE_DEFAULTS.find((d) => d.entityType === 'floor');
71
+ const floorItemIds = floorDefault?.liveItemIds?.length ? [...floorDefault.liveItemIds] : [];
72
+ const floorColor = findColor(colors, 'FLOOR');
73
+ const rgba = floorColor ? colorToRgba(floorColor) : '';
74
+
75
+ const entities = [];
76
+ for (let y = 0; y < mapDims.gridY; y += tileDim) {
77
+ for (let x = 0; x < mapDims.gridX; x += tileDim) {
78
+ entities.push({
79
+ entityType: 'floor',
80
+ initCellX: x,
81
+ initCellY: y,
82
+ dimX: Math.min(tileDim, mapDims.gridX - x),
83
+ dimY: Math.min(tileDim, mapDims.gridY - y),
84
+ color: rgba,
85
+ objectLayerItemIds: floorItemIds,
86
+ });
87
+ }
88
+ }
89
+ return entities;
90
+ }
91
+
92
+ // ── Portal entity generator ──────────────────────────────────────────────────
93
+
94
+ /**
95
+ * Generate a portal entity at a random walkable position with random dimensions.
96
+ *
97
+ * @param {{ gridX: number, gridY: number }} mapDims
98
+ * @param {Array<{ key: string, r: number, g: number, b: number, a: number }>} colors
99
+ * @param {OccupancyGrid} [grid] If provided, places portal only on walkable cells and marks them blocked.
100
+ * @param {string} [portalSubtype] One of PORTAL_MODE_LIST values. Determines the portal colour.
101
+ * @returns {object|null} Portal entity or null if no valid position found.
102
+ */
103
+ function generatePortalEntity(mapDims, colors, grid, portalSubtype) {
104
+ // Resolve colour from subtype-specific palette key, falling back to generic PORTAL
105
+ const colorKey = portalSubtype ? PORTAL_MODE_COLOR_KEY[portalSubtype] : 'PORTAL';
106
+ const portalColor = findColor(colors, colorKey) || findColor(colors, 'PORTAL');
107
+ const rgba = portalColor ? colorToRgba(portalColor) : 'rgba(0, 200, 200, 1)';
108
+ const dimX = randInt(PORTAL_DIM_RANGE[0], PORTAL_DIM_RANGE[1]);
109
+ const dimY = randInt(PORTAL_DIM_RANGE[0], PORTAL_DIM_RANGE[1]);
110
+
111
+ if (grid) {
112
+ const pos = grid.findPosition(dimX, dimY);
113
+ if (!pos) return null;
114
+ grid.block(pos.x, pos.y, dimX, dimY);
115
+ return {
116
+ entityType: 'portal',
117
+ portalSubtype: portalSubtype || 'inter-portal',
118
+ initCellX: pos.x,
119
+ initCellY: pos.y,
120
+ dimX,
121
+ dimY,
122
+ color: rgba,
123
+ objectLayerItemIds: [],
124
+ };
125
+ }
126
+
127
+ const maxX = Math.max(0, mapDims.gridX - dimX);
128
+ const maxY = Math.max(0, mapDims.gridY - dimY);
129
+ return {
130
+ entityType: 'portal',
131
+ portalSubtype: portalSubtype || 'inter-portal',
132
+ initCellX: randInt(0, maxX),
133
+ initCellY: randInt(0, maxY),
134
+ dimX,
135
+ dimY,
136
+ color: rgba,
137
+ objectLayerItemIds: [],
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Generate a random number of portal entities for a map, each with a
143
+ * randomly assigned portal subtype (and corresponding colour).
144
+ *
145
+ * @param {{ gridX: number, gridY: number }} mapDims
146
+ * @param {Array<{ key: string, r: number, g: number, b: number, a: number }>} colors
147
+ * @param {object} [opts]
148
+ * @param {number} [opts.count] Override count (ignores range).
149
+ * @param {OccupancyGrid} [opts.grid] If provided, places portals only on walkable cells.
150
+ * @returns {object[]}
151
+ */
152
+ function generatePortalEntities(mapDims, colors, opts = {}) {
153
+ const count = opts.count ?? randInt(PORTAL_COUNT_RANGE[0], PORTAL_COUNT_RANGE[1]);
154
+ const entities = [];
155
+ for (let i = 0; i < count; i++) {
156
+ // First portal is always inter-portal (reserved for the ring topology);
157
+ // extra portals get a random non-ring subtype.
158
+ const subtype =
159
+ i === 0 ? PORTAL_MODES.INTER_PORTAL : EXTRA_PORTAL_MODES[Math.floor(Math.random() * EXTRA_PORTAL_MODES.length)];
160
+ const portal = generatePortalEntity(mapDims, colors, opts.grid, subtype);
161
+ if (portal) entities.push(portal);
162
+ }
163
+ return entities;
164
+ }
165
+
166
+ // ── Bot generator ────────────────────────────────────────────────────────────
167
+
168
+ /**
169
+ * Generate bot entities for a map.
170
+ *
171
+ * - Each bot picks a random skin from BOT_SKIN_POOL for visual variety.
172
+ * - Random chance to also carry `atlas_pistol_mk2` weapon.
173
+ * - Random count within BOT_RANGE, random positions, random dimensions.
174
+ * - Uses the BOT palette color as fallback.
175
+ *
176
+ * @param {{ gridX: number, gridY: number }} mapDims
177
+ * @param {Array<{ key: string, r: number, g: number, b: number, a: number }>} colors
178
+ * @param {object} [opts]
179
+ * @param {number} [opts.count] Override count (ignores range).
180
+ * @param {number} [opts.spawnRadius]
181
+ * @param {number} [opts.aggroRange]
182
+ * @param {number} [opts.maxLife]
183
+ * @param {OccupancyGrid} [opts.grid] If provided, places bots only on walkable cells.
184
+ * @returns {object[]}
185
+ */
186
+ function generateBots(mapDims, colors, opts = {}) {
187
+ const count = opts.count ?? randInt(BOT_RANGE[0], BOT_RANGE[1]);
188
+ const { spawnRadius = 5, aggroRange = 10, maxLife = 100 } = opts;
189
+ const botColor = findColor(colors, 'BOT');
190
+ const rgba = botColor ? colorToRgba(botColor) : 'rgba(255, 128, 0, 1)';
191
+
192
+ const entities = [];
193
+ for (let i = 0; i < count; i++) {
194
+ const dim = randInt(DEFAULT_BOT_DIM_RANGE[0], DEFAULT_BOT_DIM_RANGE[1]);
195
+
196
+ let cellX, cellY;
197
+ if (opts.grid) {
198
+ const pos = opts.grid.findPosition(dim, dim);
199
+ if (!pos) continue;
200
+ opts.grid.block(pos.x, pos.y, dim, dim);
201
+ cellX = pos.x;
202
+ cellY = pos.y;
203
+ } else {
204
+ const maxX = Math.max(0, mapDims.gridX - dim);
205
+ const maxY = Math.max(0, mapDims.gridY - dim);
206
+ cellX = randInt(0, maxX);
207
+ cellY = randInt(0, maxY);
208
+ }
209
+
210
+ const skin = BOT_SKIN_POOL[Math.floor(Math.random() * BOT_SKIN_POOL.length)];
211
+ const hasWeapon = Math.random() < BOT_WEAPON_CHANCE;
212
+ const itemIds = hasWeapon ? [skin, 'atlas_pistol_mk2'] : [skin];
213
+
214
+ entities.push({
215
+ entityType: 'bot',
216
+ initCellX: cellX,
217
+ initCellY: cellY,
218
+ dimX: dim,
219
+ dimY: dim,
220
+ color: rgba,
221
+ objectLayerItemIds: itemIds,
222
+ spawnRadius,
223
+ aggroRange,
224
+ maxLife,
225
+ lifeRegen: 0,
226
+ });
227
+ }
228
+ return entities;
229
+ }
230
+
231
+ // ── Single map generator ─────────────────────────────────────────────────────
232
+
233
+ /**
234
+ * Generate a complete in-memory map with all entity types.
235
+ * Everything except floor tiles is randomized.
236
+ *
237
+ * @param {string} mapCode
238
+ * @param {Array} colors Palette from CYBERIA_INSTANCE_CONF_DEFAULTS.
239
+ * @param {object} [opts]
240
+ * @param {number} [opts.gridSize]
241
+ * @param {number} [opts.obstacleCount]
242
+ * @param {number} [opts.foregroundCount]
243
+ * @param {number} [opts.botCount]
244
+ * @returns {object} CyberiaMap-shaped plain object.
245
+ */
246
+ function generateFallbackMap(mapCode, colors, opts = {}) {
247
+ const gridSize = opts.gridSize || DEFAULT_GRID_SIZE;
248
+ const mapDims = { gridX: gridSize, gridY: gridSize };
249
+
250
+ // 1. Floor — deterministic full coverage
251
+ const floors = generateFloorEntities(mapDims, colors);
252
+
253
+ // 2. Obstacles — random count, position, dimensions (placed first)
254
+ const obstacles = generateObstacles(mapDims, colors, { count: opts.obstacleCount });
255
+
256
+ // 3. Build occupancy grid from obstacles
257
+ const grid = new OccupancyGrid(gridSize, gridSize);
258
+ grid.addObstacles(obstacles);
259
+
260
+ // 4. Portals — placed on walkable cells only, then blocked so bots avoid them
261
+ const portalEntities = generatePortalEntities(mapDims, colors, { grid });
262
+
263
+ // 5. Bots — placed on walkable cells (avoids obstacles and portals)
264
+ const bots = generateBots(mapDims, colors, { count: opts.botCount, grid });
265
+
266
+ // 6. Foreground — decorative, no collision restriction
267
+ const foreground = generateForeground(mapDims, colors, { count: opts.foregroundCount });
268
+
269
+ const entities = [...floors, ...obstacles, ...portalEntities, ...foreground, ...bots];
270
+
271
+ return {
272
+ code: mapCode,
273
+ name: `Fallback ${mapCode}`,
274
+ gridX: gridSize,
275
+ gridY: gridSize,
276
+ cellWidth: 32,
277
+ cellHeight: 32,
278
+ entities,
279
+ };
280
+ }
281
+
282
+ // ── Full world generator ─────────────────────────────────────────────────────
283
+
284
+ /**
285
+ * Generate a complete in-memory fallback world: multiple maps connected
286
+ * by a portal ring topology.
287
+ *
288
+ * Returns a plain-object structure matching what a real CyberiaInstance
289
+ * + CyberiaMap query would return, so consumers can treat it identically.
290
+ *
291
+ * Nothing is persisted to MongoDB.
292
+ *
293
+ * @param {object} [opts]
294
+ * @param {number} [opts.mapCount=4] Number of maps to generate.
295
+ * @param {number} [opts.gridSize=64] Grid size per map.
296
+ * @param {number} [opts.obstacleCount] Obstacles per map (random if omitted).
297
+ * @param {number} [opts.foregroundCount] Foreground entities per map (random if omitted).
298
+ * @param {number} [opts.botCount] Bots per map (random if omitted).
299
+ * @param {Array} [opts.colors] Override palette.
300
+ * @returns {{
301
+ * instance: object,
302
+ * maps: object[],
303
+ * portals: object[],
304
+ * topology: string,
305
+ * config: object,
306
+ * _fallback: true
307
+ * }}
308
+ */
309
+ function generateFallbackWorld(opts = {}) {
310
+ const {
311
+ mapCount = DEFAULT_MAP_COUNT,
312
+ gridSize,
313
+ obstacleCount,
314
+ foregroundCount,
315
+ botCount,
316
+ colors = CYBERIA_INSTANCE_CONF_DEFAULTS.colors,
317
+ } = opts;
318
+
319
+ // Generate map codes.
320
+ const mapCodes = [];
321
+ for (let i = 0; i < mapCount; i++) {
322
+ mapCodes.push(`fallback-map-${i}`);
323
+ }
324
+
325
+ // Generate each map (all randomized independently).
326
+ const maps = mapCodes.map((code) =>
327
+ generateFallbackMap(code, colors, {
328
+ gridSize,
329
+ obstacleCount,
330
+ foregroundCount,
331
+ botCount,
332
+ }),
333
+ );
334
+
335
+ // Connect maps with portal topology using the portal connector module.
336
+ const { portals, topology } = connectPortals(mapCodes, maps);
337
+
338
+ // Build the instance shell (never persisted — no mongoId).
339
+ const instance = {
340
+ code: 'fallback',
341
+ name: 'Fallback Instance',
342
+ description: 'Auto-generated procedural world (not persisted)',
343
+ tags: ['fallback', 'procedural'],
344
+ cyberiaMapCodes: mapCodes,
345
+ portals,
346
+ topologyMode: 'procedural',
347
+ };
348
+
349
+ return {
350
+ instance,
351
+ maps,
352
+ portals,
353
+ topology,
354
+ config: CYBERIA_INSTANCE_CONF_DEFAULTS,
355
+ _fallback: true,
356
+ };
357
+ }
358
+
359
+ // ── Public API ───────────────────────────────────────────────────────────────
360
+
361
+ export {
362
+ generateFallbackWorld,
363
+ generateFallbackMap,
364
+ generateFloorEntities,
365
+ generatePortalEntity,
366
+ generatePortalEntities,
367
+ generateBots,
368
+ };
@@ -0,0 +1,92 @@
1
+ import { loggerFactory } from '../../server/logger.js';
2
+ import { CyberiaInstanceService } from './cyberia-instance.service.js';
3
+
4
+ const logger = loggerFactory(import.meta);
5
+
6
+ const CyberiaInstanceController = {
7
+ fallbackWorld: async (req, res, options) => {
8
+ try {
9
+ const result = await CyberiaInstanceService.fallbackWorld(req);
10
+ return res.status(200).json({ status: 'success', data: result });
11
+ } catch (error) {
12
+ logger.error(error, error.stack);
13
+ return res.status(400).json({ status: 'error', message: error.message });
14
+ }
15
+ },
16
+ portalConnect: async (req, res, options) => {
17
+ try {
18
+ const result = await CyberiaInstanceService.portalConnect(req, res, options);
19
+ return res.status(200).json({ status: 'success', data: result });
20
+ } catch (error) {
21
+ logger.error(error, error.stack);
22
+ return res.status(400).json({ status: 'error', message: error.message });
23
+ }
24
+ },
25
+ post: async (req, res, options) => {
26
+ try {
27
+ const result = await CyberiaInstanceService.post(req, res, options);
28
+ return res.status(200).json({
29
+ status: 'success',
30
+ data: result,
31
+ });
32
+ } catch (error) {
33
+ logger.error(error, error.stack);
34
+ return res.status(400).json({
35
+ status: 'error',
36
+ message: error.message,
37
+ });
38
+ }
39
+ },
40
+ get: async (req, res, options) => {
41
+ try {
42
+ const { page, limit } = req.query;
43
+ const result = await CyberiaInstanceService.get(
44
+ { ...req, query: { ...req.query, page: parseInt(page), limit: parseInt(limit) } },
45
+ res,
46
+ options,
47
+ );
48
+ return res.status(200).json({
49
+ status: 'success',
50
+ data: result,
51
+ });
52
+ } catch (error) {
53
+ logger.error(error, error.stack);
54
+ return res.status(400).json({
55
+ status: 'error',
56
+ message: error.message,
57
+ });
58
+ }
59
+ },
60
+ put: async (req, res, options) => {
61
+ try {
62
+ const result = await CyberiaInstanceService.put(req, res, options);
63
+ return res.status(200).json({
64
+ status: 'success',
65
+ data: result,
66
+ });
67
+ } catch (error) {
68
+ logger.error(error, error.stack);
69
+ return res.status(400).json({
70
+ status: 'error',
71
+ message: error.message,
72
+ });
73
+ }
74
+ },
75
+ delete: async (req, res, options) => {
76
+ try {
77
+ const result = await CyberiaInstanceService.delete(req, res, options);
78
+ return res.status(200).json({
79
+ status: 'success',
80
+ data: result,
81
+ });
82
+ } catch (error) {
83
+ logger.error(error, error.stack);
84
+ return res.status(400).json({
85
+ status: 'error',
86
+ message: error.message,
87
+ });
88
+ }
89
+ },
90
+ };
91
+
92
+ export { CyberiaInstanceController };
@@ -0,0 +1,84 @@
1
+ import { Schema, model } from 'mongoose';
2
+
3
+ /**
4
+ * Directed edge in a graph.
5
+ * Each portal represents an adjacency relation:
6
+ * source node -> target node
7
+ */
8
+ const PortalEdgeSchema = new Schema(
9
+ {
10
+ // Origin node (graph source vertex)
11
+ sourceMapCode: { type: String, required: true, trim: true },
12
+ sourceCellX: { type: Number },
13
+ sourceCellY: { type: Number },
14
+
15
+ // Destination node (graph target vertex)
16
+ targetMapCode: { type: String, required: true, trim: true },
17
+ targetCellX: { type: Number },
18
+ targetCellY: { type: Number },
19
+
20
+ // Transport behaviour of this edge:
21
+ // inter-portal — teleport to a portal entity on another map
22
+ // inter-random — teleport to a random walkable cell on another map
23
+ // intra-random — teleport to a random walkable cell on the same map
24
+ // intra-portal — teleport to a portal entity on the same map
25
+ portalMode: {
26
+ type: String,
27
+ enum: ['inter-portal', 'inter-random', 'intra-random', 'intra-portal'],
28
+ default: 'inter-portal',
29
+ },
30
+ },
31
+ { _id: false },
32
+ );
33
+
34
+ /**
35
+ * Graph container / instance.
36
+ * The instance stores:
37
+ * - nodes: cyberiaMapCodes[]
38
+ * - edges: portals[]
39
+ */
40
+ const CyberiaInstanceSchema = new Schema(
41
+ {
42
+ // Instance identifier
43
+ code: { type: String, default: '', trim: true, unique: true },
44
+
45
+ name: { type: String, default: '', trim: true },
46
+ description: { type: String, default: '' },
47
+ tags: { type: [String], default: [] },
48
+ creator: { type: Schema.Types.ObjectId, ref: 'User' },
49
+ status: { type: String, default: 'unlisted' },
50
+ thumbnail: { type: Schema.Types.ObjectId, ref: 'File' },
51
+
52
+ // Vertex set of the graph
53
+ cyberiaMapCodes: { type: [String], default: [] },
54
+
55
+ // Directed edge set of the graph
56
+ portals: { type: [PortalEdgeSchema], default: [] },
57
+
58
+ // Optional topology generation mode
59
+ seed: { type: String, default: '' },
60
+ topologyMode: {
61
+ type: String,
62
+ enum: ['manual', 'procedural', 'hybrid'],
63
+ default: 'hybrid',
64
+ },
65
+
66
+ // Game server configuration (all tuning parameters live in a separate document).
67
+ // Linked to CyberiaInstanceConf.instanceCode === this.code.
68
+ conf: { type: Schema.Types.ObjectId, ref: 'CyberiaInstanceConf' },
69
+ },
70
+ { timestamps: true },
71
+ );
72
+
73
+ /**
74
+ * Indexes for fast adjacency lookups.
75
+ * Useful for traversing the graph by source or target node.
76
+ */
77
+ CyberiaInstanceSchema.index({ 'portals.sourceMapCode': 1 });
78
+ CyberiaInstanceSchema.index({ 'portals.targetMapCode': 1 });
79
+
80
+ const CyberiaInstanceModel = model('CyberiaInstance', CyberiaInstanceSchema);
81
+
82
+ const ProviderSchema = CyberiaInstanceSchema;
83
+
84
+ export { CyberiaInstanceSchema, CyberiaInstanceModel, ProviderSchema };
@@ -0,0 +1,63 @@
1
+ import { loggerFactory } from '../../server/logger.js';
2
+ import { CyberiaInstanceController } from './cyberia-instance.controller.js';
3
+ import { userGuard, adminGuard } from '../../server/auth.js';
4
+ import express from 'express';
5
+
6
+ const logger = loggerFactory(import.meta);
7
+
8
+ const CyberiaInstanceRouter = (options) => {
9
+ const router = express.Router();
10
+ const authMiddleware = options.authMiddleware;
11
+ // ── Custom actions (must come before generic /:id routes) ──────────────
12
+ router.get(`/fallback-world`, async (req, res) => await CyberiaInstanceController.fallbackWorld(req, res, options));
13
+ router.get(
14
+ `/:id/portal-connect`,
15
+ authMiddleware,
16
+ userGuard,
17
+ async (req, res) => await CyberiaInstanceController.portalConnect(req, res, options),
18
+ );
19
+
20
+ router.post(
21
+ `/:id`,
22
+ authMiddleware,
23
+ userGuard,
24
+ async (req, res) => await CyberiaInstanceController.post(req, res, options),
25
+ );
26
+ router.post(
27
+ `/`,
28
+ authMiddleware,
29
+ userGuard,
30
+ async (req, res) => await CyberiaInstanceController.post(req, res, options),
31
+ );
32
+ router.get(`/:id`, async (req, res) => await CyberiaInstanceController.get(req, res, options));
33
+ router.get(`/`, async (req, res) => await CyberiaInstanceController.get(req, res, options));
34
+ router.put(
35
+ `/:id`,
36
+ authMiddleware,
37
+ userGuard,
38
+ async (req, res) => await CyberiaInstanceController.put(req, res, options),
39
+ );
40
+ router.put(
41
+ `/`,
42
+ authMiddleware,
43
+ userGuard,
44
+ async (req, res) => await CyberiaInstanceController.put(req, res, options),
45
+ );
46
+ router.delete(
47
+ `/:id`,
48
+ authMiddleware,
49
+ userGuard,
50
+ async (req, res) => await CyberiaInstanceController.delete(req, res, options),
51
+ );
52
+ router.delete(
53
+ `/`,
54
+ authMiddleware,
55
+ adminGuard,
56
+ async (req, res) => await CyberiaInstanceController.delete(req, res, options),
57
+ );
58
+ return router;
59
+ };
60
+
61
+ const ApiRouter = CyberiaInstanceRouter;
62
+
63
+ export { ApiRouter, CyberiaInstanceRouter };