@open-core/framework 1.0.5-beta.2 → 1.0.6

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 (128) hide show
  1. package/README.md +2 -8
  2. package/dist/adapters/contracts/IEngineEvents.d.ts +3 -3
  3. package/dist/adapters/contracts/IExports.d.ts +2 -2
  4. package/dist/adapters/contracts/client/IClientLocalPlayerBridge.d.ts +21 -0
  5. package/dist/adapters/contracts/client/IClientLocalPlayerBridge.js +6 -0
  6. package/dist/adapters/contracts/client/IClientRuntimeBridge.d.ts +5 -5
  7. package/dist/adapters/contracts/client/camera/IClientCameraPort.d.ts +107 -0
  8. package/dist/adapters/contracts/client/camera/IClientCameraPort.js +8 -0
  9. package/dist/adapters/contracts/client/camera/index.d.ts +1 -0
  10. package/dist/adapters/contracts/client/camera/index.js +1 -0
  11. package/dist/adapters/contracts/client/index.d.ts +4 -0
  12. package/dist/adapters/contracts/client/index.js +4 -0
  13. package/dist/adapters/contracts/client/ped/IClientPedPort.d.ts +62 -0
  14. package/dist/adapters/contracts/client/ped/IClientPedPort.js +5 -0
  15. package/dist/adapters/contracts/client/ped/index.d.ts +1 -0
  16. package/dist/adapters/contracts/client/ped/index.js +1 -0
  17. package/dist/adapters/contracts/client/progress/IClientProgressPort.d.ts +53 -0
  18. package/dist/adapters/contracts/client/progress/IClientProgressPort.js +8 -0
  19. package/dist/adapters/contracts/client/progress/index.d.ts +1 -0
  20. package/dist/adapters/contracts/client/progress/index.js +1 -0
  21. package/dist/adapters/contracts/client/spawn/IClientSpawnBridge.d.ts +5 -6
  22. package/dist/adapters/contracts/client/spawn/IClientSpawnBridge.js +5 -1
  23. package/dist/adapters/contracts/client/spawn/IClientSpawnPort.d.ts +19 -0
  24. package/dist/adapters/contracts/client/spawn/IClientSpawnPort.js +2 -0
  25. package/dist/adapters/contracts/client/spawn/index.d.ts +2 -0
  26. package/dist/adapters/contracts/client/spawn/index.js +2 -0
  27. package/dist/adapters/contracts/client/spawn/types.d.ts +3 -0
  28. package/dist/adapters/contracts/client/ui/webview/IClientWebViewBridge.d.ts +1 -0
  29. package/dist/adapters/contracts/client/ui/webview/types.d.ts +2 -0
  30. package/dist/adapters/contracts/client/vehicle/IClientVehiclePort.d.ts +166 -0
  31. package/dist/adapters/contracts/client/vehicle/IClientVehiclePort.js +8 -0
  32. package/dist/adapters/contracts/client/vehicle/index.d.ts +1 -0
  33. package/dist/adapters/contracts/client/vehicle/index.js +1 -0
  34. package/dist/adapters/contracts/transport/events.api.d.ts +3 -3
  35. package/dist/adapters/contracts/transport/index.d.ts +1 -0
  36. package/dist/adapters/contracts/transport/index.js +1 -0
  37. package/dist/adapters/contracts/transport/rpc-error.d.ts +17 -0
  38. package/dist/adapters/contracts/transport/rpc-error.js +28 -0
  39. package/dist/adapters/contracts/transport/rpc.api.d.ts +3 -3
  40. package/dist/adapters/node/transport/node.events.d.ts +4 -4
  41. package/dist/adapters/node/transport/node.rpc.d.ts +3 -3
  42. package/dist/adapters/node/transport/node.rpc.js +1 -1
  43. package/dist/contracts.d.ts +1 -0
  44. package/dist/contracts.js +1 -0
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.js +1 -0
  47. package/dist/kernel/logger/client-log-console.js +8 -8
  48. package/dist/kernel/logger/index.d.ts +1 -1
  49. package/dist/kernel/logger/index.js +1 -1
  50. package/dist/kernel/logger/logger.types.d.ts +1 -0
  51. package/dist/kernel/logger/logger.types.js +35 -0
  52. package/dist/kernel/logger/transports/buffered.transport.js +4 -4
  53. package/dist/kernel/logger/transports/console.transport.js +2 -2
  54. package/dist/kernel/logger/transports/simple-console.transport.js +2 -2
  55. package/dist/runtime/client/adapter/index.d.ts +5 -0
  56. package/dist/runtime/client/adapter/index.js +5 -0
  57. package/dist/runtime/client/adapter/node-camera-port.d.ts +19 -0
  58. package/dist/runtime/client/adapter/node-camera-port.js +31 -0
  59. package/dist/runtime/client/adapter/node-client-adapter.js +15 -1
  60. package/dist/runtime/client/adapter/node-local-player-bridge.d.ts +3 -0
  61. package/dist/runtime/client/adapter/node-local-player-bridge.js +9 -0
  62. package/dist/runtime/client/adapter/node-log-console.js +8 -8
  63. package/dist/runtime/client/adapter/node-ped-port.d.ts +20 -0
  64. package/dist/runtime/client/adapter/node-ped-port.js +38 -0
  65. package/dist/runtime/client/adapter/node-progress-port.d.ts +8 -0
  66. package/dist/runtime/client/adapter/node-progress-port.js +27 -0
  67. package/dist/runtime/client/adapter/node-runtime-bridge.d.ts +2 -4
  68. package/dist/runtime/client/adapter/node-spawn-bridge.d.ts +5 -5
  69. package/dist/runtime/client/adapter/node-spawn-bridge.js +8 -4
  70. package/dist/runtime/client/adapter/node-vehicle-port.d.ts +31 -0
  71. package/dist/runtime/client/adapter/node-vehicle-port.js +73 -0
  72. package/dist/runtime/client/adapter/node-webview-bridge.d.ts +1 -0
  73. package/dist/runtime/client/adapter/node-webview-bridge.js +2 -0
  74. package/dist/runtime/client/controllers/appearance.controller.d.ts +3 -3
  75. package/dist/runtime/client/controllers/appearance.controller.js +11 -10
  76. package/dist/runtime/client/controllers/spawner.controller.js +4 -3
  77. package/dist/runtime/client/services/camera.d.ts +4 -26
  78. package/dist/runtime/client/services/camera.js +21 -27
  79. package/dist/runtime/client/services/notification.service.d.ts +3 -3
  80. package/dist/runtime/client/services/notification.service.js +7 -7
  81. package/dist/runtime/client/services/ped.service.d.ts +6 -21
  82. package/dist/runtime/client/services/ped.service.js +31 -78
  83. package/dist/runtime/client/services/progress.service.d.ts +4 -50
  84. package/dist/runtime/client/services/progress.service.js +11 -143
  85. package/dist/runtime/client/services/session-bridge.service.js +3 -2
  86. package/dist/runtime/client/services/spawn.service.d.ts +3 -5
  87. package/dist/runtime/client/services/spawn.service.js +12 -17
  88. package/dist/runtime/client/services/vehicle-client.service.d.ts +3 -3
  89. package/dist/runtime/client/services/vehicle-client.service.js +43 -143
  90. package/dist/runtime/client/services/vehicle.service.d.ts +4 -41
  91. package/dist/runtime/client/services/vehicle.service.js +24 -130
  92. package/dist/runtime/client/webview-bridge.d.ts +3 -0
  93. package/dist/runtime/client/webview-bridge.js +6 -0
  94. package/dist/runtime/client/webview.service.d.ts +1 -0
  95. package/dist/runtime/client/webview.service.js +5 -0
  96. package/dist/runtime/server/adapter/node-player-appearance-lifecycle-server.js +3 -2
  97. package/dist/runtime/server/adapter/node-player-lifecycle-server.js +4 -3
  98. package/dist/runtime/server/adapter/node-vehicle-lifecycle-server.js +2 -1
  99. package/dist/runtime/server/apis/chat.api.js +6 -5
  100. package/dist/runtime/server/apis/npcs.api.js +2 -1
  101. package/dist/runtime/server/apis/parallel-compute.api.js +1 -0
  102. package/dist/runtime/server/apis/vehicle-modification.api.js +6 -4
  103. package/dist/runtime/server/apis/vehicles.api.js +7 -4
  104. package/dist/runtime/server/bootstrap.js +13 -12
  105. package/dist/runtime/server/controllers/command-export.controller.js +4 -2
  106. package/dist/runtime/server/controllers/remote-command-execution.controller.js +2 -1
  107. package/dist/runtime/server/controllers/vehicle.controller.js +6 -5
  108. package/dist/runtime/server/decorators/command.d.ts +2 -0
  109. package/dist/runtime/server/decorators/command.js +3 -1
  110. package/dist/runtime/server/entities/npc.d.ts +1 -1
  111. package/dist/runtime/server/entities/player.d.ts +1 -1
  112. package/dist/runtime/server/entities/player.js +10 -3
  113. package/dist/runtime/server/helpers/command-validation.helper.js +20 -7
  114. package/dist/runtime/server/helpers/function-helper.d.ts +1 -0
  115. package/dist/runtime/server/helpers/function-helper.js +15 -8
  116. package/dist/runtime/server/implementations/local/channel.local.d.ts +1 -1
  117. package/dist/runtime/server/implementations/local/channel.local.js +3 -2
  118. package/dist/runtime/server/ports/channel.api-port.d.ts +1 -1
  119. package/dist/runtime/server/services/parallel/worker-pool.d.ts +1 -1
  120. package/dist/runtime/server/services/parallel/worker-pool.js +38 -6
  121. package/dist/runtime/server/services/parallel/worker.js +1 -0
  122. package/dist/runtime/server/system/processors/onRpc.processor.js +14 -3
  123. package/dist/runtime/server/system/schema-generator.d.ts +1 -1
  124. package/dist/runtime/server/system/schema-generator.js +6 -3
  125. package/dist/runtime/shared/helpers/process-tuple-schema.js +3 -0
  126. package/dist/runtime/shared/types/system-types.d.ts +55 -0
  127. package/dist/runtime/shared/types/system-types.js +54 -0
  128. package/package.json +21 -11
@@ -12,6 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  };
13
13
  import { inject } from 'tsyringe';
14
14
  import { EventsAPI } from '../../../adapters/contracts/transport/events.api';
15
+ import { SYSTEM_EVENTS } from '../../shared/types/system-types';
15
16
  import { Controller, OnNet } from '../decorators';
16
17
  import { Player } from '../entities/player';
17
18
  import { Vehicles } from '../apis/vehicles.api';
@@ -34,11 +35,11 @@ let VehicleController = class VehicleController {
34
35
  handleGetData(player, networkId) {
35
36
  const vehicle = this.vehicleService.getByNetworkId(networkId);
36
37
  if (!vehicle) {
37
- this.events.emit('opencore:vehicle:dataResult', player.clientID, null);
38
+ this.events.emit(SYSTEM_EVENTS.vehicle.dataResult, player.clientID, null);
38
39
  return null;
39
40
  }
40
41
  const data = vehicle.serialize();
41
- this.events.emit('opencore:vehicle:dataResult', player.clientID, data);
42
+ this.events.emit(SYSTEM_EVENTS.vehicle.dataResult, player.clientID, data);
42
43
  return data;
43
44
  }
44
45
  /**
@@ -47,18 +48,18 @@ let VehicleController = class VehicleController {
47
48
  handleGetPlayerVehicles(player) {
48
49
  const vehicles = this.vehicleService.getPlayerVehicles(player.clientID);
49
50
  const serialized = vehicles.map((v) => v.serialize());
50
- this.events.emit('opencore:vehicle:playerVehiclesResult', player.clientID, serialized);
51
+ this.events.emit(SYSTEM_EVENTS.vehicle.playerVehiclesResult, player.clientID, serialized);
51
52
  return serialized;
52
53
  }
53
54
  };
54
55
  __decorate([
55
- OnNet('opencore:vehicle:getData'),
56
+ OnNet(SYSTEM_EVENTS.vehicle.getData),
56
57
  __metadata("design:type", Function),
57
58
  __metadata("design:paramtypes", [Player, Number]),
58
59
  __metadata("design:returntype", void 0)
59
60
  ], VehicleController.prototype, "handleGetData", null);
60
61
  __decorate([
61
- OnNet('opencore:vehicle:getPlayerVehicles'),
62
+ OnNet(SYSTEM_EVENTS.vehicle.getPlayerVehicles),
62
63
  __metadata("design:type", Function),
63
64
  __metadata("design:paramtypes", [Player]),
64
65
  __metadata("design:returntype", void 0)
@@ -36,6 +36,8 @@ export interface CommandMetadata extends CommandConfig {
36
36
  security?: SecurityMetadata;
37
37
  /** True if the last parameter uses the spread operator (...args) */
38
38
  hasSpreadParam?: boolean;
39
+ /** True when a parameter defines a JS default value. */
40
+ defaultParams?: boolean[];
39
41
  }
40
42
  type ServerCommandHandler = (() => any) | ((player: Player, ...args: any[]) => any);
41
43
  /**
@@ -1,5 +1,5 @@
1
1
  import { Player } from '../entities/player';
2
- import { getParameterNames, getSpreadParameterIndices } from '../helpers/function-helper';
2
+ import { getDefaultParameterIndices, getParameterNames, getSpreadParameterIndices, } from '../helpers/function-helper';
3
3
  import { METADATA_KEYS } from '../system/metadata-server.keys';
4
4
  // Implementation
5
5
  export function Command(configOrName, schema) {
@@ -20,6 +20,7 @@ export function Command(configOrName, schema) {
20
20
  throw new Error(`@Command '${config.command}': first parameter must be Player if parameters are present`);
21
21
  }
22
22
  const paramNames = getParameterNames(descriptor.value);
23
+ const defaultParams = getDefaultParameterIndices(descriptor.value);
23
24
  const spreadIndices = getSpreadParameterIndices(descriptor.value);
24
25
  const hasSpreadParam = spreadIndices.length > 0 && spreadIndices[spreadIndices.length - 1];
25
26
  const metadata = {
@@ -30,6 +31,7 @@ export function Command(configOrName, schema) {
30
31
  paramNames,
31
32
  expectsPlayer,
32
33
  hasSpreadParam,
34
+ defaultParams,
33
35
  };
34
36
  Reflect.defineMetadata(METADATA_KEYS.COMMAND, metadata, target, propertyKey);
35
37
  };
@@ -1,4 +1,3 @@
1
- import { NativeHandle } from 'src/runtime/core/nativehandle';
2
1
  import { IEntityServer } from '../../../adapters/contracts/server/IEntityServer';
3
2
  import { INpcLifecycleServer } from '../../../adapters/contracts/server/npc-lifecycle/INpcLifecycleServer';
4
3
  import { IPedServer } from '../../../adapters/contracts/server/IPedServer';
@@ -6,6 +5,7 @@ import { Vector3 } from '../../../kernel/utils/vector3';
6
5
  import { BaseEntity } from '../../core/entity';
7
6
  import { Spatial } from '../../core/spatial';
8
7
  import { SerializedNpcData } from '../types/npc.types';
8
+ import { NativeHandle } from '../../core';
9
9
  export interface NpcAdapters {
10
10
  entityServer: IEntityServer;
11
11
  pedServer: IPedServer;
@@ -11,7 +11,7 @@ import { Spatial } from '../../core/spatial';
11
11
  import { LinkedID } from '../types/linked-id';
12
12
  import { PlayerSession } from '../types/player-session.types';
13
13
  import { SerializedPlayerData } from '../types/core-exports.types';
14
- import { NativeHandle } from 'src/runtime/core/nativehandle';
14
+ import { NativeHandle } from '../../core';
15
15
  /**
16
16
  * Adapter bundle for player operations.
17
17
  * Passed to Player instances by PlayerDirectory.
@@ -1,5 +1,6 @@
1
1
  import { loggers } from '../../../kernel/logger';
2
2
  import { BaseEntity } from '../../core/entity';
3
+ import { SYSTEM_EVENTS } from '../../shared/types/system-types';
3
4
  /**
4
5
  * Core-level representation of a connected player on the server.
5
6
  *
@@ -30,10 +31,16 @@ export class Player extends BaseEntity {
30
31
  this._position = adapters.playerInfo.getPlayerPosition(session.clientID);
31
32
  }
32
33
  getHeading() {
33
- return this.adapters.entityServer.getHeading(this.clientID);
34
+ const ped = this.adapters.playerServer.getPed(this.clientID.toString());
35
+ if (!ped || ped === 0)
36
+ return 0;
37
+ return this.adapters.entityServer.getHeading(ped);
34
38
  }
35
39
  setHeading(heading) {
36
- this.adapters.entityServer.setHeading(this.clientID, heading);
40
+ const ped = this.adapters.playerServer.getPed(this.clientID.toString());
41
+ if (!ped || ped === 0)
42
+ return;
43
+ this.adapters.entityServer.setHeading(ped, heading);
37
44
  }
38
45
  getHandle() {
39
46
  return this.clientID;
@@ -120,7 +127,7 @@ export class Player extends BaseEntity {
120
127
  * @param type - Message type for styling
121
128
  */
122
129
  send(message, type = 'chat') {
123
- this.emit('core:chat:send', message, type);
130
+ this.emit(SYSTEM_EVENTS.chat.send, message, type);
124
131
  }
125
132
  // ─────────────────────────────────────────────────────────────────
126
133
  // Spawning / Teleporting
@@ -4,17 +4,21 @@ import { generateSchemaFromTypes } from '../system/schema-generator';
4
4
  import { processTupleSchema } from '../../shared/helpers/process-tuple-schema';
5
5
  export async function validateAndExecuteCommand(meta, player, args, handler) {
6
6
  const paramNames = meta.expectsPlayer ? meta.paramNames.slice(1) : meta.paramNames;
7
+ const defaultParams = meta.expectsPlayer
8
+ ? (meta.defaultParams ?? []).slice(1)
9
+ : (meta.defaultParams ?? []);
7
10
  let schema = meta.schema;
8
11
  if (!meta.expectsPlayer) {
9
12
  if (args.length > 0) {
10
- throw new AppError('GAME:BAD_REQUEST', `Incorrect usage, use: ${meta.usage}`, 'client', {
11
- usage: meta.usage,
13
+ const usage = resolveCommandUsage(meta, paramNames, defaultParams);
14
+ throw new AppError('GAME:BAD_REQUEST', `Incorrect usage, use: ${usage}`, 'client', {
15
+ usage,
12
16
  });
13
17
  }
14
18
  return await handler();
15
19
  }
16
20
  if (!schema) {
17
- schema = generateSchemaFromTypes(meta.paramTypes);
21
+ schema = generateSchemaFromTypes(meta.paramTypes, meta.defaultParams);
18
22
  if (!schema) {
19
23
  if (paramNames.length > 0) {
20
24
  throw new AppError('SCHEMA:MISMATCH', `Command '${meta.command}' has parameters ${paramNames.join(', ')} but no schema was provided.`, 'core');
@@ -39,8 +43,9 @@ export async function validateAndExecuteCommand(meta, player, args, handler) {
39
43
  inputObj[paramNames[i]] = args[i];
40
44
  }
41
45
  const validated = await schema.parseAsync(inputObj).catch(() => {
42
- throw new AppError('GAME:BAD_REQUEST', `Incorrect usage, use: ${meta.usage}`, 'client', {
43
- usage: meta.usage,
46
+ const usage = resolveCommandUsage(meta, paramNames, defaultParams);
47
+ throw new AppError('GAME:BAD_REQUEST', `Incorrect usage, use: ${usage}`, 'client', {
48
+ usage,
44
49
  });
45
50
  });
46
51
  const obj = validated;
@@ -51,8 +56,9 @@ export async function validateAndExecuteCommand(meta, player, args, handler) {
51
56
  if (schema instanceof z.ZodTuple) {
52
57
  const processedArgs = processTupleSchema(schema, args);
53
58
  const validated = await schema.parseAsync(processedArgs).catch(() => {
54
- throw new AppError('GAME:BAD_REQUEST', `Incorrect usage, use: ${meta.usage}`, 'client', {
55
- usage: meta.usage,
59
+ const usage = resolveCommandUsage(meta, paramNames, defaultParams);
60
+ throw new AppError('GAME:BAD_REQUEST', `Incorrect usage, use: ${usage}`, 'client', {
61
+ usage,
56
62
  });
57
63
  });
58
64
  const finalArgs = validated;
@@ -71,3 +77,10 @@ export async function validateAndExecuteCommand(meta, player, args, handler) {
71
77
  // fallback
72
78
  return await handler(player);
73
79
  }
80
+ function resolveCommandUsage(meta, paramNames, defaultParams) {
81
+ if (meta.usage?.trim()) {
82
+ return meta.usage;
83
+ }
84
+ const renderedParams = paramNames.map((name, index) => defaultParams[index] ? `[${name}]` : `<${name}>`);
85
+ return `/${meta.command}${renderedParams.length > 0 ? ` ${renderedParams.join(' ')}` : ''}`;
86
+ }
@@ -1,4 +1,5 @@
1
1
  export declare function getParameterNames(func: (...args: any[]) => any): string[];
2
+ export declare function getDefaultParameterIndices(func: (...args: any[]) => any): boolean[];
2
3
  /**
3
4
  * Detects which parameter indices use the spread operator (...args).
4
5
  * Returns an array of booleans where true means the parameter at that index is a spread parameter.
@@ -1,14 +1,10 @@
1
1
  export function getParameterNames(func) {
2
- const stripped = func
3
- .toString()
4
- .replace(/\/\/.*$/gm, '')
5
- .replace(/\/\*[\s\S]*?\*\//gm, '');
6
- const args = stripped
7
- .slice(stripped.indexOf('(') + 1, stripped.indexOf(')'))
8
- .split(',')
2
+ return parseParameterTokens(func)
9
3
  .map((arg) => arg.replace(/=[\s\S]*/, '').trim())
10
4
  .filter(Boolean);
11
- return args;
5
+ }
6
+ export function getDefaultParameterIndices(func) {
7
+ return parseParameterTokens(func).map((arg) => arg.includes('='));
12
8
  }
13
9
  /**
14
10
  * Detects which parameter indices use the spread operator (...args).
@@ -26,3 +22,14 @@ export function getSpreadParameterIndices(func) {
26
22
  .filter(Boolean);
27
23
  return args.map((arg) => arg.startsWith('...'));
28
24
  }
25
+ function parseParameterTokens(func) {
26
+ const stripped = func
27
+ .toString()
28
+ .replace(/\/\/.*$/gm, '')
29
+ .replace(/\/\*[\s\S]*?\*\//gm, '');
30
+ return stripped
31
+ .slice(stripped.indexOf('(') + 1, stripped.indexOf(')'))
32
+ .split(',')
33
+ .map((arg) => arg.trim())
34
+ .filter(Boolean);
35
+ }
@@ -1,5 +1,5 @@
1
1
  import { EventsAPI } from '../../../../adapters/contracts/transport/events.api';
2
- import { RGB } from 'src/kernel';
2
+ import { RGB } from '../../../../kernel/utils';
3
3
  import { Player } from '../../entities';
4
4
  import { Channels } from '../../ports/channel.api-port';
5
5
  import { IChannelValidator, ChannelMetadata, ChannelType } from '../../types';
@@ -12,6 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  };
13
13
  import { EventsAPI } from '../../../../adapters/contracts/transport/events.api';
14
14
  import { inject, injectable } from 'tsyringe';
15
+ import { SYSTEM_EVENTS } from '../../../shared/types/system-types';
15
16
  import { Channels } from '../../ports/channel.api-port';
16
17
  import { ChannelType } from '../../types';
17
18
  import { Players } from '../../ports/players.api-port';
@@ -96,7 +97,7 @@ let LocalChannelImplementation = class LocalChannelImplementation extends Channe
96
97
  if (targetIds.length === 0) {
97
98
  return;
98
99
  }
99
- this.events.emit('core:chat:addMessage', targetIds, {
100
+ this.events.emit(SYSTEM_EVENTS.chat.addMessage, targetIds, {
100
101
  args: [author ?? sender.name, message],
101
102
  color: color,
102
103
  });
@@ -111,7 +112,7 @@ let LocalChannelImplementation = class LocalChannelImplementation extends Channe
111
112
  if (targetIds.length === 0) {
112
113
  return;
113
114
  }
114
- this.events.emit('core:chat:addMessage', targetIds, {
115
+ this.events.emit(SYSTEM_EVENTS.chat.addMessage, targetIds, {
115
116
  args: [author, message],
116
117
  color: color,
117
118
  });
@@ -1,4 +1,4 @@
1
- import { RGB } from 'src/kernel';
1
+ import { RGB } from '../../../kernel/utils';
2
2
  import { Channel } from '../concepts/channel';
3
3
  import { Player } from '../entities';
4
4
  import { ChannelMetadata, ChannelType, IChannelValidator } from '../types';
@@ -23,7 +23,7 @@ export declare class WorkerPool extends SimpleEventEmitter {
23
23
  private workerIdCounter;
24
24
  private cleanupInterval;
25
25
  private isShuttingDown;
26
- private workerScriptPath;
26
+ private workerSource;
27
27
  constructor(config?: Partial<WorkerPoolConfig>);
28
28
  /**
29
29
  * Check if using native workers
@@ -5,7 +5,6 @@
5
5
  * Tasks are executed in `node:worker_threads` and communicated through
6
6
  * structured-cloned messages.
7
7
  */
8
- import * as path from 'node:path';
9
8
  import { Worker } from 'node:worker_threads';
10
9
  const DEFAULT_CONFIG = {
11
10
  minWorkers: 0,
@@ -56,9 +55,9 @@ class NativeWorker {
56
55
  currentTask = null;
57
56
  worker;
58
57
  pendingResponses = new Map();
59
- constructor(id, workerScriptPath, callbacks) {
58
+ constructor(id, workerSource, callbacks) {
60
59
  this.id = id;
61
- this.worker = new Worker(workerScriptPath);
60
+ this.worker = new Worker(workerSource, { eval: true });
62
61
  this.worker.on('message', (response) => {
63
62
  const handlers = this.pendingResponses.get(response.id);
64
63
  if (handlers) {
@@ -149,11 +148,44 @@ export class WorkerPool extends SimpleEventEmitter {
149
148
  workerIdCounter = 0;
150
149
  cleanupInterval = null;
151
150
  isShuttingDown = false;
152
- workerScriptPath;
151
+ workerSource;
153
152
  constructor(config = {}) {
154
153
  super();
155
154
  this.config = { ...DEFAULT_CONFIG, ...config };
156
- this.workerScriptPath = path.join(__dirname, 'native-worker.entry.js');
155
+ this.workerSource = `
156
+ const { parentPort } = require('worker_threads')
157
+ const { performance } = require('perf_hooks')
158
+
159
+ if (!parentPort) {
160
+ throw new Error('native worker must be executed inside a Worker thread')
161
+ }
162
+
163
+ function executeCompute(functionBody, input) {
164
+ const fn = new Function('input', 'return (' + functionBody + ')(input)')
165
+ return fn(input)
166
+ }
167
+
168
+ parentPort.on('message', (message) => {
169
+ const startTime = performance.now()
170
+
171
+ try {
172
+ const result = executeCompute(message.functionBody, message.input)
173
+ parentPort.postMessage({
174
+ id: message.id,
175
+ success: true,
176
+ result,
177
+ executionTime: performance.now() - startTime,
178
+ })
179
+ } catch (error) {
180
+ parentPort.postMessage({
181
+ id: message.id,
182
+ success: false,
183
+ error: error instanceof Error ? error.message : String(error),
184
+ executionTime: performance.now() - startTime,
185
+ })
186
+ }
187
+ })
188
+ `;
157
189
  // Start cleanup interval
158
190
  this.cleanupInterval = setInterval(() => this.cleanupIdleWorkers(), 10000);
159
191
  // Spawn minimum workers
@@ -261,7 +293,7 @@ export class WorkerPool extends SimpleEventEmitter {
261
293
  spawnWorker() {
262
294
  try {
263
295
  const id = this.workerIdCounter++;
264
- const worker = new NativeWorker(id, this.workerScriptPath, {
296
+ const worker = new NativeWorker(id, this.workerSource, {
265
297
  onExit: (workerId, code) => {
266
298
  const existing = this.workers.get(workerId);
267
299
  if (!existing)
@@ -11,6 +11,7 @@
11
11
  * - Inputs/outputs must be structured-cloneable.
12
12
  * - Do not pass untrusted code as compute functions.
13
13
  */
14
+ import { performance } from 'node:perf_hooks';
14
15
  /**
15
16
  * Execute a compute function from its string body
16
17
  */
@@ -124,10 +124,21 @@ let OnRpcProcessor = class OnRpcProcessor {
124
124
  });
125
125
  throw error;
126
126
  }
127
- if (hasNoDeclaredParams) {
128
- return handler();
127
+ try {
128
+ if (hasNoDeclaredParams) {
129
+ return await handler();
130
+ }
131
+ return await handler(player, ...validatedArgs);
132
+ }
133
+ catch (error) {
134
+ loggers.netEvent.error(`Handler error in RPC`, {
135
+ event: metadata.eventName,
136
+ handler: handlerName,
137
+ playerId: player.clientID,
138
+ accountId: player.accountID,
139
+ }, error);
140
+ throw error;
129
141
  }
130
- return handler(player, ...validatedArgs);
131
142
  });
132
143
  loggers.netEvent.debug(`Registered RPC: ${metadata.eventName} -> ${handlerName}`);
133
144
  }
@@ -1,2 +1,2 @@
1
1
  import z from 'zod';
2
- export declare function generateSchemaFromTypes(paramTypes: any[]): z.ZodTuple | undefined;
2
+ export declare function generateSchemaFromTypes(paramTypes: any[], defaultParams?: boolean[]): z.ZodTuple | undefined;
@@ -19,7 +19,10 @@ function typeToZodSchema(type) {
19
19
  return undefined;
20
20
  }
21
21
  }
22
- export function generateSchemaFromTypes(paramTypes) {
22
+ function applyOptional(schema, optional) {
23
+ return optional ? schema.optional() : schema;
24
+ }
25
+ export function generateSchemaFromTypes(paramTypes, defaultParams = []) {
23
26
  if (!paramTypes || paramTypes.length === 0)
24
27
  return z.tuple([]);
25
28
  if (paramTypes[0] !== Player) {
@@ -28,11 +31,11 @@ export function generateSchemaFromTypes(paramTypes) {
28
31
  if (paramTypes.length === 1)
29
32
  return z.tuple([]);
30
33
  const argSchemas = [];
31
- for (const t of paramTypes.slice(1)) {
34
+ for (const [index, t] of paramTypes.slice(1).entries()) {
32
35
  const s = typeToZodSchema(t);
33
36
  if (!s)
34
37
  return undefined;
35
- argSchemas.push(s);
38
+ argSchemas.push(applyOptional(s, defaultParams[index + 1] ?? false));
36
39
  }
37
40
  return z.tuple(argSchemas);
38
41
  }
@@ -25,5 +25,8 @@ export function processTupleSchema(schema, args) {
25
25
  return [...positional, [args[positionalCount]]];
26
26
  }
27
27
  }
28
+ if (args.length < items.length) {
29
+ return [...args, ...Array.from({ length: items.length - args.length }, () => undefined)];
30
+ }
28
31
  return args;
29
32
  }
@@ -0,0 +1,55 @@
1
+ type ValueOf<T> = T[keyof T];
2
+ declare const SYSTEM_EVENT_NAMESPACE = "opencore";
3
+ export type RemoteCommandExecuteEventName = `${typeof SYSTEM_EVENT_NAMESPACE}:command:execute:${string}`;
4
+ export declare const buildRemoteCommandExecuteEventName: (resourceName: string) => RemoteCommandExecuteEventName;
5
+ export declare const SYSTEM_EVENTS: {
6
+ readonly core: {
7
+ readonly ready: `_systemcore:${string}`;
8
+ readonly requestReady: `_systemcore:${string}`;
9
+ };
10
+ readonly chat: {
11
+ readonly message: `opencore:${string}:${string}`;
12
+ readonly addMessage: `opencore:${string}:${string}`;
13
+ readonly send: `opencore:${string}:${string}`;
14
+ readonly clear: `opencore:${string}:${string}`;
15
+ };
16
+ readonly command: {
17
+ readonly execute: `opencore:${string}:${string}`;
18
+ };
19
+ readonly spawner: {
20
+ readonly spawn: `opencore:${string}:${string}`;
21
+ readonly teleport: `opencore:${string}:${string}`;
22
+ readonly respawn: `opencore:${string}:${string}`;
23
+ };
24
+ readonly appearance: {
25
+ readonly apply: `opencore:${string}:${string}`;
26
+ readonly reset: `opencore:${string}:${string}`;
27
+ };
28
+ readonly vehicle: {
29
+ readonly create: `opencore:${string}:${string}`;
30
+ readonly createResult: `opencore:${string}:${string}`;
31
+ readonly delete: `opencore:${string}:${string}`;
32
+ readonly deleteResult: `opencore:${string}:${string}`;
33
+ readonly repair: `opencore:${string}:${string}`;
34
+ readonly repairResult: `opencore:${string}:${string}`;
35
+ readonly repaired: `opencore:${string}:${string}`;
36
+ readonly setLocked: `opencore:${string}:${string}`;
37
+ readonly getData: `opencore:${string}:${string}`;
38
+ readonly dataResult: `opencore:${string}:${string}`;
39
+ readonly getPlayerVehicles: `opencore:${string}:${string}`;
40
+ readonly playerVehiclesResult: `opencore:${string}:${string}`;
41
+ readonly created: `opencore:${string}:${string}`;
42
+ readonly deleted: `opencore:${string}:${string}`;
43
+ readonly modified: `opencore:${string}:${string}`;
44
+ readonly warpInto: `opencore:${string}:${string}`;
45
+ };
46
+ readonly npc: {
47
+ readonly deleted: `opencore:${string}:${string}`;
48
+ };
49
+ readonly session: {
50
+ readonly playerInit: `opencore:${string}:${string}`;
51
+ readonly teleportTo: `opencore:${string}:${string}`;
52
+ };
53
+ };
54
+ export type SystemEventName = ValueOf<ValueOf<typeof SYSTEM_EVENTS>>;
55
+ export {};
@@ -0,0 +1,54 @@
1
+ const SYSTEM_EVENT_NAMESPACE = 'opencore';
2
+ const SYSTEM_CORE_EVENT_NAMESPACE = '_systemcore';
3
+ const systemEvent = (scope, action) => `${SYSTEM_EVENT_NAMESPACE}:${scope}:${action}`;
4
+ const systemCoreEvent = (action) => `${SYSTEM_CORE_EVENT_NAMESPACE}:${action}`;
5
+ export const buildRemoteCommandExecuteEventName = (resourceName) => `${SYSTEM_EVENTS.command.execute}:${resourceName}`;
6
+ export const SYSTEM_EVENTS = {
7
+ core: {
8
+ ready: systemCoreEvent('ready'),
9
+ requestReady: systemCoreEvent('request-ready'),
10
+ },
11
+ chat: {
12
+ message: systemEvent('chat', 'message'),
13
+ addMessage: systemEvent('chat', 'addMessage'),
14
+ send: systemEvent('chat', 'send'),
15
+ clear: systemEvent('chat', 'clear'),
16
+ },
17
+ command: {
18
+ execute: systemEvent('command', 'execute'),
19
+ },
20
+ spawner: {
21
+ spawn: systemEvent('spawner', 'spawn'),
22
+ teleport: systemEvent('spawner', 'teleport'),
23
+ respawn: systemEvent('spawner', 'respawn'),
24
+ },
25
+ appearance: {
26
+ apply: systemEvent('appearance', 'apply'),
27
+ reset: systemEvent('appearance', 'reset'),
28
+ },
29
+ vehicle: {
30
+ create: systemEvent('vehicle', 'create'),
31
+ createResult: systemEvent('vehicle', 'createResult'),
32
+ delete: systemEvent('vehicle', 'delete'),
33
+ deleteResult: systemEvent('vehicle', 'deleteResult'),
34
+ repair: systemEvent('vehicle', 'repair'),
35
+ repairResult: systemEvent('vehicle', 'repairResult'),
36
+ repaired: systemEvent('vehicle', 'repaired'),
37
+ setLocked: systemEvent('vehicle', 'setLocked'),
38
+ getData: systemEvent('vehicle', 'getData'),
39
+ dataResult: systemEvent('vehicle', 'dataResult'),
40
+ getPlayerVehicles: systemEvent('vehicle', 'getPlayerVehicles'),
41
+ playerVehiclesResult: systemEvent('vehicle', 'playerVehiclesResult'),
42
+ created: systemEvent('vehicle', 'created'),
43
+ deleted: systemEvent('vehicle', 'deleted'),
44
+ modified: systemEvent('vehicle', 'modified'),
45
+ warpInto: systemEvent('vehicle', 'warpInto'),
46
+ },
47
+ npc: {
48
+ deleted: systemEvent('npc', 'deleted'),
49
+ },
50
+ session: {
51
+ playerInit: systemEvent('player', 'sessionInit'),
52
+ teleportTo: systemEvent('player', 'teleportTo'),
53
+ },
54
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-core/framework",
3
- "version": "1.0.5-beta.2",
3
+ "version": "1.0.6",
4
4
  "description": "Secure, event-driven TypeScript Framework & Runtime engine for CitizenFX (Cfx).",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -75,12 +75,15 @@
75
75
  "bench": "npx tsx benchmark/index.ts",
76
76
  "bench:core": "npx tsx benchmark/index.ts --core",
77
77
  "bench:load": "npx vitest run --project benchmark",
78
- "bench:all": "npx tsx benchmark/index.ts --all"
78
+ "bench:all": "npx tsx benchmark/index.ts --all",
79
+ "validate": "pnpm check && pnpm typecheck && pnpm test",
80
+ "lint-staged": "lint-staged",
81
+ "prepare": "husky"
79
82
  },
80
83
  "keywords": [
81
84
  "framework",
82
85
  "opencore",
83
- "cfx",
86
+ "ragemp",
84
87
  "citizenfx",
85
88
  "redm",
86
89
  "typescript",
@@ -88,7 +91,7 @@
88
91
  ],
89
92
  "author": "OpenCore Team",
90
93
  "license": "MPL-2.0",
91
- "packageManager": "pnpm@10.13.1",
94
+ "packageManager": "pnpm@10.33.0",
92
95
  "peerDependencies": {
93
96
  "reflect-metadata": "^0.2.2",
94
97
  "tsyringe": "^4.10.0",
@@ -98,16 +101,23 @@
98
101
  "uuid": "^13.0.0"
99
102
  },
100
103
  "devDependencies": {
101
- "@biomejs/biome": "^2.3.11",
102
- "@types/node": "^25.0.3",
103
- "@vitest/coverage-v8": "^4.0.16",
104
- "dependency-cruiser": "^17.3.6",
104
+ "@biomejs/biome": "^2.4.8",
105
+ "@types/node": "^25.5.0",
106
+ "@vitest/coverage-v8": "^4.1.1",
107
+ "dependency-cruiser": "^17.3.9",
105
108
  "eslint-config-prettier": "^10.1.8",
106
109
  "eslint-plugin-import": "^2.32.0",
107
110
  "graphviz": "^0.0.9",
108
- "tinybench": "^2.9.0",
111
+ "husky": "^9.1.7",
112
+ "lint-staged": "^16.2.6",
113
+ "tinybench": "^6.0.0",
109
114
  "tsx": "^4.21.0",
110
- "typescript": "^5.9.3",
111
- "vitest": "^4.0.16"
115
+ "typescript": "^6.0.2",
116
+ "vitest": "^4.1.1"
117
+ },
118
+ "lint-staged": {
119
+ "*.{js,cjs,mjs,ts,tsx,json,md}": [
120
+ "biome check --write --no-errors-on-unmatched"
121
+ ]
112
122
  }
113
123
  }