cyberia 3.2.9 → 3.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +6 -0
  2. package/.github/workflows/npmpkg.ci.yml +1 -0
  3. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  4. package/.github/workflows/release.cd.yml +1 -0
  5. package/.vscode/extensions.json +9 -9
  6. package/.vscode/settings.json +20 -4
  7. package/CHANGELOG.md +213 -1
  8. package/CLI-HELP.md +92 -23
  9. package/README.md +190 -348
  10. package/bin/build.js +24 -8
  11. package/bin/build.template.js +187 -0
  12. package/bin/cyberia.js +229 -52
  13. package/bin/deploy.js +12 -2
  14. package/bin/index.js +229 -52
  15. package/bump.config.js +26 -0
  16. package/conf.js +130 -24
  17. package/deployment.yaml +4 -2
  18. package/hardhat/package-lock.json +113 -144
  19. package/hardhat/package.json +4 -3
  20. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  21. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  22. package/manifests/deployment/dd-cyberia-development/deployment.yaml +4 -2
  23. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  24. package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
  25. package/manifests/kind-config-dev.yaml +8 -0
  26. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  27. package/manifests/mongodb/pv-pvc.yaml +44 -8
  28. package/manifests/mongodb/statefulset.yaml +55 -68
  29. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  30. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  31. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  32. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  33. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  34. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  35. package/manifests/valkey/statefulset.yaml +1 -1
  36. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  37. package/package.json +27 -15
  38. package/scripts/ipxe-setup.sh +52 -49
  39. package/scripts/k3s-node-setup.sh +81 -46
  40. package/scripts/lxd-vm-setup.sh +193 -8
  41. package/scripts/maas-nat-firewalld.sh +145 -0
  42. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +38 -33
  43. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +16 -16
  44. package/src/api/core/core.router.js +19 -14
  45. package/src/api/core/core.service.js +5 -5
  46. package/src/api/crypto/crypto.router.js +18 -12
  47. package/src/api/crypto/crypto.service.js +3 -3
  48. package/src/api/cyberia-action/cyberia-action.model.js +1 -1
  49. package/src/api/cyberia-action/cyberia-action.router.js +22 -18
  50. package/src/api/cyberia-action/cyberia-action.service.js +5 -5
  51. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +74 -0
  52. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +99 -0
  53. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +98 -0
  54. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  55. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +25 -20
  56. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +6 -6
  57. package/src/api/cyberia-entity/cyberia-entity.router.js +22 -18
  58. package/src/api/cyberia-entity/cyberia-entity.service.js +5 -5
  59. package/src/api/cyberia-instance/cyberia-fallback-world.js +79 -4
  60. package/src/api/cyberia-instance/cyberia-instance.router.js +57 -52
  61. package/src/api/cyberia-instance/cyberia-instance.service.js +10 -10
  62. package/src/api/cyberia-instance/cyberia-world-generator.js +3 -3
  63. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +14 -48
  64. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +22 -18
  65. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +5 -5
  66. package/src/api/cyberia-map/cyberia-map.router.js +35 -30
  67. package/src/api/cyberia-map/cyberia-map.service.js +7 -7
  68. package/src/api/cyberia-quest/cyberia-quest.model.js +1 -1
  69. package/src/api/cyberia-quest/cyberia-quest.router.js +22 -18
  70. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  71. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +22 -18
  72. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +5 -5
  73. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +451 -0
  74. package/src/api/default/default.router.js +22 -18
  75. package/src/api/default/default.service.js +5 -5
  76. package/src/api/document/document.router.js +28 -23
  77. package/src/api/document/document.service.js +100 -23
  78. package/src/api/file/file.router.js +19 -13
  79. package/src/api/file/file.service.js +9 -7
  80. package/src/api/instance/instance.router.js +29 -24
  81. package/src/api/instance/instance.service.js +6 -6
  82. package/src/api/ipfs/ipfs.router.js +21 -16
  83. package/src/api/ipfs/ipfs.service.js +8 -8
  84. package/src/api/object-layer/object-layer.router.js +512 -507
  85. package/src/api/object-layer/object-layer.service.js +17 -14
  86. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +22 -18
  87. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +5 -5
  88. package/src/api/test/test.router.js +17 -12
  89. package/src/api/types.js +24 -0
  90. package/src/api/user/guest.service.js +5 -4
  91. package/src/api/user/user.router.js +297 -288
  92. package/src/api/user/user.service.js +100 -35
  93. package/src/cli/baremetal.js +132 -101
  94. package/src/cli/cluster.js +700 -232
  95. package/src/cli/db.js +59 -60
  96. package/src/cli/deploy.js +216 -137
  97. package/src/cli/fs.js +13 -3
  98. package/src/cli/index.js +80 -15
  99. package/src/cli/ipfs.js +4 -6
  100. package/src/cli/kubectl.js +4 -1
  101. package/src/cli/lxd.js +1099 -223
  102. package/src/cli/monitor.js +9 -3
  103. package/src/cli/release.js +334 -140
  104. package/src/cli/repository.js +68 -23
  105. package/src/cli/run.js +193 -49
  106. package/src/cli/secrets.js +11 -2
  107. package/src/cli/test.js +9 -3
  108. package/src/client/Default.index.js +9 -3
  109. package/src/client/components/core/Auth.js +5 -0
  110. package/src/client/components/core/ClientEvents.js +76 -0
  111. package/src/client/components/core/EventBus.js +4 -0
  112. package/src/client/components/core/Modal.js +82 -41
  113. package/src/client/components/core/PanelForm.js +56 -52
  114. package/src/client/components/core/Worker.js +162 -363
  115. package/src/client/components/cyberia/MapEngineCyberia.js +1 -1
  116. package/src/client/components/cyberia/SharedDefaultsCyberia.js +330 -0
  117. package/src/client/public/cyberia-docs/ARCHITECTURE.md +50 -410
  118. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +114 -327
  119. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +200 -222
  120. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +203 -185
  121. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  122. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +2 -2
  123. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  124. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  125. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  126. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  127. package/src/client/ssr/views/CyberiaServerMetrics.js +982 -0
  128. package/src/client/sw/core.sw.js +174 -112
  129. package/src/db/DataBaseProvider.js +115 -15
  130. package/src/db/mariadb/MariaDB.js +2 -1
  131. package/src/db/mongo/MongoBootstrap.js +657 -0
  132. package/src/db/mongo/MongooseDB.js +129 -21
  133. package/src/grpc/cyberia/grpc-server.js +25 -57
  134. package/src/index.js +1 -1
  135. package/src/runtime/cyberia-client/Dockerfile +24 -3
  136. package/src/runtime/cyberia-client/Dockerfile.dev +82 -0
  137. package/src/runtime/cyberia-server/Dockerfile +29 -4
  138. package/src/runtime/cyberia-server/Dockerfile.dev +71 -0
  139. package/src/runtime/express/Express.js +2 -2
  140. package/src/runtime/wp/Wp.js +8 -5
  141. package/src/server/auth.js +2 -2
  142. package/src/server/client-build-docs.js +1 -1
  143. package/src/server/client-build.js +94 -129
  144. package/src/server/conf.js +86 -83
  145. package/src/server/process.js +180 -19
  146. package/src/server/proxy.js +9 -2
  147. package/src/server/runtime.js +1 -1
  148. package/src/server/start.js +17 -5
  149. package/src/server/valkey.js +2 -0
  150. package/src/ws/IoInterface.js +16 -16
  151. package/src/ws/core/channels/core.ws.chat.js +11 -11
  152. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  153. package/src/ws/core/channels/core.ws.stream.js +19 -19
  154. package/src/ws/core/core.ws.connection.js +8 -8
  155. package/src/ws/core/core.ws.server.js +6 -5
  156. package/src/ws/default/channels/default.ws.main.js +10 -10
  157. package/src/ws/default/default.ws.connection.js +4 -4
  158. package/src/ws/default/default.ws.server.js +4 -3
  159. package/bin/file.js +0 -202
  160. package/bin/vs.js +0 -74
  161. package/bin/zed.js +0 -84
  162. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +0 -574
  163. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +0 -467
  164. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  165. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  166. package/src/client/ssr/pages/CyberiaServerMetrics.js +0 -461
  167. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  168. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  169. /package/src/client/ssr/{pages → views}/Test.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import mongoose from 'mongoose';
2
- import { loggerFactory } from '../../server/logger.js';
3
2
  import { getCapVariableName } from '../../client/components/core/CommonJs.js';
3
+ import { loggerFactory } from '../../server/logger.js';
4
4
 
5
5
  /**
6
6
  * Module for connecting to and loading models for a MongoDB database using Mongoose.
@@ -10,6 +10,32 @@ import { getCapVariableName } from '../../client/components/core/CommonJs.js';
10
10
 
11
11
  const logger = loggerFactory(import.meta);
12
12
 
13
+ const MONGODB_SERVICE_NAME = 'mongodb-service';
14
+ const MONGODB_STATEFULSET_NAME = 'mongodb';
15
+ const MONGODB_DEFAULT_AUTH_SOURCE = 'admin';
16
+ const MONGODB_DEFAULT_REPLICA_SET = 'rs0';
17
+ const MONGODB_DEFAULT_REPLICA_COUNT = 3;
18
+
19
+ /**
20
+ * Resolves MongoDB replica hosts from explicit input or StatefulSet defaults.
21
+ * @param {{hostList?: string, replicaCount?: number}} [options] - Host resolution options.
22
+ * @returns {Array<string>} Normalized host:port entries.
23
+ */
24
+ const resolveMongoReplicaHosts = ({ hostList = '', replicaCount = MONGODB_DEFAULT_REPLICA_COUNT }) => {
25
+ if (hostList) {
26
+ return hostList
27
+ .split(',')
28
+ .map((host) => host.trim())
29
+ .filter(Boolean)
30
+ .map((host) => (host.includes(':') ? host : `${host}:27017`));
31
+ }
32
+
33
+ return Array.from(
34
+ { length: replicaCount },
35
+ (_, index) => `${MONGODB_STATEFULSET_NAME}-${index}.${MONGODB_SERVICE_NAME}:27017`,
36
+ );
37
+ };
38
+
13
39
  /**
14
40
  * @class
15
41
  * @alias MongooseDBService
@@ -23,35 +49,109 @@ const logger = loggerFactory(import.meta);
23
49
  * 3. No built-in defaults — both `host` and `name` are required from the caller or environment.
24
50
  */
25
51
  class MongooseDBService {
52
+ /**
53
+ * Normalizes Mongo host inputs into plain host:port entries.
54
+ * @param {Array<string>|string} hosts - Host input as list or comma-separated string.
55
+ * @returns {Array<string>} Normalized host:port entries.
56
+ */
57
+ normalizeHosts(hosts) {
58
+ const hostEntries = Array.isArray(hosts) ? hosts : `${hosts || ''}`.split(',');
59
+
60
+ return hostEntries
61
+ .map((entry) => `${entry || ''}`.trim())
62
+ .filter(Boolean)
63
+ .map((entry) => entry.replace(/^mongodb:\/\//, '').replace(/\/.*$/, ''));
64
+ }
65
+
66
+ /**
67
+ * Normalizes connection config from object or legacy host/name signature.
68
+ * @param {object|string} configOrHost - Connection config object or host string.
69
+ * @param {string} [name] - Legacy DB name when using host string input.
70
+ * @returns {{authSource: string, dbName: string, directConnection: boolean, hosts: Array<string>, password: string, replicaSet: string, user: string}} Normalized config.
71
+ */
72
+ normalizeConfig(configOrHost, name) {
73
+ const config =
74
+ typeof configOrHost === 'object' && configOrHost !== null ? { ...configOrHost } : { host: configOrHost, name };
75
+
76
+ const rawHosts = config.host || process.env.DB_HOST;
77
+ const hosts = this.normalizeHosts(rawHosts);
78
+ const dbName = config.name || process.env.DB_NAME;
79
+
80
+ if (!hosts.length || !dbName) {
81
+ const missing = [!hosts.length && 'host (db.host|DB_HOST)', !dbName && 'name (db.name|DB_NAME)']
82
+ .filter(Boolean)
83
+ .join(', ');
84
+ throw new Error(`MongooseDBService.connect: missing required parameter(s): ${missing}`);
85
+ }
86
+
87
+ const user = config.user || process.env.DB_USER || '';
88
+ const password = config.password || process.env.DB_PASSWORD || '';
89
+ const directConnection = hosts.length === 1;
90
+ const replicaSet = directConnection
91
+ ? ''
92
+ : config.replicaSet || process.env.DB_REPLICA_SET || MONGODB_DEFAULT_REPLICA_SET;
93
+ const authSource = config.authSource || process.env.DB_AUTH_SOURCE || (user ? MONGODB_DEFAULT_AUTH_SOURCE : '');
94
+
95
+ return {
96
+ authSource,
97
+ dbName,
98
+ directConnection,
99
+ hosts,
100
+ password,
101
+ replicaSet,
102
+ user,
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Builds a MongoDB URI from normalized config options.
108
+ * @param {object|string} configOrHost - Connection config object or host string.
109
+ * @param {string} [name] - Legacy DB name when using host string input.
110
+ * @returns {string} MongoDB connection URI.
111
+ */
112
+ buildUri(configOrHost, name) {
113
+ const config = this.normalizeConfig(configOrHost, name);
114
+ const credentials =
115
+ config.user && config.password
116
+ ? `${encodeURIComponent(config.user)}:${encodeURIComponent(config.password)}@`
117
+ : '';
118
+ const query = new URLSearchParams();
119
+
120
+ if (config.directConnection) query.set('directConnection', 'true');
121
+ else if (config.replicaSet) query.set('replicaSet', config.replicaSet);
122
+ if (config.authSource) query.set('authSource', config.authSource);
123
+
124
+ return `mongodb://${credentials}${config.hosts.join(',')}/${config.dbName}${query.size ? `?${query.toString()}` : ''}`;
125
+ }
126
+
26
127
  /**
27
128
  * Establishes a Mongoose connection to the specified MongoDB instance.
28
129
  *
29
130
  * @async
30
- * @param {string} host - The MongoDB host URI (e.g., `'mongodb://localhost:27017'`).
31
- * Falls back to `process.env.DB_HOST` when not provided.
32
- * @param {string} name - The database name.
33
- * Falls back to `process.env.DB_NAME` when not provided.
131
+ * @param {object|string} configOrHost - Either a db config object or a legacy host string.
132
+ * @param {string} [configOrHost.host] - Legacy single host or comma-separated host list.
133
+ * @param {string} [configOrHost.name] - The database name.
134
+ * @param {string} [configOrHost.replicaSet] - The MongoDB replica set name.
135
+ * @param {string} [configOrHost.authSource] - The authentication database.
136
+ * @param {string} [configOrHost.user] - The MongoDB username.
137
+ * @param {string} [configOrHost.password] - The MongoDB password.
138
+ * @param {string} [name] - Legacy database name when a host string is passed.
34
139
  * @returns {Promise<mongoose.Connection>} A promise that resolves to the established Mongoose connection object.
35
140
  * @throws {Error} If neither the argument nor the corresponding environment variable supplies a value.
36
141
  */
37
- async connect(host, name) {
38
- host = host || process.env.DB_HOST;
39
- name = name || process.env.DB_NAME;
40
-
41
- if (!host || !name) {
42
- const missing = [!host && 'host (DB_HOST)', !name && 'name (DB_NAME)'].filter(Boolean).join(', ');
43
- throw new Error(`MongooseDBService.connect: missing required parameter(s): ${missing}`);
44
- }
45
-
46
- const uri = `${host}/${name}`;
47
- // logger.info('MongooseDB connect', { host, name, uri });
142
+ async connect(configOrHost, name) {
143
+ const uri = this.buildUri(configOrHost, name);
144
+ if (process.env.NODE_ENV === 'development') logger.info(`Connecting to MongoDB with URI`, uri);
48
145
  return await mongoose
49
146
  .createConnection(uri, {
147
+ autoIndex: process.env.NODE_ENV !== 'production',
148
+ heartbeatFrequencyMS: 10000,
149
+ maxPoolSize: 20,
150
+ minPoolSize: 2,
151
+ retryReads: true,
152
+ retryWrites: true,
50
153
  serverSelectionTimeoutMS: 5000,
51
- // readPreference: 'primary',
52
- // directConnection: true,
53
- // useNewUrlParser: true,
54
- // useUnifiedTopology: true,
154
+ socketTimeoutMS: 45000,
55
155
  })
56
156
  .asPromise();
57
157
  }
@@ -87,4 +187,12 @@ class MongooseDBService {
87
187
  */
88
188
  const MongooseDB = new MongooseDBService();
89
189
 
90
- export { MongooseDB, MongooseDBService as MongooseDBClass };
190
+ export {
191
+ MongooseDB,
192
+ MongooseDBService as MongooseDBClass,
193
+ MONGODB_DEFAULT_REPLICA_COUNT,
194
+ MONGODB_DEFAULT_REPLICA_SET,
195
+ MONGODB_SERVICE_NAME,
196
+ MONGODB_STATEFULSET_NAME,
197
+ resolveMongoReplicaHosts,
198
+ };
@@ -13,13 +13,15 @@ import * as protoLoader from '@grpc/proto-loader';
13
13
  import crypto from 'crypto';
14
14
  import path from 'path';
15
15
  import { fileURLToPath } from 'url';
16
- import { DataBaseProvider } from '../../db/DataBaseProvider.js';
16
+ import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
17
17
  import { loggerFactory } from '../../server/logger.js';
18
18
  import {
19
19
  CYBERIA_INSTANCE_CONF_DEFAULTS as FALLBACK_CONFIG_DEFAULTS,
20
20
  ENTITY_TYPE_DEFAULTS,
21
- STATUS_ICONS,
22
- } from '../../api/cyberia-instance-conf/cyberia-instance-conf.defaults.js';
21
+ // STATUS_ICONS deliberately not imported here — see toInstanceConfig.
22
+ // Server simulation only cares about the numeric u8 IDs (which travel on
23
+ // the AOI wire). Icon stems + border colours live in SharedDefaultsCyberia.
24
+ } from '../../api/cyberia-server-defaults/cyberia-server-defaults.js';
23
25
  import { generateFallbackWorld } from '../../api/cyberia-instance/cyberia-fallback-world.js';
24
26
 
25
27
  const logger = loggerFactory(import.meta);
@@ -27,7 +29,7 @@ const logger = loggerFactory(import.meta);
27
29
  const __filename = fileURLToPath(import.meta.url);
28
30
  const __dirname = path.dirname(__filename);
29
31
 
30
- const PROTO_PATH = path.resolve(__dirname, '../../../cyberia-server/proto/cyberia.proto');
32
+ const PROTO_PATH = path.resolve(__dirname, '../../../cyberia-server/gen/proto/cyberia.proto');
31
33
 
32
34
  const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
33
35
  keepCase: false,
@@ -44,11 +46,11 @@ const proto = grpc.loadPackageDefinition(packageDefinition).cyberia;
44
46
  // ═══════════════════════════════════════════════════════════════════
45
47
 
46
48
  function getModels(dbKey) {
47
- const bucket = DataBaseProvider.instance[dbKey];
48
- if (!bucket || !bucket.mongoose || !bucket.mongoose.models) {
49
- throw new Error(`DataBaseProvider not loaded for key "${dbKey}"`);
49
+ const bucket = DataBaseProviderService.getProvider(dbKey, 'mongoose');
50
+ if (!bucket || !bucket.models) {
51
+ throw new Error(`DataBaseProviderService not loaded for key "${dbKey}"`);
50
52
  }
51
- return bucket.mongoose.models;
53
+ return bucket.models;
52
54
  }
53
55
 
54
56
  function countSharedItemIds(source = [], target = []) {
@@ -72,7 +74,6 @@ function normalizeEntityDefault(entityDefault = {}, canonical = {}) {
72
74
  liveItemIds: [...(entityDefault.liveItemIds ?? canonical.liveItemIds ?? [])],
73
75
  deadItemIds: [...(entityDefault.deadItemIds ?? canonical.deadItemIds ?? [])],
74
76
  dropItemIds: [...(entityDefault.dropItemIds ?? canonical.dropItemIds ?? [])],
75
- colorKey: entityDefault.colorKey ?? canonical.colorKey ?? '',
76
77
  defaultObjectLayers: defaultObjectLayers.map((ol) => ({
77
78
  itemId: ol.itemId || '',
78
79
  active: !!ol.active,
@@ -96,10 +97,6 @@ function selectCanonicalEntityDefaultIndex(entityDefault, canonicalDefaults, use
96
97
  if (firstSameTypeIndex === -1) {
97
98
  firstSameTypeIndex = index;
98
99
  }
99
- if (entityDefault.colorKey && canonical.colorKey === entityDefault.colorKey) {
100
- return index;
101
- }
102
-
103
100
  const liveOverlap = countSharedItemIds(entityDefault.liveItemIds, canonical.liveItemIds);
104
101
  if (
105
102
  liveOverlap > 0 &&
@@ -264,36 +261,24 @@ function toInstanceConfig(gc) {
264
261
  const fb = FALLBACK_CONFIG_DEFAULTS;
265
262
  if (!gc) return buildFallbackConfig();
266
263
 
267
- // Per-key merge: start with canonical defaults, overlay any DB-defined colours.
268
- const dbColorMap = new Map((gc.colors || []).map((c) => [c.key, c]));
269
- const colors = fb.colors.map((c) => {
270
- const ov = dbColorMap.get(c.key);
271
- return ov ? { key: c.key, r: ov.r ?? c.r, g: ov.g ?? c.g, b: ov.b ?? c.b, a: ov.a ?? c.a } : { ...c };
272
- });
273
- // Append any DB colours whose keys are absent from the canonical defaults.
274
- for (const [key, c] of dbColorMap) {
275
- if (!colors.some((fc) => fc.key === key)) {
276
- colors.push({ key, r: c.r ?? 0, g: c.g ?? 0, b: c.b ?? 0, a: c.a ?? 255 });
277
- }
278
- }
264
+ // STRICT BOUNDARY this function produces the *simulation* config for
265
+ // cyberia-server only. Every presentation concern is excluded:
266
+ //
267
+ // - palette (colors), camera tunings, screen factors, devUi,
268
+ // interpolationMs, status-icon visuals, entityDefaults[].colorKey,
269
+ // cellSize, defaultObj* — all of these reach the client through
270
+ // /api/cyberia-client-hints, never through gRPC.
271
+ //
272
+ // The Go simulation does not need any of them to advance world state;
273
+ // the C/WASM cyberia-client owns its own render policy. See
274
+ // src/client/components/cyberia/SharedDefaultsCyberia.js.
279
275
 
280
- // Merge entity defaults while preserving duplicate builds (for example,
281
- // multiple resource or portal variants sharing the same entityType).
282
276
  const gcDefaults = gc.entityDefaults && gc.entityDefaults.length > 0 ? gc.entityDefaults : [];
283
277
  const entityDefaults = mergeEntityDefaults(gcDefaults);
284
278
 
285
279
  return {
286
- cellSize: gc.cellSize ?? fb.cellSize,
287
- fps: gc.fps ?? fb.fps,
288
- interpolationMs: gc.interpolationMs ?? fb.interpolationMs,
289
- defaultObjWidth: gc.defaultObjWidth ?? fb.defaultObjWidth,
290
- defaultObjHeight: gc.defaultObjHeight ?? fb.defaultObjHeight,
291
- cameraSmoothing: gc.cameraSmoothing ?? fb.cameraSmoothing,
292
- cameraZoom: gc.cameraZoom ?? fb.cameraZoom,
293
- defaultWidthScreenFactor: gc.defaultWidthScreenFactor ?? fb.defaultWidthScreenFactor,
294
- defaultHeightScreenFactor: gc.defaultHeightScreenFactor ?? fb.defaultHeightScreenFactor,
295
- devUi: gc.devUi ?? fb.devUi,
296
- colors,
280
+ tickRate: gc.tickRate ?? fb.tickRate,
281
+ snapshotRate: gc.snapshotRate ?? fb.snapshotRate,
297
282
  aoiRadius: gc.aoiRadius ?? fb.aoiRadius,
298
283
  portalHoldTimeMs: gc.portalHoldTimeMs ?? fb.portalHoldTimeMs,
299
284
  portalSpawnRadius: gc.portalSpawnRadius ?? fb.portalSpawnRadius,
@@ -352,23 +337,6 @@ function toInstanceConfig(gc) {
352
337
  onePerType: gc.equipmentRules?.onePerType ?? fb.equipmentRules.onePerType,
353
338
  requireSkin: gc.equipmentRules?.requireSkin ?? fb.equipmentRules.requireSkin,
354
339
  },
355
- // Status icon mapping — u8 ID → icon filename stem + border colour.
356
- // Border colours come from the frozen STATUS_ICONS constant (canonical
357
- // source of truth). DB entries may override iconId but borderColor is
358
- // always canonical — the DB schema defaults are generic grey, not the
359
- // actual per-status colours.
360
- statusIcons: STATUS_ICONS.map((canon) => {
361
- const dbEntry = (gc.statusIcons || []).find((s) => s.id === canon.id);
362
- const bc = canon.borderColor || {};
363
- return {
364
- id: canon.id,
365
- iconId: (dbEntry && dbEntry.iconId) || canon.iconId || '',
366
- borderColorR: bc.r ?? 100,
367
- borderColorG: bc.g ?? 100,
368
- borderColorB: bc.b ?? 100,
369
- borderColorA: bc.a ?? 200,
370
- };
371
- }),
372
340
  };
373
341
  }
374
342
 
@@ -598,8 +566,8 @@ class GrpcServer {
598
566
 
599
567
  /**
600
568
  * @param {Object} opts
601
- * @param {string} opts.host - DataBaseProvider host key
602
- * @param {string} opts.path - DataBaseProvider path key
569
+ * @param {string} opts.host - DataBaseProviderService host key
570
+ * @param {string} opts.path - DataBaseProviderService path key
603
571
  * @param {number} [opts.port=50051]
604
572
  */
605
573
  static async start({ host, path: dbPath, port = 50051 } = {}) {
package/src/index.js CHANGED
@@ -44,7 +44,7 @@ class Underpost {
44
44
  * @type {String}
45
45
  * @memberof Underpost
46
46
  */
47
- static version = 'v3.2.9';
47
+ static version = 'v3.2.12';
48
48
 
49
49
  /**
50
50
  * Required Node.js major version
@@ -53,19 +53,37 @@ RUN ./emsdk install ${EMSDK_VERSION} && \
53
53
  ./emsdk activate ${EMSDK_VERSION}
54
54
 
55
55
  WORKDIR /cyberia-client
56
- COPY cyberia-client/ .
56
+ # Build context is the cyberia-client project repo root (the workflow
57
+ # in .github/workflows/docker-image.cyberia-client.ci.yml sets
58
+ # context: . from the cyberia-client checkout). The legacy
59
+ # engine-context layout (COPY cyberia-client/ .) is no longer used.
60
+ COPY . .
57
61
 
58
- RUN make -f Web.mk all BUILD_MODE=${BUILD_MODE} OUTPUT_DIR=bin/
62
+ # `make clean` first so stale bin/ or build/ artefacts copied in from a
63
+ # developer's local checkout (or a previous Docker layer cache hit)
64
+ # can't shadow a fresh BUILD_MODE rebuild — e.g. mixing a DEBUG-mode
65
+ # index.wasm with a RELEASE-mode index.js once produced the
66
+ # "corrupted heap memory area" panic that masked the real Closure bug.
67
+ RUN make -f Web.mk clean && make -f Web.mk all BUILD_MODE=${BUILD_MODE} OUTPUT_DIR=bin/
59
68
 
60
69
  # --- Runtime Image
61
70
  FROM rockylinux/rockylinux:9 AS runtime
62
71
 
72
+ # Runtime needs:
73
+ # - python3 (serves the built /bin/ static files via server.py)
74
+ # - nodejs + the underpost CLI globally, for the container-status
75
+ # lifecycle hooks invoked from conf.instances.json before / after
76
+ # launching server.py. Installing it at build time avoids the slow
77
+ # `npm install -g` startup the K8S cmd would otherwise repeat.
78
+ ARG UNDERPOST_VERSION=3.2.9
63
79
  RUN dnf -y update && \
64
80
  dnf -y install epel-release && \
65
81
  dnf -y install --allowerasing curl git python3 && \
66
82
  curl -fsSL https://rpm.nodesource.com/setup_24.x | bash - && \
67
83
  dnf install -y nodejs && \
68
- dnf clean all
84
+ npm install -g underpost@${UNDERPOST_VERSION} && \
85
+ dnf clean all && \
86
+ npm cache clean --force
69
87
 
70
88
  WORKDIR /home/dd/engine/cyberia-client
71
89
 
@@ -77,4 +95,7 @@ ENV CYBERIA_MODE=production
77
95
 
78
96
  EXPOSE 8081 8082
79
97
 
98
+ # Default CMD when the image is run directly (not via K8S cmd).
99
+ # In K8S deploys conf.instances.json supplies its own cmd that wraps
100
+ # container-status hooks + this same server.py invocation.
80
101
  CMD ["sh", "-c", "exec python3 server.py ${CYBERIA_PORT} bin ${CYBERIA_MODE}"]
@@ -0,0 +1,82 @@
1
+ # cyberia-client DEV runtime image.
2
+ #
3
+ # Differences vs the production Dockerfile in this same directory:
4
+ # - Builds the C/WASM bundle with BUILD_MODE=DEBUG (raylib + emcc keep
5
+ # symbols, asserts, and DWARF info; the bundle is larger but
6
+ # debuggable in the browser devtools).
7
+ # - Default CYBERIA_MODE is `development` instead of `production`.
8
+ # - Default CYBERIA_PORT is `8082` (debug port) instead of `8081`.
9
+ # - Keeps `vim-minimal`, `lsof`, `strace`, `procps-ng` in the runtime
10
+ # image so the operator can attach and inspect a misbehaving pod.
11
+ # - Does NOT clean the npm cache.
12
+ #
13
+ # Selected automatically by `node bin run instance-build-manifest`
14
+ # whenever the `--dev` flag is set; the production Dockerfile in the
15
+ # same directory is used otherwise.
16
+
17
+ ARG BUILD_MODE=DEBUG
18
+
19
+ # --- Build Image
20
+ FROM rockylinux/rockylinux:9 AS builder
21
+ ARG BUILD_MODE=DEBUG
22
+
23
+ RUN dnf -y update && \
24
+ dnf -y install epel-release && \
25
+ dnf -y install --allowerasing \
26
+ git curl wget openssl-devel libnsl perl gnupg2 bzip2 && \
27
+ dnf clean all
28
+
29
+ RUN dnf groupinstall -y "Development Tools" && \
30
+ dnf install -y \
31
+ cmake unzip python3 python3.11 \
32
+ alsa-lib-devel mesa-libGL-devel mesa-libGLU-devel \
33
+ libX11-devel libXrandr-devel libXi-devel libXcursor-devel \
34
+ libXinerama-devel libXfixes-devel \
35
+ freeglut-devel glfw-devel libatomic.x86_64 && \
36
+ dnf clean all && \
37
+ alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 2 && \
38
+ alternatives --set python3 /usr/bin/python3.11
39
+
40
+ ENV EMSDK=/opt/emsdk
41
+ ENV PATH="${EMSDK}:${EMSDK}/upstream/emscripten:${PATH}"
42
+
43
+ ARG EMSDK_VERSION=5.0.6
44
+ WORKDIR /opt
45
+ RUN git clone https://github.com/emscripten-core/emsdk.git ${EMSDK}
46
+ WORKDIR ${EMSDK}
47
+ RUN ./emsdk install ${EMSDK_VERSION} && \
48
+ ./emsdk activate ${EMSDK_VERSION}
49
+
50
+ WORKDIR /cyberia-client
51
+ # Build context is the cyberia-client project repo root (see the
52
+ # production Dockerfile in this directory for the rationale).
53
+ COPY . .
54
+
55
+ # Dev build: BUILD_MODE=DEBUG keeps symbols, asserts, source maps.
56
+ RUN make -f Web.mk all BUILD_MODE=${BUILD_MODE} OUTPUT_DIR=bin/
57
+
58
+ # --- Runtime Image
59
+ FROM rockylinux/rockylinux:9 AS runtime
60
+
61
+ ARG UNDERPOST_VERSION=3.2.9
62
+ RUN dnf -y update && \
63
+ dnf -y install epel-release && \
64
+ dnf -y install --allowerasing \
65
+ curl git python3 \
66
+ procps-ng strace lsof vim-minimal && \
67
+ curl -fsSL https://rpm.nodesource.com/setup_24.x | bash - && \
68
+ dnf install -y nodejs && \
69
+ npm install -g underpost@${UNDERPOST_VERSION} && \
70
+ dnf clean all
71
+
72
+ WORKDIR /home/dd/engine/cyberia-client
73
+
74
+ COPY --from=builder /cyberia-client/server.py ./server.py
75
+ COPY --from=builder /cyberia-client/bin ./bin/
76
+
77
+ ENV CYBERIA_PORT=8082
78
+ ENV CYBERIA_MODE=development
79
+
80
+ EXPOSE 8081 8082
81
+
82
+ CMD ["sh", "-c", "exec python3 server.py ${CYBERIA_PORT} bin ${CYBERIA_MODE}"]
@@ -11,27 +11,52 @@ RUN dnf -y update && \
11
11
 
12
12
  WORKDIR /build
13
13
 
14
- # Cache dependency downloads independently of source changes
15
- COPY cyberia-server/go.mod cyberia-server/go.sum ./
14
+ # Build context is the cyberia-server project repo root (the workflow
15
+ # in .github/workflows/docker-image.cyberia-server.ci.yml sets
16
+ # context: . from the cyberia-server checkout). The legacy
17
+ # engine-context layout (COPY cyberia-server/ .) is no longer used.
18
+ COPY go.mod go.sum ./
16
19
  RUN go mod download
17
20
 
18
- COPY cyberia-server/ ./
21
+ COPY . ./
19
22
  RUN chmod +x build.sh && ./build.sh
20
23
 
21
24
  # --- Runtime Image
22
25
  FROM rockylinux/rockylinux:9 AS runtime
23
26
 
27
+ # Runtime needs:
28
+ # - the compiled Go binary (cyberia-server itself)
29
+ # - nodejs + the underpost CLI globally, for the container-status
30
+ # lifecycle hooks (`underpost config set container-status ...`) that
31
+ # the conf.instances.json cmd invokes before / after launching the
32
+ # server binary. Installing it at build time avoids the slow
33
+ # `npm install -g` startup the K8S cmd would otherwise have to do
34
+ # on every pod boot.
35
+ ARG UNDERPOST_VERSION=3.2.9
24
36
  RUN dnf -y update && \
25
37
  dnf -y install epel-release && \
26
38
  dnf -y install --allowerasing curl git && \
27
39
  curl -fsSL https://rpm.nodesource.com/setup_24.x | bash - && \
28
40
  dnf install -y nodejs && \
29
- dnf clean all
41
+ npm install -g underpost@${UNDERPOST_VERSION} && \
42
+ dnf clean all && \
43
+ npm cache clean --force
30
44
 
31
45
  WORKDIR /home/dd/engine/cyberia-server
32
46
 
33
47
  COPY --from=builder /build/server ./server
34
48
 
49
+ # Static SSR dashboard rendered by `node bin/cyberia run-workflow
50
+ # build-server-dashboard --output-path <project-root>/public/index.html`
51
+ # from inside the engine repo checkout. The Go server's findPublicDir()
52
+ # hard-requires public/index.html at boot and log.Fatalf's without it.
53
+ # CI writes the dashboard into ./public/ at the cyberia-server repo root
54
+ # before docker build picks it up here.
55
+ COPY public/ ./public/
56
+
35
57
  EXPOSE 8081
36
58
 
59
+ # Default entrypoint when the image is run directly (not via K8S cmd).
60
+ # In K8S deploys conf.instances.json supplies its own cmd that wraps env
61
+ # sourcing + container-status hooks + this same binary.
37
62
  ENTRYPOINT ["/home/dd/engine/cyberia-server/server"]
@@ -0,0 +1,71 @@
1
+ # cyberia-server DEV runtime image.
2
+ #
3
+ # Differences vs the production Dockerfile in this same directory:
4
+ # - Builds the Go binary WITHOUT `-trimpath -ldflags="-s -w"` so symbols
5
+ # and source paths survive into the binary for stack traces and pprof.
6
+ # - Keeps -race (CGO-free race detector is not available; we still pass
7
+ # `-gcflags="all=-N -l"` to disable inlining + optimisations so the
8
+ # dlv debugger and live profilers can attach predictably).
9
+ # - Keeps `git`, `procps-ng`, `strace`, `lsof`, `vim`, `curl` in the
10
+ # runtime image so the operator can attach and inspect a misbehaving
11
+ # pod without re-baking the image. Production keeps the runtime
12
+ # layer minimal.
13
+ # - Does NOT strip the npm cache after `npm install -g underpost@…` so
14
+ # `npm upgrade -g underpost` works in a running container.
15
+ #
16
+ # Selected automatically by `node bin run instance-build-manifest`
17
+ # whenever the `--dev` flag is set; the production Dockerfile in the
18
+ # same directory is used otherwise.
19
+
20
+ # --- Build Image
21
+ FROM rockylinux/rockylinux:9 AS builder
22
+
23
+ RUN dnf -y update && \
24
+ dnf -y install epel-release && \
25
+ dnf -y install --allowerasing \
26
+ git \
27
+ make \
28
+ golang && \
29
+ dnf clean all
30
+
31
+ WORKDIR /build
32
+
33
+ # Build context is the cyberia-server project repo root (see the
34
+ # production Dockerfile in this directory for the rationale).
35
+ COPY go.mod go.sum ./
36
+ RUN go mod download
37
+
38
+ COPY . ./
39
+ # Dev build: keep source paths, disable inlining + optimisations.
40
+ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
41
+ go build -gcflags="all=-N -l" -o server ./cmd/cyberia-server/ && \
42
+ echo "DEV build complete: $(pwd)/server"
43
+
44
+ # --- Runtime Image
45
+ FROM rockylinux/rockylinux:9 AS runtime
46
+
47
+ ARG UNDERPOST_VERSION=3.2.9
48
+ RUN dnf -y update && \
49
+ dnf -y install epel-release && \
50
+ dnf -y install --allowerasing \
51
+ curl git \
52
+ procps-ng strace lsof vim-minimal && \
53
+ curl -fsSL https://rpm.nodesource.com/setup_24.x | bash - && \
54
+ dnf install -y nodejs && \
55
+ npm install -g underpost@${UNDERPOST_VERSION} && \
56
+ dnf clean all
57
+
58
+ WORKDIR /home/dd/engine/cyberia-server
59
+
60
+ COPY --from=builder /build/server ./server
61
+
62
+ # Static SSR dashboard — required by the Go server's findPublicDir() at boot.
63
+ # See the production Dockerfile in this directory for the full rationale.
64
+ # The same `node bin/cyberia run-workflow build-server-dashboard --dev
65
+ # --output-path <project-root>/public/index.html` command is executed by
66
+ # the CI workflow before docker build.
67
+ COPY public/ ./public/
68
+
69
+ EXPOSE 8081
70
+
71
+ ENTRYPOINT ["/home/dd/engine/cyberia-server/server"]
@@ -15,7 +15,7 @@ import { createServer } from 'http';
15
15
  import { loggerFactory, loggerMiddleware } from '../../server/logger.js';
16
16
  import { getCapVariableName, newInstance } from '../../client/components/core/CommonJs.js';
17
17
  import { MailerProvider } from '../../mailer/MailerProvider.js';
18
- import { DataBaseProvider } from '../../db/DataBaseProvider.js';
18
+ import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
19
19
  import { createPeerServer } from '../../server/peer.js';
20
20
  import { createValkeyConnection } from '../../server/valkey.js';
21
21
  import { applySecurity, authMiddlewareFactory } from '../../server/auth.js';
@@ -192,7 +192,7 @@ class ExpressService {
192
192
  }
193
193
 
194
194
  // Database and Valkey connections
195
- if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
195
+ if (db && apis) await DataBaseProviderService.load({ apis, host, path, db });
196
196
 
197
197
  if (valkey) await createValkeyConnection({ host, path }, valkey);
198
198