cyberia 3.1.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 (208) hide show
  1. package/.env.example +0 -2
  2. package/.github/workflows/engine-cyberia.cd.yml +10 -8
  3. package/.github/workflows/engine-cyberia.ci.yml +12 -29
  4. package/.github/workflows/ghpkg.ci.yml +4 -4
  5. package/.github/workflows/npmpkg.ci.yml +28 -11
  6. package/.github/workflows/publish.ci.yml +21 -2
  7. package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -5
  8. package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
  9. package/.github/workflows/release.cd.yml +13 -8
  10. package/CHANGELOG.md +433 -1
  11. package/CLI-HELP.md +57 -7
  12. package/Dockerfile +4 -2
  13. package/README.md +347 -22
  14. package/bin/build.js +5 -2
  15. package/bin/cyberia.js +1789 -112
  16. package/bin/deploy.js +177 -124
  17. package/bin/file.js +3 -0
  18. package/bin/index.js +1789 -112
  19. package/conf.js +64 -8
  20. package/deployment.yaml +92 -20
  21. package/hardhat/hardhat.config.js +13 -13
  22. package/hardhat/ignition/modules/ObjectLayerToken.js +1 -1
  23. package/hardhat/package-lock.json +2554 -5859
  24. package/hardhat/package.json +13 -22
  25. package/hardhat/scripts/deployObjectLayerToken.js +1 -1
  26. package/hardhat/test/ObjectLayerToken.js +4 -2
  27. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  28. package/hardhat/types/ethers-contracts/common.ts +92 -0
  29. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  30. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  31. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  32. package/hardhat/types/ethers-contracts/index.ts +6 -0
  33. package/jsdoc.dd-cyberia.json +64 -55
  34. package/jsdoc.json +64 -55
  35. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
  36. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
  37. package/manifests/deployment/dd-cyberia-development/deployment.yaml +92 -20
  38. package/manifests/deployment/dd-cyberia-development/proxy.yaml +54 -18
  39. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  40. package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
  41. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  42. package/manifests/deployment/playwright/deployment.yaml +1 -1
  43. package/nodemon.json +1 -1
  44. package/package.json +22 -16
  45. package/proxy.yaml +54 -18
  46. package/scripts/rhel-grpc-setup.sh +56 -0
  47. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +44 -0
  48. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +16 -0
  49. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  50. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
  51. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  52. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  53. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  54. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  55. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  56. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  57. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  58. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  59. package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
  60. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  61. package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
  62. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  63. package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
  64. package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
  65. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  66. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
  67. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
  68. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  69. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
  70. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  71. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  72. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  73. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  74. package/src/api/file/file.ref.json +18 -0
  75. package/src/api/ipfs/ipfs.controller.js +4 -25
  76. package/src/api/ipfs/ipfs.model.js +43 -34
  77. package/src/api/ipfs/ipfs.router.js +8 -13
  78. package/src/api/ipfs/ipfs.service.js +54 -102
  79. package/src/api/object-layer/README.md +347 -22
  80. package/src/api/object-layer/object-layer.router.js +30 -0
  81. package/src/api/object-layer/object-layer.service.js +114 -31
  82. package/src/api/user/user.service.js +8 -7
  83. package/src/cli/cluster.js +7 -7
  84. package/src/cli/db.js +710 -827
  85. package/src/cli/deploy.js +151 -93
  86. package/src/cli/env.js +29 -0
  87. package/src/cli/fs.js +5 -2
  88. package/src/cli/index.js +48 -2
  89. package/src/cli/kubectl.js +211 -0
  90. package/src/cli/release.js +284 -0
  91. package/src/cli/repository.js +438 -75
  92. package/src/cli/run.js +195 -35
  93. package/src/cli/secrets.js +73 -0
  94. package/src/cli/test.js +3 -3
  95. package/src/client/Cryptokoyn.index.js +3 -4
  96. package/src/client/CyberiaPortal.index.js +3 -4
  97. package/src/client/Default.index.js +3 -4
  98. package/src/client/Itemledger.index.js +3 -4
  99. package/src/client/Underpost.index.js +3 -4
  100. package/src/client/components/core/AppStore.js +69 -0
  101. package/src/client/components/core/CalendarCore.js +2 -2
  102. package/src/client/components/core/DropDown.js +137 -17
  103. package/src/client/components/core/Keyboard.js +2 -2
  104. package/src/client/components/core/LogIn.js +2 -2
  105. package/src/client/components/core/LogOut.js +2 -2
  106. package/src/client/components/core/Modal.js +0 -1
  107. package/src/client/components/core/Panel.js +0 -1
  108. package/src/client/components/core/PanelForm.js +19 -19
  109. package/src/client/components/core/SocketIo.js +82 -29
  110. package/src/client/components/core/SocketIoHandler.js +75 -0
  111. package/src/client/components/core/Stream.js +143 -95
  112. package/src/client/components/core/Webhook.js +40 -7
  113. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  114. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
  115. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
  116. package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
  117. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  118. package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
  119. package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
  120. package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
  121. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
  122. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  123. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +216 -30
  124. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -3
  125. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
  126. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +40 -7
  127. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +4 -0
  128. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  129. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  130. package/src/client/components/default/AppStoreDefault.js +5 -0
  131. package/src/client/components/default/LogInDefault.js +3 -3
  132. package/src/client/components/default/LogOutDefault.js +2 -2
  133. package/src/client/components/default/MenuDefault.js +5 -5
  134. package/src/client/components/default/SocketIoDefault.js +3 -51
  135. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  136. package/src/client/components/itemledger/LogInItemledger.js +3 -3
  137. package/src/client/components/itemledger/LogOutItemledger.js +2 -2
  138. package/src/client/components/itemledger/MenuItemledger.js +3 -3
  139. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  140. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  141. package/src/client/components/underpost/LogInUnderpost.js +3 -3
  142. package/src/client/components/underpost/LogOutUnderpost.js +2 -2
  143. package/src/client/components/underpost/MenuUnderpost.js +5 -5
  144. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  145. package/src/client/services/core/core.service.js +20 -8
  146. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
  147. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  148. package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
  149. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  150. package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
  151. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
  152. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  153. package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
  154. package/src/client/services/instance/instance.management.js +2 -2
  155. package/src/client/services/ipfs/ipfs.service.js +3 -23
  156. package/src/client/services/object-layer/object-layer.management.js +3 -3
  157. package/src/client/services/object-layer/object-layer.service.js +21 -0
  158. package/src/client/services/user/user.management.js +2 -2
  159. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  160. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
  161. package/src/grpc/cyberia/README.md +326 -0
  162. package/src/grpc/cyberia/grpc-server.js +530 -0
  163. package/src/index.js +24 -1
  164. package/src/runtime/express/Dockerfile +4 -0
  165. package/src/runtime/express/Express.js +18 -1
  166. package/src/runtime/lampp/Dockerfile +13 -2
  167. package/src/runtime/lampp/Lampp.js +27 -4
  168. package/src/runtime/wp/Dockerfile +68 -0
  169. package/src/runtime/wp/Wp.js +639 -0
  170. package/src/server/auth.js +24 -1
  171. package/src/server/backup.js +37 -9
  172. package/src/server/client-build-docs.js +9 -2
  173. package/src/server/client-build.js +31 -31
  174. package/src/server/client-formatted.js +109 -57
  175. package/src/server/conf.js +24 -9
  176. package/src/server/cron.js +25 -23
  177. package/src/server/dns.js +2 -1
  178. package/src/server/ipfs-client.js +24 -1
  179. package/src/server/object-layer.js +149 -108
  180. package/src/server/peer.js +8 -0
  181. package/src/server/runtime.js +25 -1
  182. package/src/server/semantic-layer-generator-floor.js +359 -0
  183. package/src/server/semantic-layer-generator-skin.js +1294 -0
  184. package/src/server/semantic-layer-generator.js +116 -555
  185. package/src/server/start.js +2 -2
  186. package/src/ws/IoInterface.js +1 -10
  187. package/src/ws/IoServer.js +14 -33
  188. package/src/ws/core/channels/core.ws.chat.js +65 -20
  189. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  190. package/src/ws/core/channels/core.ws.stream.js +90 -31
  191. package/src/ws/core/core.ws.connection.js +12 -33
  192. package/src/ws/core/core.ws.emit.js +10 -26
  193. package/src/ws/core/core.ws.server.js +25 -58
  194. package/src/ws/default/channels/default.ws.main.js +53 -12
  195. package/src/ws/default/default.ws.connection.js +26 -13
  196. package/src/ws/default/default.ws.server.js +30 -12
  197. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  198. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  199. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  200. package/src/client/components/default/ElementsDefault.js +0 -38
  201. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  202. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  203. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  204. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  205. package/src/ws/core/management/core.ws.chat.js +0 -8
  206. package/src/ws/core/management/core.ws.mailer.js +0 -16
  207. package/src/ws/core/management/core.ws.stream.js +0 -8
  208. package/src/ws/default/management/default.ws.main.js +0 -8
@@ -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 };
@@ -0,0 +1,191 @@
1
+ import { DataBaseProvider } from '../../db/DataBaseProvider.js';
2
+ import { loggerFactory } from '../../server/logger.js';
3
+ import { DataQuery } from '../../server/data-query.js';
4
+ import { connectPortals, generateProceduralEntities } from './cyberia-portal-connector.js';
5
+ import { generateFallbackWorld } from './cyberia-fallback-world.js';
6
+ import { CYBERIA_INSTANCE_CONF_DEFAULTS } from '../cyberia-instance-conf/cyberia-instance-conf.defaults.js';
7
+
8
+ const logger = loggerFactory(import.meta);
9
+
10
+ const CyberiaInstanceService = {
11
+ post: async (req, res, options) => {
12
+ /** @type {import('./cyberia-instance.model.js').CyberiaInstanceModel} */
13
+ const CyberiaInstance = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.CyberiaInstance;
14
+ const CyberiaInstanceConf =
15
+ DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.CyberiaInstanceConf;
16
+ if (req.auth && req.auth.user) req.body.creator = req.auth.user._id;
17
+ const instance = await new CyberiaInstance(req.body).save();
18
+
19
+ // Auto-upsert a CyberiaInstanceConf for this instance using schema defaults.
20
+ // $setOnInsert ensures existing conf documents are never overwritten.
21
+ if (instance.code && CyberiaInstanceConf) {
22
+ try {
23
+ const conf = await CyberiaInstanceConf.findOneAndUpdate(
24
+ { instanceCode: instance.code },
25
+ { $setOnInsert: { instanceCode: instance.code } },
26
+ { upsert: true, new: true },
27
+ );
28
+ if (conf && !instance.conf) {
29
+ await CyberiaInstance.findByIdAndUpdate(instance._id, { conf: conf._id });
30
+ instance.conf = conf._id;
31
+ }
32
+ } catch (e) {
33
+ logger.error('auto-upsert CyberiaInstanceConf failed:', e);
34
+ }
35
+ }
36
+
37
+ return instance;
38
+ },
39
+ get: async (req, res, options) => {
40
+ /** @type {import('./cyberia-instance.model.js').CyberiaInstanceModel} */
41
+ const CyberiaInstance = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.CyberiaInstance;
42
+ const populateCreator = { path: 'creator', model: 'User', select: '_id username' };
43
+ if (req.params.id) return await CyberiaInstance.findById(req.params.id).populate(populateCreator);
44
+
45
+ // Parse query parameters using DataQuery helper
46
+ const { query, sort, skip, limit, page } = DataQuery.parse(req.query);
47
+
48
+ const [data, total] = await Promise.all([
49
+ CyberiaInstance.find(query).sort(sort).limit(limit).skip(skip).populate(populateCreator),
50
+ CyberiaInstance.countDocuments(query),
51
+ ]);
52
+
53
+ const totalPages = Math.ceil(total / limit);
54
+ return { data, total, page, totalPages };
55
+ },
56
+ put: async (req, res, options) => {
57
+ /** @type {import('./cyberia-instance.model.js').CyberiaInstanceModel} */
58
+ const CyberiaInstance = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.CyberiaInstance;
59
+ const instance = await CyberiaInstance.findById(req.params.id);
60
+ if (!instance) throw new Error('instance not found');
61
+ if (req.auth.user.role !== 'admin' && String(instance.creator) !== String(req.auth.user._id))
62
+ throw new Error('insufficient permission');
63
+ if (req.body.thumbnail && instance.thumbnail && String(req.body.thumbnail) !== String(instance.thumbnail)) {
64
+ const File = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.File;
65
+ await File.findByIdAndDelete(instance.thumbnail);
66
+ }
67
+ return await CyberiaInstance.findByIdAndUpdate(req.params.id, req.body, { returnDocument: 'after' });
68
+ },
69
+ /**
70
+ * Central portal connector endpoint.
71
+ *
72
+ * Delegates topology computation to the pure-function `connectPortals()`
73
+ * from cyberia-portal-connector.js so the same logic can be used by the
74
+ * GUI without a DB dependency.
75
+ *
76
+ * Optionally generates procedural fallback obstacle/foreground entities
77
+ * for maps that have none, controlled by query flags:
78
+ * ?generateEntities=true — append procedural obstacles & foreground
79
+ * ?obstacleCount=N — obstacles per map (default 5)
80
+ * ?foregroundCount=N — foreground per map (default 3)
81
+ * ?persist=true — save generated portals & entities to DB
82
+ */
83
+ portalConnect: async (req, res, options) => {
84
+ const CyberiaInstance = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.CyberiaInstance;
85
+ const CyberiaMap = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.CyberiaMap;
86
+
87
+ const instance = await CyberiaInstance.findById(req.params.id).lean();
88
+ if (!instance) throw new Error('instance not found');
89
+
90
+ const mapCodes = instance.cyberiaMapCodes || [];
91
+
92
+ // Load maps with the fields needed by the connector.
93
+ const mapDocs = await CyberiaMap.find(
94
+ { code: { $in: mapCodes } },
95
+ {
96
+ code: 1,
97
+ gridX: 1,
98
+ gridY: 1,
99
+ entities: 1,
100
+ },
101
+ ).lean();
102
+
103
+ // ── Portal topology (pure function) ──────────────────────────────────
104
+ const result = connectPortals(mapCodes, mapDocs);
105
+
106
+ // ── Procedural entity generation (optional) ──────────────────────────
107
+ const colors = CYBERIA_INSTANCE_CONF_DEFAULTS.colors;
108
+ const wantEntities = req.query?.generateEntities === 'true';
109
+ const obstacleCount = req.query?.obstacleCount ? parseInt(req.query.obstacleCount, 10) : undefined;
110
+ const foregroundCount = req.query?.foregroundCount ? parseInt(req.query.foregroundCount, 10) : undefined;
111
+ const seed = instance.seed || '';
112
+
113
+ const generatedEntities = {};
114
+ if (wantEntities) {
115
+ for (const doc of mapDocs) {
116
+ const hasObstacles = (doc.entities || []).some((e) => e.entityType === 'obstacle');
117
+ const hasForeground = (doc.entities || []).some((e) => e.entityType === 'foreground');
118
+ if (!hasObstacles || !hasForeground) {
119
+ const mapSeed = seed ? `${seed}:${doc.code}` : doc.code;
120
+ const generated = generateProceduralEntities({ gridX: doc.gridX || 16, gridY: doc.gridY || 16 }, colors, {
121
+ obstacleCount,
122
+ foregroundCount,
123
+ seed: mapSeed,
124
+ });
125
+ generatedEntities[doc.code] = {
126
+ obstacles: hasObstacles ? [] : generated.obstacles,
127
+ foreground: hasForeground ? [] : generated.foreground,
128
+ };
129
+ }
130
+ }
131
+ }
132
+
133
+ // ── Persist to DB when requested ─────────────────────────────────────
134
+ const persist = req.query?.persist === 'true';
135
+ if (persist) {
136
+ await CyberiaInstance.findByIdAndUpdate(req.params.id, { portals: result.portals });
137
+ for (const [mapCode, ents] of Object.entries(generatedEntities)) {
138
+ const toAdd = [...ents.obstacles, ...ents.foreground];
139
+ if (toAdd.length > 0) {
140
+ await CyberiaMap.findOneAndUpdate({ code: mapCode }, { $push: { entities: { $each: toAdd } } });
141
+ }
142
+ }
143
+ }
144
+
145
+ return {
146
+ ...result,
147
+ ...(wantEntities ? { generatedEntities } : {}),
148
+ persisted: persist,
149
+ };
150
+ },
151
+
152
+ delete: async (req, res, options) => {
153
+ /** @type {import('./cyberia-instance.model.js').CyberiaInstanceModel} */
154
+ const CyberiaInstance = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.CyberiaInstance;
155
+ if (req.params.id) {
156
+ const instance = await CyberiaInstance.findById(req.params.id);
157
+ if (!instance) throw new Error('instance not found');
158
+ if (req.auth.user.role !== 'admin' && String(instance.creator) !== String(req.auth.user._id))
159
+ throw new Error('insufficient permission');
160
+ if (instance.thumbnail) {
161
+ const File = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.File;
162
+ await File.findByIdAndDelete(instance.thumbnail);
163
+ }
164
+ return await CyberiaInstance.findByIdAndDelete(req.params.id);
165
+ } else return await CyberiaInstance.deleteMany();
166
+ },
167
+
168
+ /**
169
+ * Return an in-memory procedural fallback world.
170
+ *
171
+ * Nothing is persisted to MongoDB. The world is regenerated on every
172
+ * call but stays deterministic for a given seed.
173
+ *
174
+ * Query params:
175
+ * ?mapCount=<number> — maps to generate (default: 4)
176
+ * ?botCount=<number> — bots per map (random 8–16 if omitted)
177
+ * ?obstacleCount=<number> — obstacles per map (random 12–20 if omitted)
178
+ * ?foregroundCount=<number>— foreground per map (random 6–12 if omitted)
179
+ */
180
+ fallbackWorld: async (req) => {
181
+ const q = req.query || {};
182
+ return generateFallbackWorld({
183
+ mapCount: q.mapCount ? parseInt(q.mapCount, 10) : undefined,
184
+ botCount: q.botCount ? parseInt(q.botCount, 10) : undefined,
185
+ obstacleCount: q.obstacleCount ? parseInt(q.obstacleCount, 10) : undefined,
186
+ foregroundCount: q.foregroundCount ? parseInt(q.foregroundCount, 10) : undefined,
187
+ });
188
+ },
189
+ };
190
+
191
+ export { CyberiaInstanceService };