init
This commit is contained in:
commit
821cd589d9
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.idea
|
||||
.vscode
|
||||
dist
|
||||
node_modules
|
||||
bun.lock
|
40
biome.json
Normal file
40
biome.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": false
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"arrowParentheses": "asNeeded",
|
||||
"bracketSpacing": true,
|
||||
"indentWidth": 2,
|
||||
"lineEnding": "crlf",
|
||||
"lineWidth": 80,
|
||||
"quoteStyle": "single",
|
||||
"semicolons": "asNeeded",
|
||||
"trailingCommas": "all"
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"enabled": true,
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
license.md
Normal file
15
license.md
Normal file
@ -0,0 +1,15 @@
|
||||
Custom Attribution-NoDerivs Software License
|
||||
|
||||
Copyright (c) 2025 Entity Seven Group
|
||||
|
||||
This license allows you to use, copy, and distribute these packages (the "Software"), including for commercial purposes, provided that the following conditions are met:
|
||||
|
||||
1. **Attribution:** You must give appropriate credit to the original author of the Software, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||
|
||||
2. **No Derivative Works:** You may not modify, transform, or build upon the Software.
|
||||
|
||||
3. **Usage and Commercial Use:** You are allowed to use, sell, and gain income from projects that utilize the Software, as long as you comply with the terms of this license.
|
||||
|
||||
4. **No Additional Restrictions:** You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
37
package.json
Normal file
37
package.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"workspaces": [
|
||||
"rpc",
|
||||
"shared-types"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.1.1",
|
||||
"@types/bun": "^1.2.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "bunx biome check",
|
||||
"format": "bunx biome format --write"
|
||||
},
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "CC0-1.0",
|
||||
"author": "Entity Seven Group",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Danya H",
|
||||
"email": "dev.rilaxik@gmail.com",
|
||||
"url": "https://github.com/rilaxik/"
|
||||
},
|
||||
{
|
||||
"name": "Oleksandr Honcharov",
|
||||
"email": "0976053529@ukr.net",
|
||||
"url": "https://github.com/SashaGoncharov19/"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rilaxik/fivem-rpc.git"
|
||||
}
|
||||
}
|
15
rpc/license.md
Normal file
15
rpc/license.md
Normal file
@ -0,0 +1,15 @@
|
||||
Custom Attribution-NoDerivs Software License
|
||||
|
||||
Copyright (c) 2025 Entity Seven Group
|
||||
|
||||
This license allows you to use, copy, and distribute these packages (the "Software"), including for commercial purposes, provided that the following conditions are met:
|
||||
|
||||
1. **Attribution:** You must give appropriate credit to the original author of the Software, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||
|
||||
2. **No Derivative Works:** You may not modify, transform, or build upon the Software.
|
||||
|
||||
3. **Usage and Commercial Use:** You are allowed to use, sell, and gain income from projects that utilize the Software, as long as you comply with the terms of this license.
|
||||
|
||||
4. **No Additional Restrictions:** You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
37
rpc/package.json
Normal file
37
rpc/package.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@entityseven/fivem-rpc",
|
||||
"description": "FiveM RPC is an abstraction for events in GTA V FiveM servers in JS/TS",
|
||||
"version": "0.1.0",
|
||||
"main": "",
|
||||
"types": "",
|
||||
"files": [
|
||||
"types/**/*",
|
||||
"readme.md",
|
||||
"license.md"
|
||||
],
|
||||
"keywords": [
|
||||
"fivem-rpc",
|
||||
"fivem-rpc-shared-types",
|
||||
"fivem",
|
||||
"gta"
|
||||
],
|
||||
"type": "module",
|
||||
"author": "Entity Seven Group",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Danya H",
|
||||
"email": "dev.rilaxik@gmail.com",
|
||||
"url": "https://github.com/rilaxik/"
|
||||
}
|
||||
],
|
||||
"license": "Custom-Attribution-NoDerivs",
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "^7.47.9",
|
||||
"@citizenfx/client": "^2.0.15015-1",
|
||||
"@citizenfx/server": "^2.0.14862-1",
|
||||
"tsup": "^8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
0
rpc/readme.md
Normal file
0
rpc/readme.md
Normal file
402
rpc/src/core/client.ts
Normal file
402
rpc/src/core/client.ts
Normal file
@ -0,0 +1,402 @@
|
||||
/// <reference types="@citizenfx/client" />
|
||||
|
||||
import type * as s from '@entityseven/fivem-rpc-shared-types'
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { generateUUID, parse, stringify, stringifyWeb } from '../utils/funcs'
|
||||
import {
|
||||
NATIVE_CLIENT_EVENTS,
|
||||
NATIVE_CLIENT_NETWORK_EVENTS,
|
||||
} from '../utils/native'
|
||||
import {
|
||||
type RPCConfig,
|
||||
RPCErrors,
|
||||
RPCEvents,
|
||||
type RPCNativeClientEvents,
|
||||
type RPCNativeClientNetworksEvents,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
type RPCStateWeb,
|
||||
} from '../utils/types'
|
||||
import { Wrapper } from './wrapper'
|
||||
|
||||
export class RPCInstanceClient extends Wrapper {
|
||||
private readonly _emitterServer: Emitter
|
||||
private readonly _pendingServer: Emitter
|
||||
private readonly _emitterWeb: Emitter
|
||||
private readonly _pendingWeb: Emitter
|
||||
private readonly _pendingWebToServer: Emitter
|
||||
|
||||
constructor(props: RPCConfig<'client'>) {
|
||||
super(props)
|
||||
|
||||
this._emitterServer = new Emitter()
|
||||
this._pendingServer = new Emitter()
|
||||
this._emitterWeb = new Emitter()
|
||||
this._pendingWeb = new Emitter()
|
||||
this._pendingWebToServer = new Emitter()
|
||||
|
||||
this.console.log('[RPC] Initialized Client')
|
||||
|
||||
onNet(RPCEvents.LISTENER_SERVER, this._handleServer.bind(this))
|
||||
RegisterNuiCallbackType(RPCEvents.LISTENER_WEB)
|
||||
on(
|
||||
`__cfx_nui:${RPCEvents.LISTENER_WEB}`,
|
||||
async (data: RPCState, callback: (res: unknown) => void) => {
|
||||
const res = await this._handleWeb(data)
|
||||
callback(res)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// ===== HANDLERS =====
|
||||
|
||||
private async _handleServer(payloadRaw: RPCStateRaw) {
|
||||
try {
|
||||
parse(payloadRaw)
|
||||
} catch (e) {
|
||||
throw new Error(RPCErrors.INVALID_DATA)
|
||||
}
|
||||
const payload = parse(payloadRaw)
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:client:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.type === 'event') {
|
||||
if (payload.calledTo === 'client') {
|
||||
this.verifyEvent(this._emitterServer, payload)
|
||||
|
||||
const responseData = await this._emitterServer.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'client',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_CLIENT, stringify(response))
|
||||
}
|
||||
if (payload.calledTo === 'webview') {
|
||||
this._sendWebMessage({
|
||||
origin: RPCEvents.LISTENER_SERVER,
|
||||
data: payload,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (payload.type === 'response') {
|
||||
if (payload.calledTo === 'client') {
|
||||
await this._pendingServer.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
if (payload.calledTo === 'webview') {
|
||||
await this._pendingWebToServer.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleWeb(payload: RPCState): Promise<unknown> {
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:client:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.type === 'event') {
|
||||
if (payload.calledTo === 'client') {
|
||||
return await this._emitterWeb.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
if (payload.calledTo === 'server') {
|
||||
payload.player = GetPlayerServerId(PlayerId())
|
||||
emitNet(RPCEvents.LISTENER_WEB, stringify(payload))
|
||||
|
||||
return new Promise(res => {
|
||||
this._pendingWebToServer.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.type === 'response') {
|
||||
if (payload.calledTo === 'client') {
|
||||
await this._pendingWeb.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
return { status: 'ok' }
|
||||
}
|
||||
if (payload.calledTo === 'server') {
|
||||
payload.player = GetPlayerServerId(PlayerId())
|
||||
emitNet(RPCEvents.LISTENER_WEB, stringify(payload))
|
||||
|
||||
return { status: 'ok' }
|
||||
}
|
||||
}
|
||||
return { status: 'unknown' }
|
||||
}
|
||||
|
||||
// ===== SERVER =====
|
||||
|
||||
public onServer<
|
||||
EventName extends keyof s.RPCEvents_ServerClient,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ServerClient[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ServerClient[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offServer<EventName extends keyof s.RPCEvents_ServerClient>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitServer<
|
||||
EventName extends keyof s.RPCEvents_ClientServer,
|
||||
Arguments extends Parameters<s.RPCEvents_ClientServer[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ClientServer[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'client',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: GetPlayerServerId(PlayerId()),
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_CLIENT, stringify(payload))
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingServer.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
// ===== WEBVIEW =====
|
||||
|
||||
public onWebview<
|
||||
EventName extends keyof s.RPCEvents_WebviewClient,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_WebviewClient[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_WebviewClient[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offWebview<EventName extends keyof s.RPCEvents_WebviewClient>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitWebview<
|
||||
EventName extends keyof s.RPCEvents_ClientWebview,
|
||||
Arguments extends Parameters<s.RPCEvents_ClientWebview[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ClientWebview[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'client',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: PlayerId(),
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
this._sendWebMessage({
|
||||
origin: RPCEvents.LISTENER_CLIENT,
|
||||
data: payload,
|
||||
})
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingWeb.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
// ===== SELF =====
|
||||
|
||||
public onSelf<
|
||||
EventName extends keyof s.RPCEvents_Client,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_Client[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_Client[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offSelf<EventName extends keyof s.RPCEvents_Client>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitSelf<
|
||||
EventName extends keyof s.RPCEvents_Client,
|
||||
Arguments extends Parameters<s.RPCEvents_Client[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_Client[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'client',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:accepted ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
this.verifyEvent(this._emitterLocal, payload)
|
||||
|
||||
return await this._emitterLocal.emit<Awaited<Response>>(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
|
||||
// ===== OTHER =====
|
||||
|
||||
public onCommand<
|
||||
CommandName extends s.RPCCommands_Client,
|
||||
CallbackArguments extends unknown[],
|
||||
>(
|
||||
command: CommandName,
|
||||
cb: (player: number, args: CallbackArguments, commandRaw: string) => void,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onCommand ${command}`)
|
||||
}
|
||||
|
||||
RegisterCommand(command, cb, false)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public onNativeEvent<
|
||||
EventName extends keyof RPCNativeClientEvents,
|
||||
CallbackArguments extends Parameters<RPCNativeClientEvents[EventName]>,
|
||||
>(eventName: EventName, cb: (...args: CallbackArguments) => void): this {
|
||||
if (!NATIVE_CLIENT_EVENTS.includes(eventName)) {
|
||||
throw new Error(RPCErrors.UNKNOWN_NATIVE)
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onNativeEvent ${eventName}`)
|
||||
}
|
||||
|
||||
on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public onNativeNetworkEvent<
|
||||
EventName extends keyof RPCNativeClientNetworksEvents,
|
||||
CallbackArguments extends Parameters<
|
||||
RPCNativeClientNetworksEvents[EventName]
|
||||
>,
|
||||
>(eventName: EventName, cb: (...args: CallbackArguments) => void): this {
|
||||
if (!NATIVE_CLIENT_NETWORK_EVENTS.includes(eventName)) {
|
||||
throw new Error(RPCErrors.UNKNOWN_NATIVE)
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onNativeNetworkEvent ${eventName}`)
|
||||
}
|
||||
|
||||
on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public setWebviewFocus(hasFocus: boolean, hasCursor: boolean): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:setWebviewFocus ${hasFocus} ${hasCursor}`)
|
||||
}
|
||||
|
||||
SetNuiFocus(hasFocus, hasCursor)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// ===== UTILS =====
|
||||
|
||||
private _sendWebMessage(payload: RPCStateWeb): void {
|
||||
SendNuiMessage(stringifyWeb(payload))
|
||||
}
|
||||
}
|
379
rpc/src/core/server.ts
Normal file
379
rpc/src/core/server.ts
Normal file
@ -0,0 +1,379 @@
|
||||
/// <reference types="@citizenfx/server" />
|
||||
|
||||
import type * as s from '@entityseven/fivem-rpc-shared-types'
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { generateUUID, parse, stringify } from '../utils/funcs'
|
||||
import { NATIVE_SERVER_EVENTS } from '../utils/native'
|
||||
import {
|
||||
type RPCConfig,
|
||||
RPCErrors,
|
||||
RPCEvents,
|
||||
type RPCNativeServerEvents,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
} from '../utils/types'
|
||||
import { Wrapper } from './wrapper'
|
||||
|
||||
export class RPCInstanceServer extends Wrapper {
|
||||
private readonly _emitterClient: Emitter
|
||||
private readonly _pendingClient: Emitter
|
||||
private readonly _emitterWeb: Emitter
|
||||
private readonly _pendingWeb: Emitter
|
||||
|
||||
constructor(props: RPCConfig<'server'>) {
|
||||
super(props)
|
||||
|
||||
this._emitterClient = new Emitter()
|
||||
this._pendingClient = new Emitter()
|
||||
this._emitterWeb = new Emitter()
|
||||
this._pendingWeb = new Emitter()
|
||||
|
||||
this.console.log('[RPC] Initialized Server')
|
||||
|
||||
onNet(RPCEvents.LISTENER_CLIENT, this._handleClient.bind(this))
|
||||
onNet(RPCEvents.LISTENER_WEB, this._handleWeb.bind(this))
|
||||
}
|
||||
|
||||
// ===== HANDLERS =====
|
||||
|
||||
private async _handleClient(payloadRaw: RPCStateRaw) {
|
||||
try {
|
||||
parse(payloadRaw)
|
||||
} catch (e) {
|
||||
throw new Error(RPCErrors.INVALID_DATA)
|
||||
}
|
||||
const payload = parse(payloadRaw)
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:server:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'client') {
|
||||
if (payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterClient, payload)
|
||||
if (payload.player === null || payload.player === -1) {
|
||||
payload.error = RPCErrors.NO_PLAYER
|
||||
this.triggerError(payload)
|
||||
return
|
||||
}
|
||||
|
||||
const responseData = await this._emitterClient.emit(
|
||||
payload.event,
|
||||
payload.player,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'server',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, response.player, stringify(response))
|
||||
}
|
||||
if (payload.type === 'response') {
|
||||
await this._pendingClient.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleWeb(payloadRaw: RPCStateRaw) {
|
||||
try {
|
||||
parse(payloadRaw)
|
||||
} catch (e) {
|
||||
throw new Error(RPCErrors.INVALID_DATA)
|
||||
}
|
||||
const payload = parse(payloadRaw)
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:server:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'webview') {
|
||||
if (payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterWeb, payload)
|
||||
if (payload.player === null || payload.player === -1) {
|
||||
payload.error = RPCErrors.NO_PLAYER
|
||||
this.triggerError(payload)
|
||||
return
|
||||
}
|
||||
|
||||
const responseData = await this._emitterWeb.emit(
|
||||
payload.event,
|
||||
payload.player,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'server',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, response.player, stringify(response))
|
||||
}
|
||||
if (payload.type === 'response') {
|
||||
await this._pendingWeb.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CLIENT =====
|
||||
|
||||
public onClient<
|
||||
EventName extends keyof s.RPCEvents_ClientServer,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ClientServer[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ClientServer[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
player: number,
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offClient<EventName extends keyof s.RPCEvents_ClientServer>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitClient<
|
||||
EventName extends keyof s.RPCEvents_ServerClient,
|
||||
Arguments extends Parameters<s.RPCEvents_ServerClient[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ServerClient[EventName]>,
|
||||
>(
|
||||
player: number,
|
||||
eventName: EventName,
|
||||
...args: Arguments
|
||||
): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: player,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, player, stringify(payload))
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingClient.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
public async emitClientEveryone<
|
||||
EventName extends keyof s.RPCEvents_ServerClient,
|
||||
Arguments extends Parameters<s.RPCEvents_ServerClient[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<void> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: -1,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, -1, stringify(payload))
|
||||
}
|
||||
|
||||
// ===== WEBVIEW =====
|
||||
|
||||
public onWebview<
|
||||
EventName extends keyof s.RPCEvents_WebviewServer,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_WebviewServer[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_WebviewServer[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
player: number,
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offWebview<EventName extends keyof s.RPCEvents_WebviewServer>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitWebview<
|
||||
EventName extends keyof s.RPCEvents_ServerWebview,
|
||||
Arguments extends Parameters<s.RPCEvents_ServerWebview[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ServerWebview[EventName]>,
|
||||
>(
|
||||
player: number,
|
||||
eventName: EventName,
|
||||
...args: Arguments
|
||||
): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: player,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, player, stringify(payload))
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingWeb.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
// ===== SELF =====
|
||||
|
||||
public onSelf<
|
||||
EventName extends keyof s.RPCEvents_Server,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_Server[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_Server[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offSelf<EventName extends keyof s.RPCEvents_Server>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitSelf<
|
||||
EventName extends keyof s.RPCEvents_Server,
|
||||
Arguments extends Parameters<s.RPCEvents_Server[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_Server[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:accepted ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
this.verifyEvent(this._emitterLocal, payload)
|
||||
|
||||
return await this._emitterLocal.emit<Awaited<Response>>(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
|
||||
// ===== OTHER =====
|
||||
|
||||
public onCommand<
|
||||
CommandName extends s.RPCCommands_Server,
|
||||
CallbackArguments extends unknown[],
|
||||
>(
|
||||
command: CommandName,
|
||||
cb: (player: number, args: CallbackArguments, commandRaw: string) => void,
|
||||
restricted = false,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onCommand ${command}`)
|
||||
}
|
||||
|
||||
RegisterCommand(command, cb, restricted)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public onNativeEvent<
|
||||
EventName extends keyof RPCNativeServerEvents,
|
||||
CallbackArguments extends Parameters<RPCNativeServerEvents[EventName]>,
|
||||
>(eventName: EventName, cb: (...args: CallbackArguments) => void): this {
|
||||
if (!NATIVE_SERVER_EVENTS.includes(eventName)) {
|
||||
throw new Error(RPCErrors.UNKNOWN_NATIVE)
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onNativeEvent ${eventName}`)
|
||||
}
|
||||
|
||||
on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
289
rpc/src/core/webview.ts
Normal file
289
rpc/src/core/webview.ts
Normal file
@ -0,0 +1,289 @@
|
||||
import type * as s from '@entityseven/fivem-rpc-shared-types'
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { generateUUID, stringify } from '../utils/funcs'
|
||||
import {
|
||||
RPCEvents,
|
||||
type RPCConfig,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
type RPCStateWeb,
|
||||
} from '../utils/types'
|
||||
import { Wrapper } from './wrapper'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
GetParentResourceName?: () => string
|
||||
}
|
||||
}
|
||||
|
||||
export class RPCInstanceWebview extends Wrapper {
|
||||
private readonly _emitterClient: Emitter
|
||||
private readonly _emitterServer: Emitter
|
||||
|
||||
constructor(props: RPCConfig<'webview'>) {
|
||||
super(props)
|
||||
|
||||
this._emitterClient = new Emitter()
|
||||
this._emitterServer = new Emitter()
|
||||
|
||||
this.console.log('[RPC] Initialized Webview')
|
||||
|
||||
window.addEventListener('message', (e: MessageEvent<RPCStateWeb>) => {
|
||||
if (e.data.origin === RPCEvents.LISTENER_CLIENT) {
|
||||
this._handleClient(e.data.data)
|
||||
}
|
||||
if (e.data.origin === RPCEvents.LISTENER_SERVER) {
|
||||
this._handleServer(e.data.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ===== HANDLERS =====
|
||||
|
||||
private async _handleClient(payload: RPCState) {
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:webview:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'client' && payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterClient, payload)
|
||||
|
||||
const responseData = await this._emitterClient.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
await this._createHttpClientRequest(response).then()
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleServer(payload: RPCState) {
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:webview:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'server' && payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterServer, payload)
|
||||
|
||||
const responseData = await this._emitterServer.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
await this._createHttpClientRequest(response)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CLIENT =====
|
||||
|
||||
public onClient<
|
||||
EventName extends keyof s.RPCEvents_ClientWebview,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ClientWebview[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ClientWebview[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offClient<EventName extends keyof s.RPCEvents_ClientWebview>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitClient<
|
||||
EventName extends keyof s.RPCEvents_WebviewClient,
|
||||
Arguments extends Parameters<s.RPCEvents_WebviewClient[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_WebviewClient[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
return await this._createHttpClientRequest<Awaited<Response>>(payload)
|
||||
}
|
||||
|
||||
// ===== SERVER =====
|
||||
|
||||
public onServer<
|
||||
EventName extends keyof s.RPCEvents_ServerWebview,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ServerWebview[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ServerWebview[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offServer<EventName extends keyof s.RPCEvents_ServerWebview>(
|
||||
eventName: EventName,
|
||||
): RPCInstanceWebview {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitServer<
|
||||
EventName extends keyof s.RPCEvents_WebviewServer,
|
||||
Arguments extends Parameters<s.RPCEvents_WebviewServer[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_WebviewServer[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
return await this._createHttpClientRequest<Awaited<Response>>(payload)
|
||||
}
|
||||
|
||||
// ===== SELF =====
|
||||
|
||||
public onSelf<
|
||||
EventName extends keyof s.RPCEvents_Webview,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_Webview[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_Webview[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offSelf<EventName extends keyof s.RPCEvents_Webview>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitSelf<
|
||||
EventName extends keyof s.RPCEvents_Webview,
|
||||
Arguments extends Parameters<s.RPCEvents_Webview[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_Webview[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:accepted ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
this.verifyEvent(this._emitterLocal, payload)
|
||||
|
||||
return await this._emitterLocal.emit<Awaited<Response>>(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
|
||||
// ===== UTILS =====
|
||||
|
||||
private async _createHttpClientRequest<R>(
|
||||
data: RPCStateRaw | RPCState,
|
||||
): Promise<R> {
|
||||
const dataRaw = typeof data === 'string' ? data : stringify(data)
|
||||
const options = {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
},
|
||||
body: dataRaw,
|
||||
}
|
||||
const resourceName = window?.GetParentResourceName?.() ?? 'nui-frame-app'
|
||||
return fetch(
|
||||
`https://${resourceName}/${RPCEvents.LISTENER_WEB}`,
|
||||
options,
|
||||
).then(res => res.json())
|
||||
}
|
||||
}
|
51
rpc/src/core/wrapper.ts
Normal file
51
rpc/src/core/wrapper.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { parse } from '../utils/funcs'
|
||||
import {
|
||||
type RPCConfig,
|
||||
type RPCEnvironment,
|
||||
RPCErrors,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
} from '../utils/types'
|
||||
|
||||
export class Wrapper {
|
||||
protected env: RPCEnvironment
|
||||
protected _emitterLocal: Emitter
|
||||
protected debug: boolean
|
||||
protected console: Console
|
||||
|
||||
constructor(cfg: RPCConfig<RPCEnvironment>) {
|
||||
this.env = cfg.env
|
||||
this._emitterLocal = new Emitter()
|
||||
this.debug = cfg.debug ?? false
|
||||
this.console = console
|
||||
}
|
||||
|
||||
protected verifyEvent(state: Emitter, data: RPCStateRaw | RPCState) {
|
||||
const rpcData = typeof data === 'string' ? parse(data) : data
|
||||
|
||||
if (!state.has(rpcData.event)) {
|
||||
rpcData.error = RPCErrors.EVENT_NOT_REGISTERED
|
||||
this.triggerError(rpcData)
|
||||
}
|
||||
}
|
||||
|
||||
protected triggerError(rpcData: RPCState, error?: string): Error {
|
||||
const errorMessage = [
|
||||
`${rpcData.error}`,
|
||||
`Event: ${rpcData.event}`,
|
||||
`Uuid: ${rpcData.uuid}`,
|
||||
`From: ${rpcData.calledFrom}`,
|
||||
`To: ${rpcData.calledTo}`,
|
||||
`Player: ${rpcData.player}`,
|
||||
`Type: ${rpcData.type}`,
|
||||
`Data: ${rpcData.data}`,
|
||||
]
|
||||
|
||||
if (error) {
|
||||
errorMessage.push(`Info: ${error}`)
|
||||
}
|
||||
|
||||
throw new Error(errorMessage.join('\n | '))
|
||||
}
|
||||
}
|
74
rpc/src/index.ts
Normal file
74
rpc/src/index.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { RPCInstanceClient } from './core/client'
|
||||
import { RPCInstanceServer } from './core/server'
|
||||
import { RPCInstanceWebview } from './core/webview'
|
||||
import { Wrapper } from './core/wrapper'
|
||||
import {
|
||||
type RPCConfig,
|
||||
type RPCEnvironment,
|
||||
type RPCEnvironmentResolved,
|
||||
RPCErrors,
|
||||
} from './utils/types'
|
||||
|
||||
/**
|
||||
* RPC Factory
|
||||
*
|
||||
* @example
|
||||
* // returns RPCInstanceServer
|
||||
* const rpc = new RPCFactory({ env: "server" }).get()
|
||||
*
|
||||
* @example
|
||||
* // returns RPCInstanceClient
|
||||
* const rpc = new RPCFactory({ env: "client" }).get()
|
||||
*
|
||||
* @example
|
||||
* // returns RPCInstanceWebview
|
||||
* const rpc = new RPCFactory({ env: "webview" }).get()
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class RPCFactory<T extends RPCEnvironment> extends Wrapper {
|
||||
private readonly operator:
|
||||
| RPCInstanceServer
|
||||
| RPCInstanceClient
|
||||
| RPCInstanceWebview
|
||||
|
||||
/**
|
||||
* Instance options
|
||||
* @param {object} opts - Options
|
||||
* @param {string} opts.env - Instance environment
|
||||
* @param {boolean} opts.debug - Show additional logs
|
||||
*/
|
||||
constructor(opts: RPCConfig<T>) {
|
||||
super(opts)
|
||||
|
||||
this.console.log('[RPC] Initializing...')
|
||||
|
||||
switch (opts.env) {
|
||||
case 'server':
|
||||
this.operator = new RPCInstanceServer(opts as RPCConfig<'server'>)
|
||||
break
|
||||
case 'client':
|
||||
this.operator = new RPCInstanceClient(opts as RPCConfig<'client'>)
|
||||
break
|
||||
case 'webview':
|
||||
this.operator = new RPCInstanceWebview(opts as RPCConfig<'webview'>)
|
||||
break
|
||||
default:
|
||||
throw new Error(RPCErrors.UNKNOWN_ENVIRONMENT)
|
||||
}
|
||||
}
|
||||
|
||||
public get(): RPCEnvironmentResolved<T> {
|
||||
return this.operator as RPCEnvironmentResolved<T>
|
||||
}
|
||||
}
|
||||
|
||||
export { RPCFactory }
|
||||
// export const rpcClient = new RPCFactory({ env: "client" }).get();
|
||||
// export const rpcServer = new RPCFactory({ env: "server" }).get();
|
||||
// export const rpcWebview = new RPCFactory({ env: "webview" }).get();
|
||||
export * from './utils/types'
|
||||
export * from './utils/native'
|
||||
export type * from './core/server'
|
||||
export type * from './core/client'
|
||||
export type * from './core/webview'
|
54
rpc/src/utils/emitter.ts
Normal file
54
rpc/src/utils/emitter.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { RPCErrors } from './types'
|
||||
|
||||
export class Emitter {
|
||||
/** Map<event, [callback function, once]> */
|
||||
private _storage: Map<string, [(...args: any[]) => any, boolean]>
|
||||
|
||||
constructor() {
|
||||
this._storage = new Map()
|
||||
}
|
||||
|
||||
get _raw_storage() {
|
||||
return this._storage
|
||||
}
|
||||
|
||||
public on(event: string, cb: (...args: any[]) => any): this {
|
||||
this._storage.set(event, [cb, false])
|
||||
return this
|
||||
}
|
||||
|
||||
public once(event: string, cb: (...args: any[]) => any): this {
|
||||
this._storage.set(event, [cb, true])
|
||||
return this
|
||||
}
|
||||
|
||||
public off(event: string): this {
|
||||
this._storage.delete(event)
|
||||
return this
|
||||
}
|
||||
|
||||
public has(event: string): boolean {
|
||||
return this._storage.has(event)
|
||||
}
|
||||
|
||||
public async emit<R>(event: string, ...args: any[]): Promise<R> {
|
||||
return new Promise((res, rej) => {
|
||||
if (!this._storage.has(event)) {
|
||||
rej(RPCErrors.EVENT_NOT_REGISTERED)
|
||||
}
|
||||
|
||||
const [cb, once] = this._storage.get(event) as [
|
||||
(...args: any[]) => any,
|
||||
boolean,
|
||||
]
|
||||
|
||||
if (once) {
|
||||
this._storage.delete(event)
|
||||
}
|
||||
|
||||
Promise.resolve(cb(...args))
|
||||
.then(res)
|
||||
.catch(rej)
|
||||
})
|
||||
}
|
||||
}
|
50
rpc/src/utils/funcs.ts
Normal file
50
rpc/src/utils/funcs.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import type {
|
||||
RPCState,
|
||||
RPCStateRaw,
|
||||
RPCStateWeb,
|
||||
RPCStateWebRaw,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Typed data parser
|
||||
*/
|
||||
export function parse(data: RPCStateRaw): RPCState {
|
||||
return JSON.parse(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Typed data serializer
|
||||
*/
|
||||
export function stringify(data: RPCState): RPCStateRaw {
|
||||
return JSON.stringify(data) as RPCStateRaw
|
||||
}
|
||||
|
||||
// automatically parsed by FiveM
|
||||
// export function parseWeb(data: RPCStateWebRaw): RPCStateWeb {
|
||||
// return JSON.parse(data)
|
||||
// }
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Typed data serializer
|
||||
*/
|
||||
export function stringifyWeb(data: RPCStateWeb): RPCStateWebRaw {
|
||||
return JSON.stringify(data) as RPCStateWebRaw
|
||||
}
|
||||
|
||||
/** **Internal** */
|
||||
export function generateUUID(): string {
|
||||
let uuid = ''
|
||||
let random = 0
|
||||
for (let i = 0; i < 32; i++) {
|
||||
random = (Math.random() * 16) | 0
|
||||
if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-'
|
||||
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16)
|
||||
}
|
||||
return uuid
|
||||
}
|
321
rpc/src/utils/native.ts
Normal file
321
rpc/src/utils/native.ts
Normal file
@ -0,0 +1,321 @@
|
||||
import type {
|
||||
RPCNativeClientEvents,
|
||||
RPCNativeClientNetworkEventsNames,
|
||||
RPCNativeServerEvents,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/server-events/
|
||||
* @readonly
|
||||
*/
|
||||
export const NATIVE_SERVER_EVENTS: readonly (keyof RPCNativeServerEvents)[] = [
|
||||
'entityCreated',
|
||||
'entityCreating',
|
||||
'entityRemoved',
|
||||
'onResourceListRefresh',
|
||||
'onResourceStart',
|
||||
'onResourceStarting',
|
||||
'onResourceStop',
|
||||
'onServerResourceStart',
|
||||
'onServerResourceStop',
|
||||
'playerConnecting',
|
||||
'playerEnteredScope',
|
||||
'playerJoining',
|
||||
'playerLeftScope',
|
||||
'ptFxEvent',
|
||||
'removeAllWeaponsEvent',
|
||||
'startProjectileEvent',
|
||||
'weaponDamageEvent',
|
||||
] as const
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/client-events/
|
||||
* @readonly
|
||||
*/
|
||||
export const NATIVE_CLIENT_EVENTS: readonly (keyof RPCNativeClientEvents)[] = [
|
||||
'entityDamaged',
|
||||
'gameEventTriggered',
|
||||
'mumbleConnected',
|
||||
'mumbleDisconnected',
|
||||
'onClientResourceStart',
|
||||
'onClientResourceStop',
|
||||
'onResourceStart',
|
||||
'onResourceStarting',
|
||||
'onResourceStop',
|
||||
'populationPedCreating',
|
||||
] as const
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/game-references/game-events/
|
||||
* @readonly
|
||||
*/
|
||||
export const NATIVE_CLIENT_NETWORK_EVENTS: readonly RPCNativeClientNetworkEventsNames[] =
|
||||
[
|
||||
'CEventAcquaintancePed',
|
||||
'CEventAcquaintancePedDead',
|
||||
'CEventAcquaintancePedDislike',
|
||||
'CEventAcquaintancePedHate',
|
||||
'CEventAcquaintancePedLike',
|
||||
'CEventAcquaintancePedWanted',
|
||||
'CEventAgitated',
|
||||
'CEventAgitatedAction',
|
||||
'CEventCallForCover',
|
||||
'CEventCarUndriveable',
|
||||
'CEventClimbLadderOnRoute',
|
||||
'CEventClimbNavMeshOnRoute',
|
||||
'CEventCombatTaunt',
|
||||
'CEventCommunicateEvent',
|
||||
'CEventCopCarBeingStolen',
|
||||
'CEventCrimeCryForHelp',
|
||||
'CEventCrimeReported',
|
||||
'CEventDamage',
|
||||
'CEventDataDecisionMaker',
|
||||
'CEventDataFileMounter',
|
||||
'CEventDataResponseAggressiveRubberneck',
|
||||
'CEventDataResponseDeferToScenarioPointFlags',
|
||||
'CEventDataResponseFriendlyAimedAt',
|
||||
'CEventDataResponseFriendlyNearMiss',
|
||||
'CEventDataResponsePlayerDeath',
|
||||
'CEventDataResponsePoliceTaskWanted',
|
||||
'CEventDataResponseSwatTaskWanted',
|
||||
'CEventDataResponseTask',
|
||||
'CEventDataResponseTaskAgitated',
|
||||
'CEventDataResponseTaskCombat',
|
||||
'CEventDataResponseTaskCower',
|
||||
'CEventDataResponseTaskCrouch',
|
||||
'CEventDataResponseTaskDuckAndCover',
|
||||
'CEventDataResponseTaskEscapeBlast',
|
||||
'CEventDataResponseTaskEvasiveStep',
|
||||
'CEventDataResponseTaskExhaustedFlee',
|
||||
'CEventDataResponseTaskExplosion',
|
||||
'CEventDataResponseTaskFlee',
|
||||
'CEventDataResponseTaskFlyAway',
|
||||
'CEventDataResponseTaskGrowlAndFlee',
|
||||
'CEventDataResponseTaskGunAimedAt',
|
||||
'CEventDataResponseTaskHandsUp',
|
||||
'CEventDataResponseTaskHeadTrack',
|
||||
'CEventDataResponseTaskLeaveCarAndFlee',
|
||||
'CEventDataResponseTaskScenarioFlee',
|
||||
'CEventDataResponseTaskSharkAttack',
|
||||
'CEventDataResponseTaskShockingEventBackAway',
|
||||
'CEventDataResponseTaskShockingEventGoto',
|
||||
'CEventDataResponseTaskShockingEventHurryAway',
|
||||
'CEventDataResponseTaskShockingEventReact',
|
||||
'CEventDataResponseTaskShockingEventReactToAircraft',
|
||||
'CEventDataResponseTaskShockingEventStopAndStare',
|
||||
'CEventDataResponseTaskShockingEventThreatResponse',
|
||||
'CEventDataResponseTaskShockingEventWatch',
|
||||
'CEventDataResponseTaskShockingNiceCar',
|
||||
'CEventDataResponseTaskShockingPoliceInvestigate',
|
||||
'CEventDataResponseTaskThreat',
|
||||
'CEventDataResponseTaskTurnToFace',
|
||||
'CEventDataResponseTaskWalkAway',
|
||||
'CEventDataResponseTaskWalkRoundEntity',
|
||||
'CEventDataResponseTaskWalkRoundFire',
|
||||
'CEventDeadPedFound',
|
||||
'CEventDeath',
|
||||
'CEventDecisionMakerResponse',
|
||||
'CEventDisturbance',
|
||||
'CEventDraggedOutCar',
|
||||
'CEventEditableResponse',
|
||||
'CEventEncroachingPed',
|
||||
'CEventEntityDamaged',
|
||||
'CEventEntityDestroyed',
|
||||
'CEventExplosion',
|
||||
'CEventExplosionHeard',
|
||||
'CEventFireNearby',
|
||||
'CEventFootStepHeard',
|
||||
'CEventFriendlyAimedAt',
|
||||
'CEventFriendlyFireNearMiss',
|
||||
'CEventGetOutOfWater',
|
||||
'CEventGivePedTask',
|
||||
'CEventGroupScriptAI',
|
||||
'CEventGroupScriptNetwork',
|
||||
'CEventGunAimedAt',
|
||||
'CEventGunShot',
|
||||
'CEventGunShotBulletImpact',
|
||||
'CEventGunShotWhizzedBy',
|
||||
'CEventHelpAmbientFriend',
|
||||
'CEventHurtTransition',
|
||||
'CEventInAir',
|
||||
'CEventInfo',
|
||||
'CEventInfoBase',
|
||||
'CEventInjuredCryForHelp',
|
||||
'CEventLeaderEnteredCarAsDriver',
|
||||
'CEventLeaderExitedCarAsDriver',
|
||||
'CEventLeaderHolsteredWeapon',
|
||||
'CEventLeaderLeftCover',
|
||||
'CEventLeaderUnholsteredWeapon',
|
||||
'CEventMeleeAction',
|
||||
'CEventMustLeaveBoat',
|
||||
'CEventNetworkAdminInvited',
|
||||
'CEventNetworkAttemptHostMigration',
|
||||
'CEventNetworkBail',
|
||||
'CEventNetworkCashTransactionLog',
|
||||
'CEventNetworkCheatTriggered',
|
||||
'CEventNetworkClanInviteReceived',
|
||||
'CEventNetworkClanJoined',
|
||||
'CEventNetworkClanKicked',
|
||||
'CEventNetworkClanLeft',
|
||||
'CEventNetworkClanRankChanged',
|
||||
'CEventNetworkCloudEvent',
|
||||
'CEventNetworkCloudFileResponse',
|
||||
'CEventNetworkEmailReceivedEvent',
|
||||
'CEventNetworkEndMatch',
|
||||
'CEventNetworkEndSession',
|
||||
'CEventNetworkEntityDamage',
|
||||
'CEventNetworkFindSession',
|
||||
'CEventNetworkFollowInviteReceived',
|
||||
'CEventNetworkHostMigration',
|
||||
'CEventNetworkHostSession',
|
||||
'CEventNetworkIncrementStat',
|
||||
'CEventNetworkInviteAccepted',
|
||||
'CEventNetworkInviteConfirmed',
|
||||
'CEventNetworkInviteRejected',
|
||||
'CEventNetworkJoinSession',
|
||||
'CEventNetworkJoinSessionResponse',
|
||||
'CEventNetworkOnlinePermissionsUpdated',
|
||||
'CEventNetworkPedLeftBehind',
|
||||
'CEventNetworkPickupRespawned',
|
||||
'CEventNetworkPlayerArrest',
|
||||
'CEventNetworkPlayerCollectedAmbientPickup',
|
||||
'CEventNetworkPlayerCollectedPickup',
|
||||
'CEventNetworkPlayerCollectedPortablePickup',
|
||||
'CEventNetworkPlayerDroppedPortablePickup',
|
||||
'CEventNetworkPlayerEnteredVehicle',
|
||||
'CEventNetworkPlayerJoinScript',
|
||||
'CEventNetworkPlayerLeftScript',
|
||||
'CEventNetworkPlayerScript',
|
||||
'CEventNetworkPlayerSession',
|
||||
'CEventNetworkPlayerSpawn',
|
||||
'CEventNetworkPresenceInvite',
|
||||
'CEventNetworkPresenceInviteRemoved',
|
||||
'CEventNetworkPresenceInviteReply',
|
||||
'CEventNetworkPresenceTriggerEvent',
|
||||
'CEventNetworkPresence_StatUpdate',
|
||||
'CEventNetworkPrimaryClanChanged',
|
||||
'CEventNetworkRequestDelay',
|
||||
'CEventNetworkRosChanged',
|
||||
'CEventNetworkScAdminPlayerUpdated',
|
||||
'CEventNetworkScAdminReceivedCash',
|
||||
'CEventNetworkScriptEvent',
|
||||
'CEventNetworkSessionEvent',
|
||||
'CEventNetworkShopTransaction',
|
||||
'CEventNetworkSignInStateChanged',
|
||||
'CEventNetworkSocialClubAccountLinked',
|
||||
'CEventNetworkSpectateLocal',
|
||||
'CEventNetworkStartMatch',
|
||||
'CEventNetworkStartSession',
|
||||
'CEventNetworkStorePlayerLeft',
|
||||
'CEventNetworkSummon',
|
||||
'CEventNetworkSystemServiceEvent',
|
||||
'CEventNetworkTextMessageReceived',
|
||||
'CEventNetworkTimedExplosion',
|
||||
'CEventNetworkTransitionEvent',
|
||||
'CEventNetworkTransitionGamerInstruction',
|
||||
'CEventNetworkTransitionMemberJoined',
|
||||
'CEventNetworkTransitionMemberLeft',
|
||||
'CEventNetworkTransitionParameterChanged',
|
||||
'CEventNetworkTransitionStarted',
|
||||
'CEventNetworkTransitionStringChanged',
|
||||
'CEventNetworkVehicleUndrivable',
|
||||
'CEventNetworkVoiceConnectionRequested',
|
||||
'CEventNetworkVoiceConnectionResponse',
|
||||
'CEventNetworkVoiceConnectionTerminated',
|
||||
'CEventNetworkVoiceSessionEnded',
|
||||
'CEventNetworkVoiceSessionStarted',
|
||||
'CEventNetworkWithData',
|
||||
'CEventNetwork_InboxMsgReceived',
|
||||
'CEventNewTask',
|
||||
'CEventObjectCollision',
|
||||
'CEventOnFire',
|
||||
'CEventOpenDoor',
|
||||
'CEventPedCollisionWithPed',
|
||||
'CEventPedCollisionWithPlayer',
|
||||
'CEventPedEnteredMyVehicle',
|
||||
'CEventPedJackingMyVehicle',
|
||||
'CEventPedOnCarRoof',
|
||||
'CEventPedSeenDeadPed',
|
||||
'CEventPlayerCollisionWithPed',
|
||||
'CEventPlayerDeath',
|
||||
'CEventPlayerUnableToEnterVehicle',
|
||||
'CEventPotentialBeWalkedInto',
|
||||
'CEventPotentialBlast',
|
||||
'CEventPotentialGetRunOver',
|
||||
'CEventPotentialWalkIntoVehicle',
|
||||
'CEventProvidingCover',
|
||||
'CEventRanOverPed',
|
||||
'CEventReactionEnemyPed',
|
||||
'CEventReactionInvestigateDeadPed',
|
||||
'CEventReactionInvestigateThreat',
|
||||
'CEventRequestHelp',
|
||||
'CEventRequestHelpWithConfrontation',
|
||||
'CEventRespondedToThreat',
|
||||
'CEventScanner',
|
||||
'CEventScenarioForceAction',
|
||||
'CEventScriptCommand',
|
||||
'CEventScriptWithData',
|
||||
'CEventShocking',
|
||||
'CEventShockingBicycleCrash',
|
||||
'CEventShockingBicycleOnPavement',
|
||||
'CEventShockingCarAlarm',
|
||||
'CEventShockingCarChase',
|
||||
'CEventShockingCarCrash',
|
||||
'CEventShockingCarOnCar',
|
||||
'CEventShockingCarPileUp',
|
||||
'CEventShockingDangerousAnimal',
|
||||
'CEventShockingDeadBody',
|
||||
'CEventShockingDrivingOnPavement',
|
||||
'CEventShockingEngineRevved',
|
||||
'CEventShockingExplosion',
|
||||
'CEventShockingFire',
|
||||
'CEventShockingGunFight',
|
||||
'CEventShockingGunshotFired',
|
||||
'CEventShockingHelicopterOverhead',
|
||||
'CEventShockingHornSounded',
|
||||
'CEventShockingInDangerousVehicle',
|
||||
'CEventShockingInjuredPed',
|
||||
'CEventShockingMadDriver',
|
||||
'CEventShockingMadDriverBicycle',
|
||||
'CEventShockingMadDriverExtreme',
|
||||
'CEventShockingMugging',
|
||||
'CEventShockingNonViolentWeaponAimedAt',
|
||||
'CEventShockingParachuterOverhead',
|
||||
'CEventShockingPedKnockedIntoByPlayer',
|
||||
'CEventShockingPedRunOver',
|
||||
'CEventShockingPedShot',
|
||||
'CEventShockingPlaneFlyby',
|
||||
'CEventShockingPotentialBlast',
|
||||
'CEventShockingPropertyDamage',
|
||||
'CEventShockingRunningPed',
|
||||
'CEventShockingRunningStampede',
|
||||
'CEventShockingSeenCarStolen',
|
||||
'CEventShockingSeenConfrontation',
|
||||
'CEventShockingSeenGangFight',
|
||||
'CEventShockingSeenInsult',
|
||||
'CEventShockingSeenMeleeAction',
|
||||
'CEventShockingSeenNiceCar',
|
||||
'CEventShockingSeenPedKilled',
|
||||
'CEventShockingSiren',
|
||||
'CEventShockingStudioBomb',
|
||||
'CEventShockingVehicleTowed',
|
||||
'CEventShockingVisibleWeapon',
|
||||
'CEventShockingWeaponThreat',
|
||||
'CEventShockingWeirdPed',
|
||||
'CEventShockingWeirdPedApproaching',
|
||||
'CEventShoutBlockingLos',
|
||||
'CEventShoutTargetPosition',
|
||||
'CEventShovePed',
|
||||
'CEventSoundBase',
|
||||
'CEventStatChangedValue',
|
||||
'CEventStaticCountReachedMax',
|
||||
'CEventStuckInAir',
|
||||
'CEventSuspiciousActivity',
|
||||
'CEventSwitch2NM',
|
||||
'CEventUnidentifiedPed',
|
||||
'CEventVehicleCollision',
|
||||
'CEventVehicleDamage',
|
||||
'CEventVehicleDamageWeapon',
|
||||
'CEventVehicleOnFire',
|
||||
'CEventWrithe',
|
||||
] as const
|
535
rpc/src/utils/types.ts
Normal file
535
rpc/src/utils/types.ts
Normal file
@ -0,0 +1,535 @@
|
||||
import type { RPCInstanceClient } from '../core/client'
|
||||
import type { RPCInstanceServer } from '../core/server'
|
||||
import type { RPCInstanceWebview } from '../core/webview'
|
||||
|
||||
/**
|
||||
* Possible environment states for `RPCConfig`
|
||||
*/
|
||||
export type RPCEnvironment = 'server' | 'client' | 'webview'
|
||||
|
||||
export type RPCEnvironmentResolved<T extends RPCEnvironment> =
|
||||
T extends 'server'
|
||||
? RPCInstanceServer
|
||||
: T extends 'client'
|
||||
? RPCInstanceClient
|
||||
: T extends 'webview'
|
||||
? RPCInstanceWebview
|
||||
: never
|
||||
|
||||
/**
|
||||
* `RPCFactory` config.
|
||||
*
|
||||
* If environment does not match will throw `RPCErrors.UNKNOWN_ENVIRONMENT`
|
||||
*/
|
||||
export type RPCConfig<T extends RPCEnvironment | unknown> = {
|
||||
env: T
|
||||
debug?: boolean
|
||||
}
|
||||
|
||||
/** **Internal** */
|
||||
export type RPCEventType = 'event' | 'response'
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Similar to what Errors look like
|
||||
*/
|
||||
export type RPCState = {
|
||||
event: string
|
||||
uuid: string
|
||||
calledFrom: RPCEnvironment
|
||||
calledTo: RPCEnvironment
|
||||
error: string | null
|
||||
data: unknown[] | null
|
||||
player: number | null
|
||||
type: RPCEventType
|
||||
}
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* `JSON.stringify` version of `RPCState`. Makes TS think this is not type `string` for better dx
|
||||
*/
|
||||
export type RPCStateRaw = string & { __brand: 'RPCStateRaw' }
|
||||
|
||||
/** Internal */
|
||||
export type RPCStateWeb = {
|
||||
origin: RPCEvents
|
||||
data: RPCState
|
||||
}
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* `JSON.stringify` version of `RPCStateWeb`. Makes TS think this is not type `string` for better dx
|
||||
*/
|
||||
export type RPCStateWebRaw = string & { __brand: 'RPCWebStateRaw' }
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Do not create same listeners to avoid unexpected behaviour
|
||||
*/
|
||||
export enum RPCEvents {
|
||||
LISTENER_SERVER = '__rpc:listenerServer',
|
||||
LISTENER_CLIENT = '__rpc:listenerClient',
|
||||
LISTENER_WEB = '__rpc:listenerWeb',
|
||||
}
|
||||
|
||||
/**
|
||||
* Errors to check against
|
||||
*/
|
||||
export enum RPCErrors {
|
||||
EVENT_NOT_REGISTERED = 'Event not registered',
|
||||
INVALID_DATA = 'Invalid data (possibly broken JSON)',
|
||||
NO_PLAYER = 'No player (failed to resolve from local index)',
|
||||
UNKNOWN_NATIVE = 'Unknown native event (if you are sure this exists - use native handler)',
|
||||
UNKNOWN_ENVIRONMENT = 'Unknown environment (must be either "server", "client" or "webview")',
|
||||
}
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/server-events/
|
||||
*/
|
||||
export type RPCNativeServerEvents = {
|
||||
entityCreated(handle: number): void
|
||||
entityCreating(handle: number): void
|
||||
entityRemoved(entity: number): void
|
||||
onResourceListRefresh(): void
|
||||
onResourceStart(resource: string): void
|
||||
onResourceStarting(resource: string): void
|
||||
onResourceStop(resource: string): void
|
||||
onServerResourceStart(resource: string): void
|
||||
onServerResourceStop(resource: string): void
|
||||
playerConnecting(
|
||||
playerName: string,
|
||||
setKickReason: (reason: string) => void,
|
||||
deferrals: {
|
||||
defer: () => void
|
||||
done: (failureReason?: string) => void
|
||||
handover: (data: Record<string, unknown>) => void
|
||||
presentCard: (
|
||||
card: string | object,
|
||||
cb?: (data: unknown, rawData: string) => void,
|
||||
) => void
|
||||
update: (message: string) => void
|
||||
},
|
||||
source: number,
|
||||
): void
|
||||
playerEnteredScope(data: { for: string; player: string }): void
|
||||
playerJoining(source: string, oldID: string): void
|
||||
playerLeftScope(data: { for: string; player: string }): void
|
||||
ptFxEvent(
|
||||
sender: number,
|
||||
data: {
|
||||
assetHash: number
|
||||
axisBitset: number
|
||||
effectHash: number
|
||||
entityNetId: number
|
||||
f100: number
|
||||
f105: number
|
||||
f106: number
|
||||
f107: number
|
||||
f109: boolean
|
||||
f110: boolean
|
||||
f111: boolean
|
||||
f92: number
|
||||
isOnEntity: boolean
|
||||
offsetX: number
|
||||
offsetY: number
|
||||
offsetZ: number
|
||||
posX: number
|
||||
posY: number
|
||||
posZ: number
|
||||
rotX: number
|
||||
rotY: number
|
||||
rotZ: number
|
||||
scale: number
|
||||
},
|
||||
): void
|
||||
removeAllWeaponsEvent(sender: number, data: { pedId: number }): void
|
||||
startProjectileEvent(
|
||||
sender: number,
|
||||
data: {
|
||||
commandFireSingleBullet: boolean
|
||||
effectGroup: number
|
||||
firePositionX: number
|
||||
firePositionY: number
|
||||
firePositionZ: number
|
||||
initialPositionX: number
|
||||
initialPositionY: number
|
||||
initialPositionZ: number
|
||||
ownerId: number
|
||||
projectileHash: number
|
||||
targetEntity: number
|
||||
throwTaskSequence: number
|
||||
unk10: number
|
||||
unk11: number
|
||||
unk12: number
|
||||
unk13: number
|
||||
unk14: number
|
||||
unk15: number
|
||||
unk16: number
|
||||
unk3: number
|
||||
unk4: number
|
||||
unk5: number
|
||||
unk6: number
|
||||
unk7: number
|
||||
unk9: number
|
||||
unkX8: number
|
||||
unkY8: number
|
||||
unkZ8: number
|
||||
weaponHash: number
|
||||
},
|
||||
): void
|
||||
weaponDamageEvent(
|
||||
sender: number,
|
||||
data: {
|
||||
actionResultId: number
|
||||
actionResultName: number
|
||||
damageFlags: number
|
||||
damageTime: number
|
||||
damageType: number
|
||||
f104: number
|
||||
f112: boolean
|
||||
f112_1: number
|
||||
f120: number
|
||||
f133: boolean
|
||||
hasActionResult: boolean
|
||||
hasImpactDir: boolean
|
||||
hasVehicleData: boolean
|
||||
hitComponent: number
|
||||
hitEntityWeapon: boolean
|
||||
hitGlobalId: number
|
||||
hitGlobalIds: number[]
|
||||
hitWeaponAmmoAttachment: boolean
|
||||
impactDirX: number
|
||||
impactDirY: number
|
||||
impactDirZ: number
|
||||
isNetTargetPos: boolean
|
||||
localPosX: number
|
||||
localPosY: number
|
||||
localPosZ: number
|
||||
overrideDefaultDamage: boolean
|
||||
parentGlobalId: number
|
||||
silenced: boolean
|
||||
suspensionIndex: number
|
||||
tyreIndex: number
|
||||
weaponDamage: number
|
||||
weaponType: number
|
||||
willKill: boolean
|
||||
},
|
||||
): void
|
||||
}
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/client-events/
|
||||
*/
|
||||
export type RPCNativeClientEvents = {
|
||||
entityDamaged(
|
||||
victim: number,
|
||||
culprit: number,
|
||||
weapon: number,
|
||||
baseDamage: number,
|
||||
): void
|
||||
gameEventTriggered(
|
||||
name: RPCNativeClientNetworksEvents | string,
|
||||
data: number[],
|
||||
): void
|
||||
mumbleConnected(address: string, reconnecting: boolean): void
|
||||
mumbleDisconnected(address: string): void
|
||||
onClientResourceStart(resource: string): void
|
||||
onClientResourceStop(resource: string): void
|
||||
onResourceStart(resource: string): void
|
||||
onResourceStarting(resource: string): void
|
||||
onResourceStop(resource: string): void
|
||||
populationPedCreating(
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
model: number,
|
||||
overrideCalls: {
|
||||
setModel: (model: string | number) => void
|
||||
setPosition: (x: number, y: number, z: number) => void
|
||||
},
|
||||
): void
|
||||
}
|
||||
|
||||
export type RPCNativeClientNetworksEvents = {
|
||||
[name in RPCNativeClientNetworkEventsNames]: (
|
||||
entities: number[],
|
||||
eventEntity: number,
|
||||
data: unknown[],
|
||||
) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/game-references/game-events/
|
||||
*/
|
||||
export type RPCNativeClientNetworkEventsNames =
|
||||
| 'CEventAcquaintancePed'
|
||||
| 'CEventAcquaintancePedDead'
|
||||
| 'CEventAcquaintancePedDislike'
|
||||
| 'CEventAcquaintancePedHate'
|
||||
| 'CEventAcquaintancePedLike'
|
||||
| 'CEventAcquaintancePedWanted'
|
||||
| 'CEventAgitated'
|
||||
| 'CEventAgitatedAction'
|
||||
| 'CEventCallForCover'
|
||||
| 'CEventCarUndriveable'
|
||||
| 'CEventClimbLadderOnRoute'
|
||||
| 'CEventClimbNavMeshOnRoute'
|
||||
| 'CEventCombatTaunt'
|
||||
| 'CEventCommunicateEvent'
|
||||
| 'CEventCopCarBeingStolen'
|
||||
| 'CEventCrimeCryForHelp'
|
||||
| 'CEventCrimeReported'
|
||||
| 'CEventDamage'
|
||||
| 'CEventDataDecisionMaker'
|
||||
| 'CEventDataFileMounter'
|
||||
| 'CEventDataResponseAggressiveRubberneck'
|
||||
| 'CEventDataResponseDeferToScenarioPointFlags'
|
||||
| 'CEventDataResponseFriendlyAimedAt'
|
||||
| 'CEventDataResponseFriendlyNearMiss'
|
||||
| 'CEventDataResponsePlayerDeath'
|
||||
| 'CEventDataResponsePoliceTaskWanted'
|
||||
| 'CEventDataResponseSwatTaskWanted'
|
||||
| 'CEventDataResponseTask'
|
||||
| 'CEventDataResponseTaskAgitated'
|
||||
| 'CEventDataResponseTaskCombat'
|
||||
| 'CEventDataResponseTaskCower'
|
||||
| 'CEventDataResponseTaskCrouch'
|
||||
| 'CEventDataResponseTaskDuckAndCover'
|
||||
| 'CEventDataResponseTaskEscapeBlast'
|
||||
| 'CEventDataResponseTaskEvasiveStep'
|
||||
| 'CEventDataResponseTaskExhaustedFlee'
|
||||
| 'CEventDataResponseTaskExplosion'
|
||||
| 'CEventDataResponseTaskFlee'
|
||||
| 'CEventDataResponseTaskFlyAway'
|
||||
| 'CEventDataResponseTaskGrowlAndFlee'
|
||||
| 'CEventDataResponseTaskGunAimedAt'
|
||||
| 'CEventDataResponseTaskHandsUp'
|
||||
| 'CEventDataResponseTaskHeadTrack'
|
||||
| 'CEventDataResponseTaskLeaveCarAndFlee'
|
||||
| 'CEventDataResponseTaskScenarioFlee'
|
||||
| 'CEventDataResponseTaskSharkAttack'
|
||||
| 'CEventDataResponseTaskShockingEventBackAway'
|
||||
| 'CEventDataResponseTaskShockingEventGoto'
|
||||
| 'CEventDataResponseTaskShockingEventHurryAway'
|
||||
| 'CEventDataResponseTaskShockingEventReact'
|
||||
| 'CEventDataResponseTaskShockingEventReactToAircraft'
|
||||
| 'CEventDataResponseTaskShockingEventStopAndStare'
|
||||
| 'CEventDataResponseTaskShockingEventThreatResponse'
|
||||
| 'CEventDataResponseTaskShockingEventWatch'
|
||||
| 'CEventDataResponseTaskShockingNiceCar'
|
||||
| 'CEventDataResponseTaskShockingPoliceInvestigate'
|
||||
| 'CEventDataResponseTaskThreat'
|
||||
| 'CEventDataResponseTaskTurnToFace'
|
||||
| 'CEventDataResponseTaskWalkAway'
|
||||
| 'CEventDataResponseTaskWalkRoundEntity'
|
||||
| 'CEventDataResponseTaskWalkRoundFire'
|
||||
| 'CEventDeadPedFound'
|
||||
| 'CEventDeath'
|
||||
| 'CEventDecisionMakerResponse'
|
||||
| 'CEventDisturbance'
|
||||
| 'CEventDraggedOutCar'
|
||||
| 'CEventEditableResponse'
|
||||
| 'CEventEncroachingPed'
|
||||
| 'CEventEntityDamaged'
|
||||
| 'CEventEntityDestroyed'
|
||||
| 'CEventExplosion'
|
||||
| 'CEventExplosionHeard'
|
||||
| 'CEventFireNearby'
|
||||
| 'CEventFootStepHeard'
|
||||
| 'CEventFriendlyAimedAt'
|
||||
| 'CEventFriendlyFireNearMiss'
|
||||
| 'CEventGetOutOfWater'
|
||||
| 'CEventGivePedTask'
|
||||
| 'CEventGroupScriptAI'
|
||||
| 'CEventGroupScriptNetwork'
|
||||
| 'CEventGunAimedAt'
|
||||
| 'CEventGunShot'
|
||||
| 'CEventGunShotBulletImpact'
|
||||
| 'CEventGunShotWhizzedBy'
|
||||
| 'CEventHelpAmbientFriend'
|
||||
| 'CEventHurtTransition'
|
||||
| 'CEventInAir'
|
||||
| 'CEventInfo'
|
||||
| 'CEventInfoBase'
|
||||
| 'CEventInjuredCryForHelp'
|
||||
| 'CEventLeaderEnteredCarAsDriver'
|
||||
| 'CEventLeaderExitedCarAsDriver'
|
||||
| 'CEventLeaderHolsteredWeapon'
|
||||
| 'CEventLeaderLeftCover'
|
||||
| 'CEventLeaderUnholsteredWeapon'
|
||||
| 'CEventMeleeAction'
|
||||
| 'CEventMustLeaveBoat'
|
||||
| 'CEventNetworkAdminInvited'
|
||||
| 'CEventNetworkAttemptHostMigration'
|
||||
| 'CEventNetworkBail'
|
||||
| 'CEventNetworkCashTransactionLog'
|
||||
| 'CEventNetworkCheatTriggered'
|
||||
| 'CEventNetworkClanInviteReceived'
|
||||
| 'CEventNetworkClanJoined'
|
||||
| 'CEventNetworkClanKicked'
|
||||
| 'CEventNetworkClanLeft'
|
||||
| 'CEventNetworkClanRankChanged'
|
||||
| 'CEventNetworkCloudEvent'
|
||||
| 'CEventNetworkCloudFileResponse'
|
||||
| 'CEventNetworkEmailReceivedEvent'
|
||||
| 'CEventNetworkEndMatch'
|
||||
| 'CEventNetworkEndSession'
|
||||
| 'CEventNetworkEntityDamage'
|
||||
| 'CEventNetworkFindSession'
|
||||
| 'CEventNetworkFollowInviteReceived'
|
||||
| 'CEventNetworkHostMigration'
|
||||
| 'CEventNetworkHostSession'
|
||||
| 'CEventNetworkIncrementStat'
|
||||
| 'CEventNetworkInviteAccepted'
|
||||
| 'CEventNetworkInviteConfirmed'
|
||||
| 'CEventNetworkInviteRejected'
|
||||
| 'CEventNetworkJoinSession'
|
||||
| 'CEventNetworkJoinSessionResponse'
|
||||
| 'CEventNetworkOnlinePermissionsUpdated'
|
||||
| 'CEventNetworkPedLeftBehind'
|
||||
| 'CEventNetworkPickupRespawned'
|
||||
| 'CEventNetworkPlayerArrest'
|
||||
| 'CEventNetworkPlayerCollectedAmbientPickup'
|
||||
| 'CEventNetworkPlayerCollectedPickup'
|
||||
| 'CEventNetworkPlayerCollectedPortablePickup'
|
||||
| 'CEventNetworkPlayerDroppedPortablePickup'
|
||||
| 'CEventNetworkPlayerEnteredVehicle'
|
||||
| 'CEventNetworkPlayerJoinScript'
|
||||
| 'CEventNetworkPlayerLeftScript'
|
||||
| 'CEventNetworkPlayerScript'
|
||||
| 'CEventNetworkPlayerSession'
|
||||
| 'CEventNetworkPlayerSpawn'
|
||||
| 'CEventNetworkPresenceInvite'
|
||||
| 'CEventNetworkPresenceInviteRemoved'
|
||||
| 'CEventNetworkPresenceInviteReply'
|
||||
| 'CEventNetworkPresenceTriggerEvent'
|
||||
| 'CEventNetworkPresence_StatUpdate'
|
||||
| 'CEventNetworkPrimaryClanChanged'
|
||||
| 'CEventNetworkRequestDelay'
|
||||
| 'CEventNetworkRosChanged'
|
||||
| 'CEventNetworkScAdminPlayerUpdated'
|
||||
| 'CEventNetworkScAdminReceivedCash'
|
||||
| 'CEventNetworkScriptEvent'
|
||||
| 'CEventNetworkSessionEvent'
|
||||
| 'CEventNetworkShopTransaction'
|
||||
| 'CEventNetworkSignInStateChanged'
|
||||
| 'CEventNetworkSocialClubAccountLinked'
|
||||
| 'CEventNetworkSpectateLocal'
|
||||
| 'CEventNetworkStartMatch'
|
||||
| 'CEventNetworkStartSession'
|
||||
| 'CEventNetworkStorePlayerLeft'
|
||||
| 'CEventNetworkSummon'
|
||||
| 'CEventNetworkSystemServiceEvent'
|
||||
| 'CEventNetworkTextMessageReceived'
|
||||
| 'CEventNetworkTimedExplosion'
|
||||
| 'CEventNetworkTransitionEvent'
|
||||
| 'CEventNetworkTransitionGamerInstruction'
|
||||
| 'CEventNetworkTransitionMemberJoined'
|
||||
| 'CEventNetworkTransitionMemberLeft'
|
||||
| 'CEventNetworkTransitionParameterChanged'
|
||||
| 'CEventNetworkTransitionStarted'
|
||||
| 'CEventNetworkTransitionStringChanged'
|
||||
| 'CEventNetworkVehicleUndrivable'
|
||||
| 'CEventNetworkVoiceConnectionRequested'
|
||||
| 'CEventNetworkVoiceConnectionResponse'
|
||||
| 'CEventNetworkVoiceConnectionTerminated'
|
||||
| 'CEventNetworkVoiceSessionEnded'
|
||||
| 'CEventNetworkVoiceSessionStarted'
|
||||
| 'CEventNetworkWithData'
|
||||
| 'CEventNetwork_InboxMsgReceived'
|
||||
| 'CEventNewTask'
|
||||
| 'CEventObjectCollision'
|
||||
| 'CEventOnFire'
|
||||
| 'CEventOpenDoor'
|
||||
| 'CEventPedCollisionWithPed'
|
||||
| 'CEventPedCollisionWithPlayer'
|
||||
| 'CEventPedEnteredMyVehicle'
|
||||
| 'CEventPedJackingMyVehicle'
|
||||
| 'CEventPedOnCarRoof'
|
||||
| 'CEventPedSeenDeadPed'
|
||||
| 'CEventPlayerCollisionWithPed'
|
||||
| 'CEventPlayerDeath'
|
||||
| 'CEventPlayerUnableToEnterVehicle'
|
||||
| 'CEventPotentialBeWalkedInto'
|
||||
| 'CEventPotentialBlast'
|
||||
| 'CEventPotentialGetRunOver'
|
||||
| 'CEventPotentialWalkIntoVehicle'
|
||||
| 'CEventProvidingCover'
|
||||
| 'CEventRanOverPed'
|
||||
| 'CEventReactionEnemyPed'
|
||||
| 'CEventReactionInvestigateDeadPed'
|
||||
| 'CEventReactionInvestigateThreat'
|
||||
| 'CEventRequestHelp'
|
||||
| 'CEventRequestHelpWithConfrontation'
|
||||
| 'CEventRespondedToThreat'
|
||||
| 'CEventScanner'
|
||||
| 'CEventScenarioForceAction'
|
||||
| 'CEventScriptCommand'
|
||||
| 'CEventScriptWithData'
|
||||
| 'CEventShocking'
|
||||
| 'CEventShockingBicycleCrash'
|
||||
| 'CEventShockingBicycleOnPavement'
|
||||
| 'CEventShockingCarAlarm'
|
||||
| 'CEventShockingCarChase'
|
||||
| 'CEventShockingCarCrash'
|
||||
| 'CEventShockingCarOnCar'
|
||||
| 'CEventShockingCarPileUp'
|
||||
| 'CEventShockingDangerousAnimal'
|
||||
| 'CEventShockingDeadBody'
|
||||
| 'CEventShockingDrivingOnPavement'
|
||||
| 'CEventShockingEngineRevved'
|
||||
| 'CEventShockingExplosion'
|
||||
| 'CEventShockingFire'
|
||||
| 'CEventShockingGunFight'
|
||||
| 'CEventShockingGunshotFired'
|
||||
| 'CEventShockingHelicopterOverhead'
|
||||
| 'CEventShockingHornSounded'
|
||||
| 'CEventShockingInDangerousVehicle'
|
||||
| 'CEventShockingInjuredPed'
|
||||
| 'CEventShockingMadDriver'
|
||||
| 'CEventShockingMadDriverBicycle'
|
||||
| 'CEventShockingMadDriverExtreme'
|
||||
| 'CEventShockingMugging'
|
||||
| 'CEventShockingNonViolentWeaponAimedAt'
|
||||
| 'CEventShockingParachuterOverhead'
|
||||
| 'CEventShockingPedKnockedIntoByPlayer'
|
||||
| 'CEventShockingPedRunOver'
|
||||
| 'CEventShockingPedShot'
|
||||
| 'CEventShockingPlaneFlyby'
|
||||
| 'CEventShockingPotentialBlast'
|
||||
| 'CEventShockingPropertyDamage'
|
||||
| 'CEventShockingRunningPed'
|
||||
| 'CEventShockingRunningStampede'
|
||||
| 'CEventShockingSeenCarStolen'
|
||||
| 'CEventShockingSeenConfrontation'
|
||||
| 'CEventShockingSeenGangFight'
|
||||
| 'CEventShockingSeenInsult'
|
||||
| 'CEventShockingSeenMeleeAction'
|
||||
| 'CEventShockingSeenNiceCar'
|
||||
| 'CEventShockingSeenPedKilled'
|
||||
| 'CEventShockingSiren'
|
||||
| 'CEventShockingStudioBomb'
|
||||
| 'CEventShockingVehicleTowed'
|
||||
| 'CEventShockingVisibleWeapon'
|
||||
| 'CEventShockingWeaponThreat'
|
||||
| 'CEventShockingWeirdPed'
|
||||
| 'CEventShockingWeirdPedApproaching'
|
||||
| 'CEventShoutBlockingLos'
|
||||
| 'CEventShoutTargetPosition'
|
||||
| 'CEventShovePed'
|
||||
| 'CEventSoundBase'
|
||||
| 'CEventStatChangedValue'
|
||||
| 'CEventStaticCountReachedMax'
|
||||
| 'CEventStuckInAir'
|
||||
| 'CEventSuspiciousActivity'
|
||||
| 'CEventSwitch2NM'
|
||||
| 'CEventUnidentifiedPed'
|
||||
| 'CEventVehicleCollision'
|
||||
| 'CEventVehicleDamage'
|
||||
| 'CEventVehicleDamageWeapon'
|
||||
| 'CEventVehicleOnFire'
|
||||
| 'CEventWrithe'
|
19
rpc/tsconfig.json
Normal file
19
rpc/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"lib": ["ES6", "dom"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
|
||||
"outDir": "dist",
|
||||
"esModuleInterop": true,
|
||||
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
14
rpc/tsup.config.ts
Normal file
14
rpc/tsup.config.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { defineConfig } from 'tsup'
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
outDir: './dist',
|
||||
target: 'node16',
|
||||
platform: 'node',
|
||||
format: ['cjs'],
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
clean: false,
|
||||
experimentalDts: true,
|
||||
noExternal: [/.*/],
|
||||
})
|
15
shared-types/license.md
Normal file
15
shared-types/license.md
Normal file
@ -0,0 +1,15 @@
|
||||
Custom Attribution-NoDerivs Software License
|
||||
|
||||
Copyright (c) 2025 Entity Seven Group
|
||||
|
||||
This license allows you to use, copy, and distribute these packages (the "Software"), including for commercial purposes, provided that the following conditions are met:
|
||||
|
||||
1. **Attribution:** You must give appropriate credit to the original author of the Software, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||
|
||||
2. **No Derivative Works:** You may not modify, transform, or build upon the Software.
|
||||
|
||||
3. **Usage and Commercial Use:** You are allowed to use, sell, and gain income from projects that utilize the Software, as long as you comply with the terms of this license.
|
||||
|
||||
4. **No Additional Restrictions:** You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
shared-types/package.json
Normal file
30
shared-types/package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@entityseven/fivem-rpc-shared-types",
|
||||
"description": "Shared types for @entityseven/fivem-rpc. Highly recommended to install together",
|
||||
"version": "0.1.0",
|
||||
"types": "types/types/index.d.ts",
|
||||
"files": [
|
||||
"types/**/*",
|
||||
"readme.md",
|
||||
"license.md"
|
||||
],
|
||||
"keywords": [
|
||||
"fivem-rpc-shared-types",
|
||||
"fivem-rpc",
|
||||
"fivem",
|
||||
"gta"
|
||||
],
|
||||
"type": "module",
|
||||
"author": "Entity Seven Group",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Danya H",
|
||||
"email": "dev.rilaxik@gmail.com",
|
||||
"url": "https://github.com/rilaxik/"
|
||||
}
|
||||
],
|
||||
"license": "Custom-Attribution-NoDerivs",
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
0
shared-types/readme.md
Normal file
0
shared-types/readme.md
Normal file
52
shared-types/types/types/index.d.ts
vendored
Normal file
52
shared-types/types/types/index.d.ts
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
declare module '@entityseven/fivem-rpc-shared-types' {
|
||||
// Client commands names
|
||||
export type RPCCommands_Client = ''
|
||||
|
||||
// Server commands names
|
||||
export type RPCCommands_Server = ''
|
||||
|
||||
// Client -> Client events
|
||||
export interface RPCEvents_Client {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Client -> Server events
|
||||
export interface RPCEvents_ClientServer {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Client -> Webview events
|
||||
export interface RPCEvents_ClientWebview {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Server -> Server events
|
||||
export interface RPCEvents_Server {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Server -> Client events
|
||||
export interface RPCEvents_ServerClient {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Server -> Server events
|
||||
export interface RPCEvents_ServerWebview {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Webview -> Webview events
|
||||
export interface RPCEvents_Webview {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Webview -> Client events
|
||||
export interface RPCEvents_WebviewClient {
|
||||
_(): void
|
||||
}
|
||||
|
||||
// Webview -> Server events
|
||||
export interface RPCEvents_WebviewServer {
|
||||
_(): void
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user