@open-core/ragemp-adapter 0.1.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo/client.tsbuildinfo +1 -1
- package/dist/.tsbuildinfo/root.tsbuildinfo +1 -1
- package/dist/.tsbuildinfo/server.tsbuildinfo +1 -1
- package/dist/.tsbuildinfo/shared.tsbuildinfo +1 -1
- package/dist/client/create-ragemp-client-adapter.js +10 -3
- package/dist/client/native-chat.d.ts +10 -0
- package/dist/client/native-chat.js +69 -6
- package/dist/client/ragemp-camera-port.d.ts +19 -0
- package/dist/client/ragemp-camera-port.js +72 -0
- package/dist/client/ragemp-local-player-bridge.d.ts +3 -0
- package/dist/client/ragemp-local-player-bridge.js +10 -0
- package/dist/client/ragemp-ped-port.d.ts +23 -0
- package/dist/client/ragemp-ped-port.js +107 -0
- package/dist/client/ragemp-platform-bridge.js +1 -1
- package/dist/client/ragemp-progress-port.d.ts +21 -0
- package/dist/client/ragemp-progress-port.js +169 -0
- package/dist/client/ragemp-runtime-bridge.d.ts +2 -4
- package/dist/client/ragemp-runtime-bridge.js +3 -0
- package/dist/client/ragemp-spawn-bridge.d.ts +4 -4
- package/dist/client/ragemp-spawn-bridge.js +5 -3
- package/dist/client/ragemp-vehicle-port.d.ts +34 -0
- package/dist/client/ragemp-vehicle-port.js +272 -0
- package/dist/client/ragemp-webview-bridge.d.ts +5 -0
- package/dist/client/ragemp-webview-bridge.js +54 -12
- package/dist/server/ragemp-engine-events.d.ts +2 -5
- package/dist/server/ragemp-engine-events.js +3 -15
- package/dist/server/ragemp-entity-server.js +14 -1
- package/dist/server/ragemp-exports.d.ts +51 -0
- package/dist/server/ragemp-exports.js +202 -1
- package/dist/server/ragemp-player-appearance-lifecycle-server.js +4 -3
- package/dist/server/ragemp-resourceinfo.js +3 -0
- package/dist/shared/exports-registry.d.ts +2 -2
- package/dist/shared/transport/helpers.d.ts +3 -3
- package/dist/shared/transport/helpers.js +33 -7
- package/dist/shared/transport/ragemp.events.d.ts +3 -3
- package/dist/shared/transport/ragemp.events.js +4 -2
- package/dist/shared/transport/ragemp.rpc.d.ts +4 -8
- package/dist/shared/transport/ragemp.rpc.js +29 -5
- package/package.json +5 -7
|
@@ -16,17 +16,218 @@ exports.RageMPExports = void 0;
|
|
|
16
16
|
const tsyringe_1 = require("tsyringe");
|
|
17
17
|
const server_1 = require("@open-core/framework/contracts/server");
|
|
18
18
|
const exports_registry_1 = require("../shared/exports-registry");
|
|
19
|
+
const REQUEST_EVENT_PREFIX = '__oc:exports:req:';
|
|
20
|
+
const RESPONSE_EVENT_PREFIX = '__oc:exports:res:';
|
|
21
|
+
const EXPORTS_TIMEOUT_MS = 7500;
|
|
22
|
+
const WAIT_INTERVAL_MS = 150;
|
|
23
|
+
const META_HAS_EXPORT = '__oc_meta_has_export__';
|
|
24
|
+
function isObjectRecord(value) {
|
|
25
|
+
return typeof value === 'object' && value !== null;
|
|
26
|
+
}
|
|
27
|
+
function isPlayerMpLike(value) {
|
|
28
|
+
return isObjectRecord(value) && 'id' in value && 'serial' in value;
|
|
29
|
+
}
|
|
30
|
+
function isExportRequestPayload(value) {
|
|
31
|
+
return (isObjectRecord(value) &&
|
|
32
|
+
typeof value.requestId === 'string' &&
|
|
33
|
+
typeof value.callerResource === 'string' &&
|
|
34
|
+
typeof value.exportName === 'string' &&
|
|
35
|
+
Array.isArray(value.args));
|
|
36
|
+
}
|
|
37
|
+
function isExportResponsePayload(value) {
|
|
38
|
+
if (!isObjectRecord(value) ||
|
|
39
|
+
typeof value.requestId !== 'string' ||
|
|
40
|
+
typeof value.ok !== 'boolean') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (value.ok) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return (isObjectRecord(value.error) &&
|
|
47
|
+
typeof value.error.name === 'string' &&
|
|
48
|
+
typeof value.error.message === 'string');
|
|
49
|
+
}
|
|
19
50
|
let RageMPExports = class RageMPExports extends server_1.IExports {
|
|
20
51
|
constructor(resourceInfo) {
|
|
21
52
|
super();
|
|
22
53
|
this.resourceInfo = resourceInfo;
|
|
54
|
+
this.pendingRequests = new Map();
|
|
55
|
+
this.localHandlers = new Map();
|
|
56
|
+
this.currentResourceName = this.resourceInfo.getCurrentResourceName();
|
|
57
|
+
this.requestEventName = `${REQUEST_EVENT_PREFIX}${this.currentResourceName}`;
|
|
58
|
+
this.responseEventName = `${RESPONSE_EVENT_PREFIX}${this.currentResourceName}`;
|
|
59
|
+
this.registerTransportListeners();
|
|
23
60
|
}
|
|
24
61
|
register(exportName, handler) {
|
|
25
|
-
|
|
62
|
+
this.localHandlers.set(exportName, handler);
|
|
63
|
+
exports_registry_1.exportsRegistry.register(this.currentResourceName, exportName, handler);
|
|
26
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolves exports through the local registry shared by the current adapter runtime.
|
|
67
|
+
*/
|
|
27
68
|
getResource(resourceName) {
|
|
28
69
|
return exports_registry_1.exportsRegistry.resourceProxy(resourceName);
|
|
29
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns an async proxy that forwards method calls to another server resource.
|
|
73
|
+
*
|
|
74
|
+
* @remarks
|
|
75
|
+
* The proxy intentionally ignores Promise-like properties (`then`, `catch`, `finally`)
|
|
76
|
+
* so `await` does not accidentally treat the proxy itself as a thenable.
|
|
77
|
+
*/
|
|
78
|
+
getRemoteResource(resourceName) {
|
|
79
|
+
return new Proxy({}, {
|
|
80
|
+
get: (_, exportName) => {
|
|
81
|
+
if (typeof exportName !== 'string' ||
|
|
82
|
+
exportName === 'then' ||
|
|
83
|
+
exportName === 'catch' ||
|
|
84
|
+
exportName === 'finally') {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
return (...args) => this.callRemoteExport(resourceName, exportName, ...args);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Invokes a remote export by sending a server-side RageMP event to the target resource.
|
|
93
|
+
*/
|
|
94
|
+
callRemoteExport(resourceName, exportName, ...args) {
|
|
95
|
+
const requestId = this.createRequestId(resourceName, exportName);
|
|
96
|
+
const requestEventName = `${REQUEST_EVENT_PREFIX}${resourceName}`;
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
const timeout = setTimeout(() => {
|
|
99
|
+
this.pendingRequests.delete(requestId);
|
|
100
|
+
reject(new Error(`[exports] Timed out calling "${exportName}" on resource "${resourceName}".`));
|
|
101
|
+
}, EXPORTS_TIMEOUT_MS);
|
|
102
|
+
this.pendingRequests.set(requestId, { resolve, reject, timeout });
|
|
103
|
+
mp.events.call(requestEventName, {
|
|
104
|
+
requestId,
|
|
105
|
+
callerResource: this.currentResourceName,
|
|
106
|
+
exportName,
|
|
107
|
+
args,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Waits for a remote resource to expose a matching export before returning the async proxy.
|
|
113
|
+
*/
|
|
114
|
+
async waitForRemoteResource(resourceName, options) {
|
|
115
|
+
const timeoutMs = options?.timeoutMs ?? EXPORTS_TIMEOUT_MS;
|
|
116
|
+
const intervalMs = options?.intervalMs ?? WAIT_INTERVAL_MS;
|
|
117
|
+
const startedAt = Date.now();
|
|
118
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
119
|
+
try {
|
|
120
|
+
const isAvailable = await this.callRemoteExport(resourceName, META_HAS_EXPORT, options?.exportName);
|
|
121
|
+
if (isAvailable) {
|
|
122
|
+
return this.getRemoteResource(resourceName);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Ignore transient availability failures while waiting.
|
|
127
|
+
}
|
|
128
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
129
|
+
}
|
|
130
|
+
throw new Error(`[exports] Timed out waiting for resource "${resourceName}" remote exports.`);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Installs the request/response listeners used by the optional remote export helper layer.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* The listeners reject payloads that look like player-originated server events so clients
|
|
137
|
+
* cannot invoke server resource exports through this transport.
|
|
138
|
+
*/
|
|
139
|
+
registerTransportListeners() {
|
|
140
|
+
mp.events.add(this.requestEventName, (...args) => {
|
|
141
|
+
const payload = args[0];
|
|
142
|
+
if (isPlayerMpLike(payload) || !isExportRequestPayload(payload)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
void this.handleExportRequest(payload);
|
|
146
|
+
});
|
|
147
|
+
mp.events.add(this.responseEventName, (...args) => {
|
|
148
|
+
const payload = args[0];
|
|
149
|
+
if (isPlayerMpLike(payload) || !isExportResponsePayload(payload)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.handleExportResponse(payload);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Executes one remote export request against handlers registered in the current resource.
|
|
157
|
+
*/
|
|
158
|
+
async handleExportRequest(payload) {
|
|
159
|
+
const responseEventName = `${RESPONSE_EVENT_PREFIX}${payload.callerResource}`;
|
|
160
|
+
try {
|
|
161
|
+
const result = await this.executeLocalExport(payload.exportName, payload.args);
|
|
162
|
+
mp.events.call(responseEventName, {
|
|
163
|
+
requestId: payload.requestId,
|
|
164
|
+
ok: true,
|
|
165
|
+
result,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
mp.events.call(responseEventName, {
|
|
170
|
+
requestId: payload.requestId,
|
|
171
|
+
ok: false,
|
|
172
|
+
error: this.serializeError(error),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Resolves or rejects the pending promise associated with a remote export response.
|
|
178
|
+
*/
|
|
179
|
+
handleExportResponse(payload) {
|
|
180
|
+
const pendingRequest = this.pendingRequests.get(payload.requestId);
|
|
181
|
+
if (!pendingRequest) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
clearTimeout(pendingRequest.timeout);
|
|
185
|
+
this.pendingRequests.delete(payload.requestId);
|
|
186
|
+
if (payload.ok) {
|
|
187
|
+
pendingRequest.resolve(payload.result);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
pendingRequest.reject(this.deserializeError(payload.error));
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Executes an export from the current resource without going back through the shared registry.
|
|
194
|
+
*/
|
|
195
|
+
async executeLocalExport(exportName, args) {
|
|
196
|
+
if (exportName === META_HAS_EXPORT) {
|
|
197
|
+
const requestedExportName = typeof args[0] === 'string' ? args[0] : undefined;
|
|
198
|
+
if (!requestedExportName) {
|
|
199
|
+
return this.localHandlers.size > 0;
|
|
200
|
+
}
|
|
201
|
+
return this.localHandlers.has(requestedExportName);
|
|
202
|
+
}
|
|
203
|
+
const handler = this.localHandlers.get(exportName);
|
|
204
|
+
if (!handler) {
|
|
205
|
+
throw new Error(`[exports] Export "${exportName}" not found in resource "${this.currentResourceName}".`);
|
|
206
|
+
}
|
|
207
|
+
return Promise.resolve(handler(...args));
|
|
208
|
+
}
|
|
209
|
+
createRequestId(resourceName, exportName) {
|
|
210
|
+
return `${this.currentResourceName}:${resourceName}:${exportName}:${Date.now()}:${Math.random()}`;
|
|
211
|
+
}
|
|
212
|
+
serializeError(error) {
|
|
213
|
+
if (error instanceof Error) {
|
|
214
|
+
return {
|
|
215
|
+
name: error.name,
|
|
216
|
+
message: error.message,
|
|
217
|
+
stack: error.stack,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
name: 'Error',
|
|
222
|
+
message: String(error),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
deserializeError(error) {
|
|
226
|
+
const result = new Error(error.message);
|
|
227
|
+
result.name = error.name;
|
|
228
|
+
result.stack = error.stack;
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
30
231
|
};
|
|
31
232
|
exports.RageMPExports = RageMPExports;
|
|
32
233
|
exports.RageMPExports = RageMPExports = __decorate([
|
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.RageMPPlayerAppearanceLifecycleServer = void 0;
|
|
16
16
|
const tsyringe_1 = require("tsyringe");
|
|
17
17
|
const contracts_1 = require("@open-core/framework/contracts");
|
|
18
|
+
const framework_1 = require("@open-core/framework");
|
|
18
19
|
const server_1 = require("@open-core/framework/server");
|
|
19
20
|
const server_2 = require("@open-core/framework/contracts/server");
|
|
20
21
|
const server_3 = require("@open-core/framework/contracts/server");
|
|
@@ -33,21 +34,21 @@ let RageMPPlayerAppearanceLifecycleServer = class RageMPPlayerAppearanceLifecycl
|
|
|
33
34
|
if (appearance.model) {
|
|
34
35
|
this.playerServer.setModel(playerSrc, appearance.model);
|
|
35
36
|
}
|
|
36
|
-
this.events.emit(
|
|
37
|
+
this.events.emit(framework_1.SYSTEM_EVENTS.appearance.apply, target, appearance);
|
|
37
38
|
return { success: true, appearance };
|
|
38
39
|
}
|
|
39
40
|
applyClothing(playerSrc, appearance) {
|
|
40
41
|
const target = this.resolveTarget(playerSrc);
|
|
41
42
|
if (!target)
|
|
42
43
|
return false;
|
|
43
|
-
this.events.emit(
|
|
44
|
+
this.events.emit(framework_1.SYSTEM_EVENTS.appearance.apply, target, appearance);
|
|
44
45
|
return true;
|
|
45
46
|
}
|
|
46
47
|
reset(playerSrc) {
|
|
47
48
|
const target = this.resolveTarget(playerSrc);
|
|
48
49
|
if (!target)
|
|
49
50
|
return false;
|
|
50
|
-
this.events.emit(
|
|
51
|
+
this.events.emit(framework_1.SYSTEM_EVENTS.appearance.reset, target);
|
|
51
52
|
return true;
|
|
52
53
|
}
|
|
53
54
|
resolveTarget(playerSrc) {
|
|
@@ -4,6 +4,9 @@ exports.RageMPResourceInfo = void 0;
|
|
|
4
4
|
const server_1 = require("@open-core/framework/contracts/server");
|
|
5
5
|
class RageMPResourceInfo extends server_1.IResourceInfo {
|
|
6
6
|
getCurrentResourceName() {
|
|
7
|
+
if (typeof __OPENCORE_RESOURCE_NAME__ === 'string' && __OPENCORE_RESOURCE_NAME__.trim()) {
|
|
8
|
+
return __OPENCORE_RESOURCE_NAME__;
|
|
9
|
+
}
|
|
7
10
|
if (typeof __dirname !== 'string')
|
|
8
11
|
return 'default';
|
|
9
12
|
const normalized = __dirname.replace(/\\/g, '/');
|
|
@@ -11,7 +11,7 @@ export type ExportsProxy = {
|
|
|
11
11
|
export declare class ExportsRegistry {
|
|
12
12
|
private readonly registry;
|
|
13
13
|
private readonly namespaceProxyCache;
|
|
14
|
-
register(resource: string, name: string, handler:
|
|
14
|
+
register<TArgs extends readonly unknown[], TResult = unknown>(resource: string, name: string, handler: (...args: TArgs) => TResult): void;
|
|
15
15
|
call(resource: string, name: string, ...args: ExportArgs): unknown;
|
|
16
16
|
private createNamespaceProxy;
|
|
17
17
|
private getOrCreateNamespaceProxy;
|
|
@@ -24,6 +24,6 @@ export declare class ExportsRegistry {
|
|
|
24
24
|
list(resource: string): string[];
|
|
25
25
|
}
|
|
26
26
|
export declare const exportsRegistry: ExportsRegistry;
|
|
27
|
-
export declare function registerExport(resource: string, name: string, handler:
|
|
27
|
+
export declare function registerExport<TArgs extends readonly unknown[], TResult = unknown>(resource: string, name: string, handler: (...args: TArgs) => TResult): void;
|
|
28
28
|
export declare function callExport(resource: string, name: string, ...args: ExportArgs): unknown;
|
|
29
29
|
export {};
|
|
@@ -5,10 +5,10 @@ import type { RuntimeContext } from '@open-core/framework/contracts';
|
|
|
5
5
|
*
|
|
6
6
|
* Analogous to FiveM's `onNet`:
|
|
7
7
|
* - Server: RageMP prepends the PlayerMp as the first native argument.
|
|
8
|
-
* We expose it as-is so callers can read player.id before
|
|
8
|
+
* We expose it as-is so callers can read player.id before awaiting.
|
|
9
9
|
* - Client: no source argument; we pass undefined to keep the signature uniform.
|
|
10
10
|
*/
|
|
11
|
-
export declare function onNet(context: RuntimeContext, event: string, handler: (source: PlayerMp | undefined, ...args:
|
|
11
|
+
export declare function onNet<TArgs extends readonly unknown[]>(context: RuntimeContext, event: string, handler: (source: PlayerMp | undefined, ...args: TArgs) => void): void;
|
|
12
12
|
/**
|
|
13
13
|
* Normalizes RageMP's server/client event emission into a single interface.
|
|
14
14
|
*
|
|
@@ -21,4 +21,4 @@ export declare function onNet(context: RuntimeContext, event: string, handler: (
|
|
|
21
21
|
* NOTE: Only player targets are supported — vehicles/peds are not valid
|
|
22
22
|
* network targets, matching FiveM's TriggerClientEvent semantics.
|
|
23
23
|
*/
|
|
24
|
-
export declare function emitNet(context: RuntimeContext, event: string, target: number | number[] | -1, ...payload:
|
|
24
|
+
export declare function emitNet(context: RuntimeContext, event: string, target: number | number[] | -1, ...payload: unknown[]): void;
|
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.onNet = onNet;
|
|
4
4
|
exports.emitNet = emitNet;
|
|
5
|
+
const JSON_WIRE_PREFIX = '__OPENCORE_JSON__:';
|
|
6
|
+
function serializeWireValue(value) {
|
|
7
|
+
if (value === null || value === undefined)
|
|
8
|
+
return value;
|
|
9
|
+
if (Array.isArray(value) || (typeof value === 'object' && value.constructor === Object)) {
|
|
10
|
+
return `${JSON_WIRE_PREFIX}${JSON.stringify(value)}`;
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function deserializeWireValue(value) {
|
|
15
|
+
if (typeof value !== 'string' || !value.startsWith(JSON_WIRE_PREFIX)) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(value.slice(JSON_WIRE_PREFIX.length));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function deserializeWireArgs(args) {
|
|
26
|
+
return args.map((arg) => deserializeWireValue(arg));
|
|
27
|
+
}
|
|
5
28
|
/**
|
|
6
29
|
* Resolves a player by numeric ID, returning undefined when the player no
|
|
7
30
|
* longer exists (disconnected, invalid handle, etc.).
|
|
@@ -21,7 +44,7 @@ function resolvePlayer(id) {
|
|
|
21
44
|
*
|
|
22
45
|
* Analogous to FiveM's `onNet`:
|
|
23
46
|
* - Server: RageMP prepends the PlayerMp as the first native argument.
|
|
24
|
-
* We expose it as-is so callers can read player.id before
|
|
47
|
+
* We expose it as-is so callers can read player.id before awaiting.
|
|
25
48
|
* - Client: no source argument; we pass undefined to keep the signature uniform.
|
|
26
49
|
*/
|
|
27
50
|
function onNet(context, event, handler) {
|
|
@@ -32,11 +55,13 @@ function onNet(context, event, handler) {
|
|
|
32
55
|
// all other events should be safe to guard this way.
|
|
33
56
|
if (!mp.players.exists(player.id))
|
|
34
57
|
return;
|
|
35
|
-
handler(player, ...args);
|
|
58
|
+
handler(player, ...deserializeWireArgs(args));
|
|
36
59
|
});
|
|
37
60
|
}
|
|
38
61
|
else {
|
|
39
|
-
mp.events.add(event, (...args) =>
|
|
62
|
+
mp.events.add(event, (...args) => {
|
|
63
|
+
handler(undefined, ...deserializeWireArgs(args));
|
|
64
|
+
});
|
|
40
65
|
}
|
|
41
66
|
}
|
|
42
67
|
/**
|
|
@@ -52,19 +77,20 @@ function onNet(context, event, handler) {
|
|
|
52
77
|
* network targets, matching FiveM's TriggerClientEvent semantics.
|
|
53
78
|
*/
|
|
54
79
|
function emitNet(context, event, target, ...payload) {
|
|
80
|
+
const serializedPayload = payload.map((item) => serializeWireValue(item));
|
|
55
81
|
if (context !== 'server') {
|
|
56
|
-
mp.events.callRemote(event, ...
|
|
82
|
+
mp.events.callRemote(event, ...serializedPayload);
|
|
57
83
|
return;
|
|
58
84
|
}
|
|
59
85
|
if (target === -1) {
|
|
60
|
-
mp.players.call(event,
|
|
86
|
+
mp.players.call(event, serializedPayload);
|
|
61
87
|
return;
|
|
62
88
|
}
|
|
63
89
|
if (Array.isArray(target)) {
|
|
64
90
|
for (const id of target) {
|
|
65
|
-
resolvePlayer(id)?.call(event,
|
|
91
|
+
resolvePlayer(id)?.call(event, serializedPayload);
|
|
66
92
|
}
|
|
67
93
|
return;
|
|
68
94
|
}
|
|
69
|
-
resolvePlayer(target)?.call(event,
|
|
95
|
+
resolvePlayer(target)?.call(event, serializedPayload);
|
|
70
96
|
}
|
|
@@ -2,9 +2,9 @@ import { EventsAPI, type RuntimeContext } from '@open-core/framework/contracts';
|
|
|
2
2
|
export declare class RageMPEvents extends EventsAPI<RuntimeContext> {
|
|
3
3
|
private readonly context;
|
|
4
4
|
constructor(context: RuntimeContext);
|
|
5
|
-
on(event: string, handler: (source: {
|
|
5
|
+
on<TArgs extends readonly unknown[]>(event: string, handler: (source: {
|
|
6
6
|
clientId: number | undefined;
|
|
7
7
|
raw: PlayerMp | undefined;
|
|
8
|
-
}, ...args:
|
|
9
|
-
emit(event: string, ...args:
|
|
8
|
+
}, ...args: TArgs) => void | Promise<void>): void;
|
|
9
|
+
emit(event: string, ...args: unknown[]): void;
|
|
10
10
|
}
|
|
@@ -16,7 +16,7 @@ class RageMPEvents extends contracts_1.EventsAPI {
|
|
|
16
16
|
}
|
|
17
17
|
on(event, handler) {
|
|
18
18
|
(0, helpers_1.onNet)(this.context, event, (source, ...args) => {
|
|
19
|
-
handler({ clientId: source?.id, raw: source }, ...args);
|
|
19
|
+
void handler({ clientId: source?.id, raw: source }, ...args);
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
emit(event, ...args) {
|
|
@@ -39,7 +39,9 @@ class RageMPEvents extends contracts_1.EventsAPI {
|
|
|
39
39
|
(0, helpers_1.emitNet)(this.context, event, target.clientID, ...payload);
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
if (typeof target === 'number') {
|
|
43
|
+
(0, helpers_1.emitNet)(this.context, event, target, ...payload);
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
exports.RageMPEvents = RageMPEvents;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RpcAPI, type RuntimeContext } from '@open-core/framework/contracts';
|
|
1
|
+
import { RpcAPI, type RpcContext, type RuntimeContext } from '@open-core/framework/contracts';
|
|
2
2
|
export declare class RageMPRpc<C extends RuntimeContext = RuntimeContext> extends RpcAPI<C> {
|
|
3
3
|
private readonly context;
|
|
4
4
|
private readonly pending;
|
|
@@ -9,13 +9,9 @@ export declare class RageMPRpc<C extends RuntimeContext = RuntimeContext> extend
|
|
|
9
9
|
private readonly responseEvent;
|
|
10
10
|
private readonly defaultTimeoutMs;
|
|
11
11
|
constructor(context: C);
|
|
12
|
-
on<TArgs extends
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
raw?: unknown;
|
|
16
|
-
}, ...args: TArgs) => TResult | Promise<TResult>): void;
|
|
17
|
-
call<TResult = unknown>(name: string, ...args: any[]): Promise<TResult>;
|
|
18
|
-
notify(name: string, ...args: any[]): Promise<void>;
|
|
12
|
+
on<TArgs extends readonly unknown[], TResult>(name: string, handler: (ctx: RpcContext, ...args: TArgs) => TResult | Promise<TResult>): void;
|
|
13
|
+
call<TResult = unknown>(name: string, ...args: unknown[]): Promise<TResult>;
|
|
14
|
+
notify(name: string, ...args: unknown[]): Promise<void>;
|
|
19
15
|
private normalizeInvocation;
|
|
20
16
|
private isValidTarget;
|
|
21
17
|
private sendAndWait;
|
|
@@ -3,7 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RageMPRpc = void 0;
|
|
4
4
|
const contracts_1 = require("@open-core/framework/contracts");
|
|
5
5
|
const helpers_1 = require("./helpers");
|
|
6
|
+
function serializeRpcError(error) {
|
|
7
|
+
if (typeof error === 'object' &&
|
|
8
|
+
error !== null &&
|
|
9
|
+
'message' in error &&
|
|
10
|
+
typeof error.message === 'string' &&
|
|
11
|
+
'expose' in error &&
|
|
12
|
+
error.expose === true) {
|
|
13
|
+
return {
|
|
14
|
+
message: error.message,
|
|
15
|
+
name: 'name' in error && typeof error.name === 'string' ? error.name : undefined,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return { message: 'An internal server error occurred' };
|
|
19
|
+
}
|
|
6
20
|
function getCurrentResourceNameSafe() {
|
|
21
|
+
if (typeof __OPENCORE_RESOURCE_NAME__ === 'string' && __OPENCORE_RESOURCE_NAME__.trim()) {
|
|
22
|
+
return __OPENCORE_RESOURCE_NAME__;
|
|
23
|
+
}
|
|
7
24
|
if (typeof __dirname !== 'string')
|
|
8
25
|
return 'default';
|
|
9
26
|
const parts = __dirname.replace(/\\/g, '/').split('/');
|
|
@@ -28,7 +45,7 @@ class RageMPRpc extends contracts_1.RpcAPI {
|
|
|
28
45
|
});
|
|
29
46
|
}
|
|
30
47
|
on(name, handler) {
|
|
31
|
-
this.handlers.set(name, handler);
|
|
48
|
+
this.handlers.set(name, (ctx, ...args) => handler(ctx, ...args));
|
|
32
49
|
}
|
|
33
50
|
call(name, ...args) {
|
|
34
51
|
const { target, payload } = this.normalizeInvocation(name, 'call', args);
|
|
@@ -76,7 +93,7 @@ class RageMPRpc extends contracts_1.RpcAPI {
|
|
|
76
93
|
this.pending.delete(id);
|
|
77
94
|
reject(new Error(`RageMPRpc: timeout waiting for '${input.kind}' response for '${input.name}' (${id})`));
|
|
78
95
|
}, this.defaultTimeoutMs);
|
|
79
|
-
this.pending.set(id, { resolve: resolve, reject, timeout });
|
|
96
|
+
this.pending.set(id, { resolve: (value) => resolve(value), reject, timeout });
|
|
80
97
|
if (this.context === 'server') {
|
|
81
98
|
const resolvedTarget = this.resolveServerTarget(target, input.kind, input.name);
|
|
82
99
|
(0, helpers_1.emitNet)(this.context, this.requestEvent, resolvedTarget, msg);
|
|
@@ -139,6 +156,7 @@ class RageMPRpc extends contracts_1.RpcAPI {
|
|
|
139
156
|
}
|
|
140
157
|
}
|
|
141
158
|
catch (err) {
|
|
159
|
+
const errorInfo = serializeRpcError(err);
|
|
142
160
|
if (msg.kind === 'notify') {
|
|
143
161
|
this.emitResponse(replyTarget, { kind: 'ack', id: msg.id });
|
|
144
162
|
return;
|
|
@@ -148,8 +166,8 @@ class RageMPRpc extends contracts_1.RpcAPI {
|
|
|
148
166
|
id: msg.id,
|
|
149
167
|
ok: false,
|
|
150
168
|
error: {
|
|
151
|
-
message:
|
|
152
|
-
name:
|
|
169
|
+
message: errorInfo.message,
|
|
170
|
+
name: errorInfo.name,
|
|
153
171
|
},
|
|
154
172
|
});
|
|
155
173
|
}
|
|
@@ -171,7 +189,13 @@ class RageMPRpc extends contracts_1.RpcAPI {
|
|
|
171
189
|
return;
|
|
172
190
|
}
|
|
173
191
|
const error = new Error(msg.error?.message ?? 'RageMPRpc: remote error');
|
|
174
|
-
|
|
192
|
+
if (msg.error?.name) {
|
|
193
|
+
Object.defineProperty(error, 'name', {
|
|
194
|
+
value: msg.error.name,
|
|
195
|
+
configurable: true,
|
|
196
|
+
writable: true,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
175
199
|
pending.reject(error);
|
|
176
200
|
}
|
|
177
201
|
emitResponse(target, msg) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-core/ragemp-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "External Rage Multiplayer adapter for OpenCore framework.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,20 +37,18 @@
|
|
|
37
37
|
"adapter"
|
|
38
38
|
],
|
|
39
39
|
"license": "MPL-2.0",
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@open-core/framework": "v1.0.5-beta.2"
|
|
42
|
-
},
|
|
43
40
|
"peerDependencies": {
|
|
44
41
|
"reflect-metadata": "^0.2.2",
|
|
45
42
|
"tsyringe": "^4.10.0",
|
|
46
43
|
"zod": "^4.3.5"
|
|
47
44
|
},
|
|
48
45
|
"devDependencies": {
|
|
49
|
-
"@biomejs/biome": "^2.
|
|
46
|
+
"@biomejs/biome": "^2.4.10",
|
|
47
|
+
"@open-core/framework": "latest",
|
|
50
48
|
"@ragempcommunity/types-client": "^2.1.9",
|
|
51
49
|
"@ragempcommunity/types-server": "^2.1.9",
|
|
52
|
-
"@types/node": "^25.0
|
|
53
|
-
"typescript": "^
|
|
50
|
+
"@types/node": "^25.5.0",
|
|
51
|
+
"typescript": "^6.0.2"
|
|
54
52
|
},
|
|
55
53
|
"scripts": {
|
|
56
54
|
"build": "tsc -b",
|