Compare commits

..
Author SHA1 Message Date
Oleksandr Honcharov 22e2c6f980 v0.1.0 2024-06-15 16:47:36 +03:00
Oleksandr Honcharov 059dc7bed8 production files readme 2024-06-15 16:47:14 +03:00
sashagoncharov19 93b0cb1979 Update cli/src/utils/update.ts 2024-06-15 13:42:17 +00:00
rilaxik 37fdfa6d90 added issue template 2024-06-15 14:35:36 +01:00
rilaxik 7bf9433bef readme bump 2024-06-14 20:44:45 +01:00
rilaxik 15254a35ba readme docs reference 2024-06-14 19:06:47 +01:00
rilaxik 2b523fcbe5 license 2024-06-14 18:09:24 +01:00
rilaxik 4f4723625e readme bump 2024-06-14 17:59:16 +01:00
rilaxik 93922288b1 readme updates + wiki references 2024-06-14 17:58:04 +01:00
Oleksandr Honcharov 630c06bb91 v0.0.30-alpha.0 2024-06-13 23:08:00 +03:00
Oleksandr Honcharov 31136ad7e0 some fixes 2024-06-13 23:07:43 +03:00
Oleksandr Honcharov e9f77bf88d v0.0.29-alpha.0 2024-06-13 22:57:52 +03:00
Oleksandr Honcharov 424513dd56 v0.0.28-alpha.0 2024-06-13 22:48:02 +03:00
Oleksandr Honcharov 36c241a117 cli rework 2024-06-13 22:47:33 +03:00
Oleksandr Honcharov 333bd9df13 v0.0.27-alpha.0 2024-06-13 22:03:27 +03:00
Oleksandr Honcharov 6be93daa2d v0.0.26-alpha.0 2024-06-13 21:59:06 +03:00
Oleksandr Honcharov 06f93e61a8 v0.0.25-alpha.0 2024-06-13 21:50:40 +03:00
Oleksandr Honcharov 7357038c42 v0.0.24-alpha.0 2024-06-13 21:42:36 +03:00
rilaxik 04412d7cbc update checker middleware 2024-06-13 19:41:05 +01:00
rilaxik 9204eb81be Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	cli/src/commands/create.ts
2024-06-13 19:27:36 +01:00
rilaxik 1fd65c6b86 optional cli arguments against selector 2024-06-13 19:26:30 +01:00
Oleksandr Honcharov 396e9c0ee6 cli check for update 2024-06-13 21:16:52 +03:00
rilaxik 839dff9b78 rage-fw-cli create command 2024-06-13 18:04:09 +01:00
rilaxik 2054bc0a75 rage-fw-cli init 2024-06-13 16:26:08 +01:00
Oleksandr Honcharov 73dde02792 pnpm-lock.yaml 2024-06-13 18:24:21 +03:00
Oleksandr Honcharov 197de51a74 added rage-rpc 2024-06-13 18:24:07 +03:00
Oleksandr Honcharov d3276278a5 v0.0.23-alpha.0 2024-06-13 17:20:47 +03:00
Oleksandr Honcharov 1ad6f2c011 added mp checks in cef 2024-06-13 17:20:22 +03:00
Oleksandr Honcharov 7ad4f2b7ae v0.0.22-alpha.0 2024-06-13 17:04:39 +03:00
Oleksandr Honcharov 02c46389b3 cleared index.d.ts 2024-06-13 17:04:06 +03:00
rilaxik 2df12062d0 fixed type mismatches and callback handling in server register event 2024-06-13 15:02:41 +01:00
Oleksandr Honcharov 045614379d v0.0.21-alpha.0 2024-06-13 05:13:10 +03:00
Oleksandr Honcharov 642545595b optimized imports 2024-06-13 05:06:11 +03:00
Oleksandr Honcharov 1766f047a4 better typization for cef 2024-06-13 05:06:02 +03:00
Oleksandr Honcharov 95352908f8 split cef types 2024-06-13 05:00:24 +03:00
Oleksandr Honcharov 7a506a02e2 server triggerBrowser typization improvement 2024-06-13 04:54:29 +03:00
Oleksandr Honcharov c94bab24f2 client triggerBrowser typization improvement 2024-06-13 04:51:13 +03:00
Oleksandr Honcharov a0e8c0b461 rollback triggerBrowser 2024-06-13 04:46:29 +03:00
rilaxik 0520b869db resolved circular type dependency 2024-06-13 02:25:04 +01:00
rilaxik e412aff01b rollback tsconfig test changes 2024-06-13 02:18:55 +01:00
rilaxik c52cd8b4c8 better typings in trigger functions 2024-06-13 02:18:33 +01:00
Oleksandr Honcharov a9486db3f5 fixed server to server trigger 2024-06-13 01:31:07 +03:00
Oleksandr Honcharov 61929d7b48 v0.0.20-alpha.0 2024-06-13 00:24:22 +03:00
Oleksandr Honcharov d6e9c92d0a moved browser on client side 2024-06-13 00:23:50 +03:00
Oleksandr Honcharov 301ef4ac57 v0.0.19-alpha.0 2024-06-13 00:02:51 +03:00
Oleksandr Honcharov 79011e0fd2 added browser trigger on client/server | added copyright on server 2024-06-13 00:02:10 +03:00
Oleksandr Honcharov 57b4f236ac v0.0.18-alpha.0 2024-06-12 04:11:34 +03:00
Oleksandr Honcharov 558005d7b0 update peerDependencies 2024-06-12 04:09:14 +03:00
rilaxik a03cc01aa0 readme 2024-06-12 01:15:17 +01:00
rilaxik bad4d532f6 types docs 2024-06-12 00:03:44 +01:00
Oleksandr Honcharov 234bcecc7b v0.0.17-alpha.0 2024-06-11 23:14:22 +03:00
Oleksandr Honcharov 2b26dbffc0 client tsconfig.json changes 2024-06-11 23:12:06 +03:00
Oleksandr Honcharov 88e40a4635 cef functionality implementation 2024-06-11 23:11:52 +03:00
rilaxik fa3b1b1b0b Merge remote-tracking branch 'origin/master' 2024-06-11 19:52:07 +01:00
rilaxik 1880f28eb7 unregister + trigger 2024-06-11 19:52:00 +01:00
Oleksandr Honcharov c021ef3c59 remove shared 2024-06-11 21:35:57 +03:00
Oleksandr Honcharov 22945357fd cef pnpm workspace 2024-06-11 21:34:47 +03:00
Oleksandr Honcharov 17ef98cbd2 cef implementation 2024-06-11 21:34:35 +03:00
Oleksandr Honcharov 39ab22f9aa client logger 2024-06-11 21:30:39 +03:00
Oleksandr Honcharov 97b094c3d4 v0.0.16-alpha.0 2024-06-11 20:29:42 +03:00
Oleksandr Honcharov acd7ad3406 client / server ragempcommunity types peerDependency 2024-06-11 20:29:07 +03:00
Oleksandr Honcharov 0a81acf49e logger functionality 2024-06-11 20:28:57 +03:00
Oleksandr Honcharov 1b2f0d02ef server logger 2024-06-11 20:27:54 +03:00
Oleksandr Honcharov 3bb9256eb8 v0.0.15-alpha.0 2024-06-11 19:12:23 +03:00
Oleksandr Honcharov 23c1e6080a fixes types 2024-06-11 19:12:03 +03:00
Oleksandr Honcharov 33ebdaac84 clear usages 2024-06-11 19:09:19 +03:00
Oleksandr Honcharov 8972fee22e v0.0.14-alpha.0 2024-06-11 19:08:49 +03:00
rilaxik 828d74dad9 eslint 2024-06-11 16:25:41 +01:00
rilaxik e1450397ab split types in server/client files 2024-06-11 16:02:53 +01:00
rilaxik 3f48215eb1 resolved types 2024-06-11 15:14:20 +01:00
rilaxik 5205a9e245 resolved types 2024-06-11 14:45:47 +01:00
rilaxik 4695f5dbd1 types 2024-06-11 02:57:19 +01:00
rilaxik fa4c218a7a Merge remote-tracking branch 'origin/master'
# Conflicts:
#	server/src/index.ts
#	server/src/types.ts
2024-06-11 02:50:29 +01:00
rilaxik 454fc12d2e types 2024-06-11 02:49:56 +01:00
Oleksandr Honcharov 2da34bd382 temp 2024-06-11 03:38:31 +03:00
58 changed files with 3388 additions and 217 deletions
+31
View File
@@ -0,0 +1,31 @@
env:
node: true
es6: true
es2019: true
extends:
- 'eslint:recommended'
- 'plugin:@typescript-eslint/recommended'
# - 'prettier'
parser: '@typescript-eslint/parser'
parserOptions:
ecmaVersion: '2019'
sourceType: 'script'
plugins:
- '@typescript-eslint'
ignorePatterns:
- 'node_modules'
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
"argsIgnorePattern": '^_',
"varsIgnorePattern": '^_',
"caughtErrorsIgnorePattern": '^_'
}
],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'consistent-return': 'warn',
'no-empty': 'off',
}
+63
View File
@@ -0,0 +1,63 @@
name: Bug Report
about: Create a report to help us improve
rules:
- All issues must be reported in English
- Ensure you have searched for existing issues before creating a new one
- Provide as much detail as possible to help us resolve the issue efficiently
body:
- type: dropdown
id: package_version
attributes:
label: Package Version
description: Select the version of the package where the bug was found
options:
- 0.1.0
validations:
required: true
- type: textarea
id: bug_description
attributes:
label: Bug Description
description: Provide a detailed description of the bug
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction (Optional)
description: Steps to reproduce the bug, if applicable
validations:
required: true
- type: dropdown
id: os
attributes:
label: OS Where the Bug Was Found
description: Select the operating system where the bug was found
options:
- Windows
- macOS
- Linux
- Other (please specify at the end of bug description)
validations:
required: true
- type: input
id: cli_version
attributes:
label: CLI Version (Optional)
description: Specify the CLI version if used
validations:
required: false
- type: textarea
id: additional_information
attributes:
label: Additional Information
description: Add any other information that might be useful in diagnosing the issue
validations:
required: false
+24
View File
@@ -0,0 +1,24 @@
{
"name": "rage-fw-cef",
"version": "0.1.0",
"main": "dist/index.js",
"types": "dist/src/index.d.ts",
"files": [
"dist/**/*",
"readme.md"
],
"scripts": {
"build": "tsup"
},
"dependencies": {
"rage-rpc": "^0.4.0"
},
"peerDependencies": {
"@ragempcommunity/types-cef": "^2.1.8",
"rage-fw-shared-types": "workspace:^"
},
"keywords": [],
"author": "SashaGoncharov19",
"license": "MIT",
"description": "CEF side for rage-fw"
}
+2
View File
@@ -0,0 +1,2 @@
# RageFW CEF
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs)
+77
View File
@@ -0,0 +1,77 @@
import rpc from 'rage-rpc'
import {
_CefEventHasArgs,
_ClientEventHasArgs,
_ServerEventHasArgs,
RageFW_CefArguments,
RageFW_CefCallback,
RageFW_CefReturn,
RageFW_ClientArguments,
RageFW_ClientReturn,
RageFW_ICustomCefEvent,
RageFW_ICustomClientEvent,
RageFW_ICustomServerEvent,
RageFW_ServerArguments,
RageFW_ServerReturn,
} from './types'
class Cef {
public register<EventName extends keyof RageFW_ICustomCefEvent>(
eventName: EventName,
callback: RageFW_CefCallback<EventName>,
): void {
if ('mp' in window) {
rpc.register(eventName, callback)
}
}
public trigger<EventName extends keyof RageFW_ICustomCefEvent>(
eventName: EventName,
...args: _CefEventHasArgs<EventName> extends true
? [RageFW_CefArguments<EventName>]
: []
): Promise<RageFW_CefReturn<EventName>> {
if ('mp' in window) {
return rpc.call(eventName, args)
}
return Promise.reject(
'RageFW was started in window which not contain global variable MP!',
)
}
public triggerServer<EventName extends keyof RageFW_ICustomServerEvent>(
eventName: EventName,
...args: _ServerEventHasArgs<EventName> extends true
? [RageFW_ServerArguments<EventName>]
: []
): Promise<RageFW_ServerReturn<EventName>> {
if ('mp' in window) {
return rpc.callServer(eventName, args)
}
return Promise.reject(
'RageFW was started in window which not contain global variable MP!',
)
}
public triggerClient<EventName extends keyof RageFW_ICustomClientEvent>(
eventName: EventName,
...args: _ClientEventHasArgs<EventName> extends true
? [RageFW_ClientArguments<EventName>]
: []
): Promise<RageFW_ClientReturn<EventName>> {
if ('mp' in window) {
return rpc.callClient(eventName, args)
}
return Promise.reject(
'RageFW was started in window which not contain global variable MP!',
)
}
}
export const fw = {
event: new Cef(),
}
+39
View File
@@ -0,0 +1,39 @@
import { RageFW_ICustomCefEvent } from 'rage-fw-shared-types'
export { RageFW_ICustomCefEvent } from 'rage-fw-shared-types'
/**
* Union of all available cef event names
* These only include custom events
*/
export type RageFW_CefEvent = keyof RageFW_ICustomCefEvent
/**
* Array of arguments of an event you pass as a generic
* These only include custom cef events
*/
export type RageFW_CefArguments<K extends RageFW_CefEvent> = Parameters<
RageFW_ICustomCefEvent[K]
>
/**
* Return type of event you pass as a generic
* These only include custom cef events
*/
export type RageFW_CefReturn<K extends RageFW_CefEvent> = ReturnType<
RageFW_ICustomCefEvent[K]
>
/**
* Callback (function) of event you pass as a generic
* These only include custom cef events
*/
export type RageFW_CefCallback<K extends keyof RageFW_ICustomCefEvent> = (
args: RageFW_CefArguments<K>,
) => RageFW_CefReturn<K>
export type _CefEventHasArgs<EventName extends keyof RageFW_ICustomCefEvent> =
keyof RageFW_ICustomCefEvent extends never
? false
: Parameters<RageFW_ICustomCefEvent[EventName]>[0] extends undefined
? false
: true
+32
View File
@@ -0,0 +1,32 @@
import type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types'
export type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types'
/**
* Union of all available client event names
* These only include custom events
*/
export type RageFW_ClientEvent = keyof RageFW_ICustomClientEvent
/**
* Array of arguments of event you pass as a generic
* These only include custom client events
*/
export type RageFW_ClientArguments<K extends RageFW_ClientEvent> = Parameters<
RageFW_ICustomClientEvent[K]
>
/**
* Return type of event you pass as a generic
* These only include custom client events
*/
export type RageFW_ClientReturn<K extends RageFW_ClientEvent> = ReturnType<
RageFW_ICustomClientEvent[K]
>
export type _ClientEventHasArgs<
EventName extends keyof RageFW_ICustomClientEvent,
> = keyof RageFW_ICustomClientEvent extends never
? false
: Parameters<RageFW_ICustomClientEvent[EventName]>[0] extends undefined
? false
: true
+3
View File
@@ -0,0 +1,3 @@
export * from './cef'
export * from './client'
export * from './server'
+32
View File
@@ -0,0 +1,32 @@
import type { RageFW_ICustomServerEvent } from 'rage-fw-shared-types'
export type { RageFW_ICustomServerEvent } from 'rage-fw-shared-types'
/**
* Union of all available server event names
* These only include custom events
*/
export type RageFW_ServerEvent = keyof RageFW_ICustomServerEvent
/**
* Array of arguments of event you pass as a generic
* These only include custom server events
*/
export type RageFW_ServerArguments<K extends RageFW_ServerEvent> = Parameters<
RageFW_ICustomServerEvent[K]
>
/**
* Return type of event you pass as a generic
* These only include custom server events
*/
export type RageFW_ServerReturn<K extends RageFW_ServerEvent> = ReturnType<
RageFW_ICustomServerEvent[K]
>
export type _ServerEventHasArgs<
EventName extends keyof RageFW_ICustomServerEvent,
> = keyof RageFW_ICustomServerEvent extends never
? false
: Parameters<RageFW_ICustomServerEvent[EventName]>[0] extends undefined
? false
: true
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Base",
"exclude": [
"node_modules"
],
"compilerOptions": {
"incremental": false,
"composite": false,
"target": "ES2022",
"experimentalDecorators": true,
"moduleDetection": "auto",
"module": "CommonJS",
"resolveJsonModule": true,
"declaration": false,
"declarationMap": false,
"sourceMap": false,
"downlevelIteration": false,
"inlineSourceMap": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
+12
View File
@@ -0,0 +1,12 @@
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
outDir: './dist',
format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true,
splitting: false,
sourcemap: false,
clean: true,
})
+6
View File
@@ -0,0 +1,6 @@
tabWidth: 4
printWidth: 80
singleQuote: true
semi: false
arrowParens: avoid
endOfLine: auto
+35
View File
@@ -0,0 +1,35 @@
{
"name": "create-rage-fw",
"version": "0.1.0",
"bin": {
"rage-fw": "dist/index.js"
},
"main": "dist/index.js",
"scripts": {
"watch": "tsc -w",
"build": "tsup",
"start": "npx ./dist create"
},
"files": [
"dist/**/*",
"readme.md"
],
"description": "CLI to scaffold a template project for RageFW",
"keywords": [],
"author": "rilaxik",
"license": "ISC",
"dependencies": {
"@inquirer/prompts": "^5.0.5",
"axios": "^1.7.2",
"chalk": "4.1.2",
"git-clone": "^0.2.0",
"yargs": "^17.7.2"
},
"devDependencies": {
"@types/git-clone": "^0.2.4",
"@types/node": "^20.14.2",
"@types/yargs": "^17.0.32",
"prettier": "^3.3.2",
"typescript": "^5.4.5"
}
}
+29
View File
@@ -0,0 +1,29 @@
# RageFW CLI
To make you life easier while using RageFW we created a basic CLI. At the moment automation we have only works via [pnpm](https://pnpm.io/)
``pnpm create rage-fw@latest``
## TL;DR
- ``Initialize new project`` - create new template project
- ``Install RAGE:MP updater`` - download and update RAGE:MP server files
## Options
For now, you will see a few available options. They are described in detail below
- ``Initialize new project``
- ``Install RAGE:MP updater``
### Initialize new project
Using this options will forward you to common project-creation menu
- ``Enter project name``
This option will specify a name for your project which is used as a folder name too. Defaults to **rage-fw**
- ``Select frontend``
Use this selector menu to choose which frontend framework you want to use. We will do our best to expand this menu after some time.
Defaults to **React + TypeScript (Vite)**
### Install Rage:MP updater
This option will simplify installation process of Rage:MP server files required to start your server
+66
View File
@@ -0,0 +1,66 @@
import c from 'chalk'
import { input, select } from '@inquirer/prompts'
import clone from 'git-clone'
import path from 'node:path'
export async function initProject() {
let folder
let framework
if (!folder) {
folder = await input({
message: c.gray('Enter project name:'),
default: 'rage-fw',
})
} else {
console.log(c.gray('Project name:'), folder)
}
if (!framework) {
framework = await select({
message: c.gray('Select frontend:'),
default: 'react',
loop: true,
choices: [
{
name: 'React + TypeScript (Vite)',
value: 'react',
description: 'React + TypeScript (Vite) as a frontend',
},
// {
// name: 'vue',
// value: 'vue',
// description: 'npm is the most popular package manager',
// },
],
})
} else {
console.log(c.gray('Frontend:'), framework)
}
console.log(
c.gray('\nScaffolding template project into'),
folder,
c.gray('with'),
framework,
c.gray('as a frontend..'),
)
clone(
'https://git.entityseven.com/entityseven/rage-framework-example',
path.join(process.cwd(), folder),
{},
err => {
if (err) {
console.log(c.red('Error occured: \n', err))
return
}
console.log(c.gray('Scaffolded project into'), folder)
console.log(
c.gray(
`Project was created ar dir: ${path.join(process.cwd(), folder)}`,
),
)
},
)
}
+37
View File
@@ -0,0 +1,37 @@
import axios from 'axios'
import * as fs from 'node:fs'
const latestReleases =
'https://git.entityseven.com/api/v1/repos/entityseven/rage-server-downloader/releases?page=1&limit=1'
type Release = {
id: number
}
type Asset = {
browser_download_url: string
}
export async function downloadUpdater(): Promise<void> {
const id = await getLatestReleaseID()
const latestAssets = `https://git.entityseven.com/api/v1/repos/entityseven/rage-server-downloader/releases/${id}/assets?page=1&limit=1`
axios.get<Asset[]>(latestAssets).then(async ({ data }) => {
const downloadURL = data[0].browser_download_url
const file = await axios.get(data[0].browser_download_url, {
responseType: 'arraybuffer',
})
const fileData = Buffer.from(file.data, 'binary')
const fileSplit = downloadURL.split('/')
const fileName = fileSplit[fileSplit.length - 1]
fs.writeFileSync(`./${fileName}`, fileData)
})
}
async function getLatestReleaseID() {
return axios.get<Release[]>(latestReleases).then(({ data }) => data[0].id)
}
+40
View File
@@ -0,0 +1,40 @@
import c from 'chalk'
import { select } from '@inquirer/prompts'
import { checkForUpdate } from './utils/update'
import { initProject } from './commands/create'
import { downloadUpdater } from './commands/download-updater'
enum Actions {
INIT_PROJECT = 'INIT_PROJECT',
UPDATER = 'UPDATER',
}
;(async () => {
await checkForUpdate()
console.log(
c.blueBright('Rage FW CLI | Powered by Entity Seven Group <3'),
)
const action = await select({
message: c.gray('Select action:'),
choices: [
{
name: 'Initialize new project',
value: Actions.INIT_PROJECT,
description: 'Initialize new project and start develop',
},
{
name: 'Install RAGE:MP updater',
value: Actions.UPDATER,
description:
'Use our custom updater to download and update RAGE:MP server files.',
},
],
loop: true,
})
if (action === Actions.INIT_PROJECT) await initProject()
if (action === Actions.UPDATER) await downloadUpdater()
})()
+32
View File
@@ -0,0 +1,32 @@
import axios from 'axios'
import c from 'chalk'
import yargs from 'yargs'
const latestVersionURL =
'https://git.entityseven.com/api/v1/repos/entityseven/rage-framework/tags?page=1&limit=1'
type Version = {
name: string
message: string
}
export async function checkForUpdate(): Promise<void> {
return new Promise(res => {
yargs.showVersion(version =>
axios
.get<Version[]>(latestVersionURL)
.then(({ data }) => {
const latestVersion = data[0].name
if (latestVersion !== `v${version}`)
notifyUserAboutUpdate(latestVersion)
else console.log(c.yellow(`Version: ${version}`))
})
.then(() => res()),
)
})
}
function notifyUserAboutUpdate(version: string) {
console.log(c.green(`Update available. New version: ${version}`))
}
+24
View File
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["DOM", "ES6"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "bin",
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
+10
View File
@@ -0,0 +1,10 @@
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
outDir: './dist',
bundle: true,
splitting: false,
sourcemap: false,
clean: true,
})
+8 -5
View File
@@ -1,19 +1,22 @@
{ {
"name": "rage-fw-client", "name": "rage-fw-client",
"version": "0.0.13-alpha.0", "version": "0.1.0",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/src/index.d.ts",
"files": [ "files": [
"dist/**/*" "dist/**/*",
"readme.md"
], ],
"scripts": { "scripts": {
"build": "tsup" "build": "tsup"
}, },
"dependencies": { "dependencies": {
"@ragempcommunity/types-client": "^2.1.8",
"rage-fw-shared-types": "workspace:^",
"rage-rpc": "^0.4.0" "rage-rpc": "^0.4.0"
}, },
"peerDependencies": {
"@ragempcommunity/types-client": "^2.1.8",
"rage-fw-shared-types": "workspace:^"
},
"keywords": [], "keywords": [],
"author": "SashaGoncharov19", "author": "SashaGoncharov19",
"license": "MIT", "license": "MIT",
+2
View File
@@ -0,0 +1,2 @@
# RageFW Client
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs)
+62 -8
View File
@@ -1,33 +1,87 @@
import rpc from 'rage-rpc' import rpc from 'rage-rpc'
import Logger from './logger'
import { import {
RageFW_ClientEventReturn, _CefEventHasArgs,
_ClientEventHasArgs,
_ServerEventHasArgs,
RageFW_CefArgs,
RageFW_CefEvent,
RageFW_CefReturn,
RageFW_ClientEvent, RageFW_ClientEvent,
RageFW_ClientEventArguments, RageFW_ClientEventArguments,
RageFW_ClientServerCallback, RageFW_ClientEventCallback,
RageFW_ClientEventReturn,
RageFW_ClientServerEvent, RageFW_ClientServerEvent,
RageFW_ClientServerEventArguments,
RageFW_ClientServerEventReturn,
} from './types' } from './types'
import type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types'
class Client { class Client {
public register<EventName extends RageFW_ClientServerEvent>( public register<EventName extends RageFW_ClientEvent>(
eventName: EventName, eventName: EventName,
callback: RageFW_ClientServerCallback<EventName>, callback: RageFW_ClientEventCallback<EventName>,
): void { ): void {
rpc.register(eventName, data => { rpc.register(eventName, data => {
return callback(...data) return callback(data)
}) })
} }
public unregister<EventName extends RageFW_ClientEvent>(
eventName: EventName,
): void {
rpc.unregister(eventName)
}
} }
class Player { class Player {
public triggerServer<EventName extends RageFW_ClientEvent>( public browser: BrowserMp | undefined
public trigger<EventName extends keyof RageFW_ICustomClientEvent>(
eventName: EventName, eventName: EventName,
...args: RageFW_ClientEventArguments<EventName> ...args: _ClientEventHasArgs<EventName> extends true
? [RageFW_ClientEventArguments<EventName>]
: []
): Promise<RageFW_ClientEventReturn<EventName>> { ): Promise<RageFW_ClientEventReturn<EventName>> {
return rpc.callServer(eventName, ...args) return rpc.call<RageFW_ClientEventReturn<EventName>>(eventName, args)
}
public triggerServer<EventName extends RageFW_ClientServerEvent>(
eventName: EventName,
...args: _ServerEventHasArgs<EventName> extends true
? [RageFW_ClientServerEventArguments<EventName>]
: []
): Promise<RageFW_ClientServerEventReturn<EventName>> {
return rpc.callServer(eventName, args)
}
public triggerBrowser<EventName extends RageFW_CefEvent>(
eventName: EventName,
...args: _CefEventHasArgs<EventName> extends true
? [RageFW_CefArgs<EventName>]
: []
): Promise<RageFW_CefReturn<EventName>> {
if (!this.browser)
throw new Error('You need to initialize browser first!')
return rpc.callBrowser(this.browser, eventName, args)
}
}
class Browser extends Player {
public registerBrowser(browser: BrowserMp) {
this.browser = browser
return browser
} }
} }
export const fw = { export const fw = {
event: new Client(), event: new Client(),
player: new Player(), player: new Player(),
browser: new Browser(),
system: {
log: new Logger(),
},
} }
+19
View File
@@ -0,0 +1,19 @@
export default class Logger {
public error(message: unknown) {
mp.console.logError(
`[${new Date().toLocaleTimeString()}] [ERROR] ${message}`,
)
}
public warn(message: unknown) {
mp.console.logWarning(
`[${new Date().toLocaleTimeString()}] [WARN] ${message}`,
)
}
public info(message: unknown) {
mp.console.logInfo(
`[${new Date().toLocaleTimeString()}] [INFO] ${message}`,
)
}
}
-24
View File
@@ -1,24 +0,0 @@
/// <reference types="@ragempcommunity/types-client" />
import type {
RageFW_ICustomClientEvent,
RageFW_ICustomServerEvent,
} from 'rage-fw-shared-types'
export type RageFW_ClientEvent = keyof RageFW_ICustomServerEvent
export type RageFW_ClientEventArguments<K extends RageFW_ClientEvent> =
K extends keyof RageFW_ICustomServerEvent
? Parameters<RageFW_ICustomServerEvent[K]>
: never
export type RageFW_ClientEventReturn<K extends RageFW_ClientEvent> =
K extends keyof RageFW_ICustomClientEvent
? ReturnType<RageFW_ICustomClientEvent[K]>
: never
export type RageFW_ClientServerEvent = keyof RageFW_ICustomClientEvent
export type RageFW_ClientServerCallback<K extends RageFW_ClientServerEvent> = (
...args: Parameters<RageFW_ICustomClientEvent[K]>
) => ReturnType<RageFW_ICustomClientEvent[K]>
+20
View File
@@ -0,0 +1,20 @@
/// <reference types="@ragempcommunity/types-client" />
import type { RageFW_ICustomCefEvent } from 'rage-fw-shared-types'
export type RageFW_CefEvent = keyof RageFW_ICustomCefEvent
export type RageFW_CefArgs<K extends RageFW_CefEvent> = Parameters<
RageFW_ICustomCefEvent[K]
>
export type RageFW_CefReturn<K extends RageFW_CefEvent> = ReturnType<
RageFW_ICustomCefEvent[K]
>
export type _CefEventHasArgs<EventName extends keyof RageFW_ICustomCefEvent> =
keyof RageFW_ICustomCefEvent extends never
? false
: Parameters<RageFW_ICustomCefEvent[EventName]>[0] extends undefined
? false
: true
+47
View File
@@ -0,0 +1,47 @@
/// <reference types="@ragempcommunity/types-client" />
import type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types'
/**
* Union of all available client event names
* These include custom and system events
*/
export type RageFW_ClientEvent =
| keyof RageFW_ICustomClientEvent
| keyof IClientEvents
/**
* Array of arguments for an event, name of which you pass as a generic
* These include custom and system events
*/
export type RageFW_ClientEventArguments<K extends RageFW_ClientEvent> =
K extends keyof RageFW_ICustomClientEvent
? Parameters<RageFW_ICustomClientEvent[K]>
: K extends keyof IClientEvents
? Parameters<IClientEvents[K]>
: never
/**
* Callback (function) for an event, name of which you pass as a generic
* These only include custom events
*/
export type RageFW_ClientEventCallback<K extends RageFW_ClientEvent> = (
args: RageFW_ClientEventArguments<K>,
) => RageFW_ClientEventReturn<K>
/**
* Return type for an event, name of which you pass as a generic
* These only include custom events
*/
export type RageFW_ClientEventReturn<K extends RageFW_ClientEvent> =
K extends keyof RageFW_ICustomClientEvent
? ReturnType<RageFW_ICustomClientEvent[K]>
: never
export type _ClientEventHasArgs<
EventName extends keyof RageFW_ICustomClientEvent,
> = keyof RageFW_ICustomClientEvent extends never
? false
: Parameters<RageFW_ICustomClientEvent[EventName]>[0] extends undefined
? false
: true
+3
View File
@@ -0,0 +1,3 @@
export * from './client'
export * from './server'
export * from './browser'
+39
View File
@@ -0,0 +1,39 @@
/// <reference types="@ragempcommunity/types-client" />
import type {
RageFW_ICustomClientEvent,
RageFW_ICustomServerEvent,
} from 'rage-fw-shared-types'
/**
* Union of all available server event names callable from client
* These only include custom events
*/
export type RageFW_ClientServerEvent = keyof RageFW_ICustomServerEvent
/**
* Array of arguments for an event, name of which you pass as a generic
* These only include custom events
*/
export type RageFW_ClientServerEventArguments<
K extends RageFW_ClientServerEvent,
> = K extends keyof RageFW_ICustomServerEvent
? Parameters<RageFW_ICustomServerEvent[K]>
: never
/**
* Return type for an event, name of which you pass as a generic
* These only include custom events
*/
export type RageFW_ClientServerEventReturn<K extends RageFW_ClientServerEvent> =
K extends keyof RageFW_ICustomServerEvent
? ReturnType<RageFW_ICustomServerEvent[K]>
: never
export type _ServerEventHasArgs<
EventName extends keyof RageFW_ICustomServerEvent,
> = keyof RageFW_ICustomClientEvent extends never
? false
: Parameters<RageFW_ICustomServerEvent[EventName]>[0] extends undefined
? false
: true
-1
View File
@@ -9,7 +9,6 @@
"composite": false, "composite": false,
"target": "ES2022", "target": "ES2022",
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleDetection": "auto", "moduleDetection": "auto",
"module": "CommonJS", "module": "CommonJS",
"resolveJsonModule": true, "resolveJsonModule": true,
+1 -1
View File
@@ -5,7 +5,7 @@ export default defineConfig({
outDir: './dist', outDir: './dist',
format: ['cjs'], format: ['cjs'],
noExternal: ['rage-rpc'], noExternal: ['rage-rpc'],
dts: true, experimentalDts: true,
splitting: false, splitting: false,
sourcemap: false, sourcemap: false,
clean: true, clean: true,
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"$schema": "node_modules/lerna/schemas/lerna-schema.json", "$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "0.0.13-alpha.0", "version": "0.1.0",
"npmClient": "pnpm" "npmClient": "pnpm"
} }
+13
View File
@@ -0,0 +1,13 @@
# Custom Attribution-NoDerivs Software License
This license allows you to use, copy, and distribute the RageFW (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.
+9 -4
View File
@@ -2,18 +2,23 @@
"private": true, "private": true,
"scripts": { "scripts": {
"publish": "pnpm build && lerna publish --force-publish", "publish": "pnpm build && lerna publish --force-publish",
"build": "lerna run build" "build": "lerna run build",
"lint": "eslint --c .eslintrc.yaml --ext .ts client/ server/ shared-types/"
}, },
"dependencies": { "dependencies": {
"@microsoft/api-extractor": "^7.47.0",
"@ragempcommunity/types-cef": "^2.1.8", "@ragempcommunity/types-cef": "^2.1.8",
"@ragempcommunity/types-client": "^2.1.8", "@ragempcommunity/types-client": "^2.1.8",
"@ragempcommunity/types-server": "^2.1.8", "@ragempcommunity/types-server": "^2.1.8",
"@microsoft/api-extractor": "^7.47.0", "@typescript-eslint/eslint-plugin": "^7.13.0",
"tsup": "^8.1.0", "@typescript-eslint/parser": "^7.13.0",
"eslint": "^8.56.0",
"lerna": "^8.1.3", "lerna": "^8.1.3",
"prettier": "^3.3.1", "prettier": "^3.3.1",
"rage-rpc": "^0.4.0", "rage-rpc": "^0.4.0",
"typescript": "^5.4.5" "tsup": "^8.1.0",
"typescript": "^5.4.5",
"winston": "^3.13.0"
}, },
"type": "module" "type": "module"
} }
+1412 -94
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,5 +1,6 @@
packages: packages:
- "server" - "server"
- "client" - "client"
- "shared" - "cef"
- "cli"
- "shared-types" - "shared-types"
+1
View File
@@ -0,0 +1 @@
Currently not maintained.
+24
View File
@@ -0,0 +1,24 @@
{
"name": "rage-fw-rpc",
"version": "0.0.23-alpha.0",
"main": "dist/index.js",
"types": "dist/src/index.d.ts",
"files": [
"dist/**/*"
],
"scripts": {
"build": "tsup"
},
"dependencies": {
"rage-rpc": "^0.4.0"
},
"peerDependencies": {
"@ragempcommunity/types-client": "^2.1.8",
"rage-fw-shared-types": "workspace:^"
},
"keywords": [],
"author": "SashaGoncharov19",
"license": "MIT",
"description": "Client side of rage-fw",
"gitHead": "053e4fd12aa120d53e11e0d2009c0df78c1a2ad0"
}
+42
View File
@@ -0,0 +1,42 @@
declare var mp: any;
declare var global: any;
declare var window: any;
declare type ProcedureListener = (args: any, info: ProcedureListenerInfo) => any;
declare interface Player {
call: (eventName: string, args?: any[]) => void;
[property: string]: any;
}
declare interface Browser {
url: string;
execute: (code: string) => void;
[property: string]: any;
}
declare interface ProcedureListenerInfo {
environment: string;
id?: string;
player?: Player;
browser?: Browser;
}
declare interface CallOptions {
timeout?: number;
noRet?: boolean;
}
declare interface Event {
req?: number;
ret?: number;
b?: string;
id: string;
name?: string;
args?: any;
env: string;
fenv?: string;
res?: any;
err?: any;
noRet?: number;
}
+568
View File
@@ -0,0 +1,568 @@
import * as util from './util';
const environment = util.getEnvironment();
if(!environment) throw 'Unknown RAGE environment';
const ERR_NOT_FOUND = 'PROCEDURE_NOT_FOUND';
const IDENTIFIER = '__rpc:id';
const PROCESS_EVENT = '__rpc:process';
const BROWSER_REGISTER = '__rpc:browserRegister';
const BROWSER_UNREGISTER = '__rpc:browserUnregister';
const TRIGGER_EVENT = '__rpc:triggerEvent';
const TRIGGER_EVENT_BROWSERS = '__rpc:triggerEventBrowsers';
const glob = environment === 'cef' ? window : global;
if(!glob[PROCESS_EVENT]){
glob.__rpcListeners = {};
glob.__rpcPending = {};
glob.__rpcEvListeners = {};
glob[PROCESS_EVENT] = (player: Player | string, rawData?: string) => {
if(environment !== "server") rawData = player as string;
const data: Event = util.parseData(rawData);
if(data.req){ // someone is trying to remotely call a procedure
const info: ProcedureListenerInfo = {
id: data.id,
environment: data.fenv || data.env
};
if(environment === "server") info.player = player as Player;
const part = {
ret: 1,
id: data.id,
env: environment
};
let ret: (ev: Event) => void;
switch(environment){
case "server":
ret = ev => info.player.call(PROCESS_EVENT, [util.stringifyData(ev)]);
break;
case "client": {
if(data.env === "server"){
ret = ev => mp.events.callRemote(PROCESS_EVENT, util.stringifyData(ev));
}else if(data.env === "cef"){
const browser = data.b && glob.__rpcBrowsers[data.b];
info.browser = browser;
ret = ev => browser && util.isBrowserValid(browser) && passEventToBrowser(browser, ev, true);
}
break;
}
case "cef": {
ret = ev => mp.trigger(PROCESS_EVENT, util.stringifyData(ev));
}
}
if(ret){
const promise = callProcedure(data.name, data.args, info);
if(!data.noRet) promise.then(res => ret({ ...part, res })).catch(err => ret({ ...part, err: err ? err : null }));
}
}else if(data.ret){ // a previously called remote procedure has returned
const info = glob.__rpcPending[data.id];
if(environment === "server" && info.player !== player) return;
if(info){
info.resolve(data.hasOwnProperty('err') ? util.promiseReject(data.err) : util.promiseResolve(data.res));
delete glob.__rpcPending[data.id];
}
}
};
if(environment !== "cef"){
mp.events.add(PROCESS_EVENT, glob[PROCESS_EVENT]);
if(environment === "client"){
// set up internal pass-through events
register('__rpc:callServer', ([name, args, noRet], info) => _callServer(name, args, { fenv: info.environment, noRet }));
register('__rpc:callBrowsers', ([name, args, noRet], info) => _callBrowsers(null, name, args, { fenv: info.environment, noRet }));
// set up browser identifiers
glob.__rpcBrowsers = {};
const initBrowser = (browser: Browser): void => {
const id = util.uid();
Object.keys(glob.__rpcBrowsers).forEach(key => {
const b = glob.__rpcBrowsers[key];
if(!b || !util.isBrowserValid(b) || b === browser) delete glob.__rpcBrowsers[key];
});
glob.__rpcBrowsers[id] = browser;
browser.execute(`
window.name = '${id}';
if(typeof window['${IDENTIFIER}'] === 'undefined'){
window['${IDENTIFIER}'] = Promise.resolve(window.name);
}else{
window['${IDENTIFIER}:resolve'](window.name);
}
`);
};
mp.browsers.forEach(initBrowser);
mp.events.add('browserCreated', initBrowser);
// set up browser registration map
glob.__rpcBrowserProcedures = {};
mp.events.add(BROWSER_REGISTER, (data: string) => {
const [browserId, name] = JSON.parse(data);
glob.__rpcBrowserProcedures[name] = browserId;
});
mp.events.add(BROWSER_UNREGISTER, (data: string) => {
const [browserId, name] = JSON.parse(data);
if(glob.__rpcBrowserProcedures[name] === browserId) delete glob.__rpcBrowserProcedures[name];
});
register(TRIGGER_EVENT_BROWSERS, ([name, args], info) => {
Object.values(glob.__rpcBrowsers).forEach(browser => {
_callBrowser(browser, TRIGGER_EVENT, [name, args], { fenv: info.environment, noRet: 1 });
});
});
}
}else{
if(typeof glob[IDENTIFIER] === 'undefined'){
glob[IDENTIFIER] = new Promise(resolve => {
if (window.name) {
resolve(window.name);
}else{
glob[IDENTIFIER+':resolve'] = resolve;
}
});
}
}
register(TRIGGER_EVENT, ([name, args], info) => callEvent(name, args, info));
}
function passEventToBrowser(browser: Browser, data: Event, ignoreNotFound: boolean): void {
const raw = util.stringifyData(data);
browser.execute(`var process = window["${PROCESS_EVENT}"]; if(process){ process(${JSON.stringify(raw)}); }else{ ${ignoreNotFound ? '' : `mp.trigger("${PROCESS_EVENT}", '{"ret":1,"id":"${data.id}","err":"${ERR_NOT_FOUND}","env":"cef"}');`} }`);
}
function callProcedure(name: string, args: any, info: ProcedureListenerInfo): Promise<any> {
const listener = glob.__rpcListeners[name];
if(!listener) return util.promiseReject(ERR_NOT_FOUND);
return util.promiseResolve(listener(args, info));
}
/**
* Register a procedure.
* @param {string} name - The name of the procedure.
* @param {function} cb - The procedure's callback. The return value will be sent back to the caller.
* @returns {Function} The function, which unregister the event.
*/
export function register(name: string, cb: ProcedureListener): Function {
if(arguments.length !== 2) throw 'register expects 2 arguments: "name" and "cb"';
if(environment === "cef") glob[IDENTIFIER].then((id: string) => mp.trigger(BROWSER_REGISTER, JSON.stringify([id, name])));
glob.__rpcListeners[name] = cb;
return () => unregister(name);
}
/**
* Unregister a procedure.
* @param {string} name - The name of the procedure.
*/
export function unregister(name: string): void {
if(arguments.length !== 1) throw 'unregister expects 1 argument: "name"';
if(environment === "cef") glob[IDENTIFIER].then((id: string) => mp.trigger(BROWSER_UNREGISTER, JSON.stringify([id, name])));
glob.__rpcListeners[name] = undefined;
}
/**
* Calls a local procedure. Only procedures registered in the same context will be resolved.
*
* Can be called from any environment.
*
* @param name - The name of the locally registered procedure.
* @param args - Any parameters for the procedure.
* @param options - Any options.
* @returns The result from the procedure.
*/
export function call(name: string, args?: any, options: CallOptions = {}): Promise<any> {
if(arguments.length < 1 || arguments.length > 3) return util.promiseReject('call expects 1 to 3 arguments: "name", optional "args", and optional "options"');
return util.promiseTimeout(callProcedure(name, args, { environment }), options.timeout);
}
function _callServer(name: string, args?: any, extraData: any = {}): Promise<any> {
switch(environment){
case "server": {
return call(name, args);
}
case "client": {
const id = util.uid();
return new Promise(resolve => {
if(!extraData.noRet){
glob.__rpcPending[id] = {
resolve
};
}
const event: Event = {
req: 1,
id,
name,
env: environment,
args,
...extraData
};
mp.events.callRemote(PROCESS_EVENT, util.stringifyData(event));
});
}
case "cef": {
return callClient('__rpc:callServer', [name, args, +extraData.noRet]);
}
}
}
/**
* Calls a remote procedure registered on the server.
*
* Can be called from any environment.
*
* @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure.
* @param options - Any options.
* @returns The result from the procedure.
*/
export function callServer(name: string, args?: any, options: CallOptions = {}): Promise<any> {
if(arguments.length < 1 || arguments.length > 3) return util.promiseReject('callServer expects 1 to 3 arguments: "name", optional "args", and optional "options"');
let extraData: any = {};
if(options.noRet) extraData.noRet = 1;
return util.promiseTimeout(_callServer(name, args, extraData), options.timeout);
}
function _callClient(player: Player, name: string, args?: any, extraData: any = {}): Promise<any> {
switch(environment){
case 'client': {
return call(name, args);
}
case 'server': {
const id = util.uid();
return new Promise(resolve => {
if(!extraData.noRet){
glob.__rpcPending[id] = {
resolve,
player
};
}
const event: Event = {
req: 1,
id,
name,
env: environment,
args,
...extraData
};
player.call(PROCESS_EVENT, [util.stringifyData(event)]);
});
}
case 'cef': {
const id = util.uid();
return glob[IDENTIFIER].then((browserId: string) => {
return new Promise(resolve => {
if(!extraData.noRet){
glob.__rpcPending[id] = {
resolve
};
}
const event: Event = {
b: browserId,
req: 1,
id,
name,
env: environment,
args,
...extraData
};
mp.trigger(PROCESS_EVENT, util.stringifyData(event));
});
});
}
}
}
/**
* Calls a remote procedure registered on the client.
*
* Can be called from any environment.
*
* @param player - The player to call the procedure on.
* @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure.
* @param options - Any options.
* @returns The result from the procedure.
*/
export function callClient(player: Player | string, name?: string | any, args?: any, options: CallOptions = {}): Promise<any> {
switch(environment){
case 'client': {
options = args || {};
args = name;
name = player;
player = null;
if((arguments.length < 1 || arguments.length > 3) || typeof name !== 'string') return util.promiseReject('callClient from the client expects 1 to 3 arguments: "name", optional "args", and optional "options"');
break;
}
case 'server': {
if((arguments.length < 2 || arguments.length > 4) || typeof player !== 'object') return util.promiseReject('callClient from the server expects 2 to 4 arguments: "player", "name", optional "args", and optional "options"');
break;
}
case 'cef': {
options = args || {};
args = name;
name = player;
player = null;
if((arguments.length < 1 || arguments.length > 3) || typeof name !== 'string') return util.promiseReject('callClient from the browser expects 1 to 3 arguments: "name", optional "args", and optional "options"');
break;
}
}
let extraData: any = {};
if(options.noRet) extraData.noRet = 1;
return util.promiseTimeout(_callClient(player as Player, name, args, extraData), options.timeout);
}
function _callBrowser(browser: Browser, name: string, args?: any, extraData: any = {}): Promise<any> {
return new Promise(resolve => {
const id = util.uid();
if(!extraData.noRet){
glob.__rpcPending[id] = {
resolve
};
}
passEventToBrowser(browser, {
req: 1,
id,
name,
env: environment,
args,
...extraData
}, false);
});
}
function _callBrowsers(player: Player, name: string, args?: any, extraData: any = {}): Promise<any> {
switch(environment){
case 'client':
const browserId = glob.__rpcBrowserProcedures[name];
if(!browserId) return util.promiseReject(ERR_NOT_FOUND);
const browser = glob.__rpcBrowsers[browserId];
if(!browser || !util.isBrowserValid(browser)) return util.promiseReject(ERR_NOT_FOUND);
return _callBrowser(browser, name, args, extraData);
case 'server':
return _callClient(player, '__rpc:callBrowsers', [name, args, +extraData.noRet], extraData);
case 'cef':
return _callClient(null, '__rpc:callBrowsers', [name, args, +extraData.noRet], extraData);
}
}
/**
* Calls a remote procedure registered in any browser context.
*
* Can be called from any environment.
*
* @param player - The player to call the procedure on.
* @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure.
* @param options - Any options.
* @returns The result from the procedure.
*/
export function callBrowsers(player: Player | string, name?: string | any, args?: any, options: CallOptions = {}): Promise<any> {
let promise;
let extraData: any = {};
switch(environment){
case 'client':
case 'cef':
options = args || {};
args = name;
name = player;
if(arguments.length < 1 || arguments.length > 3) return util.promiseReject('callBrowsers from the client or browser expects 1 to 3 arguments: "name", optional "args", and optional "options"');
if(options.noRet) extraData.noRet = 1;
promise = _callBrowsers(null, name, args, extraData);
break;
case 'server':
if(arguments.length < 2 || arguments.length > 4) return util.promiseReject('callBrowsers from the server expects 2 to 4 arguments: "player", "name", optional "args", and optional "options"');
if(options.noRet) extraData.noRet = 1;
promise = _callBrowsers(player as Player, name, args, extraData);
break;
}
if(promise){
return util.promiseTimeout(promise, options.timeout);
}
}
/**
* Calls a remote procedure registered in a specific browser instance.
*
* Client-side environment only.
*
* @param browser - The browser instance.
* @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure.
* @param options - Any options.
* @returns The result from the procedure.
*/
export function callBrowser(browser: Browser, name: string, args?: any, options: CallOptions = {}): Promise<any> {
if(environment !== 'client') return util.promiseReject('callBrowser can only be used in the client environment');
if(arguments.length < 2 || arguments.length > 4) return util.promiseReject('callBrowser expects 2 to 4 arguments: "browser", "name", optional "args", and optional "options"');
let extraData: any = {};
if(options.noRet) extraData.noRet = 1;
return util.promiseTimeout(_callBrowser(browser, name, args, extraData), options.timeout);
}
function callEvent(name: string, args: any, info: ProcedureListenerInfo){
const listeners = glob.__rpcEvListeners[name];
if(listeners){
listeners.forEach(listener => listener(args, info));
}
}
/**
* Register an event handler.
* @param {string} name - The name of the event.
* @param cb - The callback for the event.
* @returns {Function} The function, which off the event.
*/
export function on(name: string, cb: ProcedureListener): Function {
if(arguments.length !== 2) throw 'on expects 2 arguments: "name" and "cb"';
const listeners = glob.__rpcEvListeners[name] || new Set();
listeners.add(cb);
glob.__rpcEvListeners[name] = listeners;
return () => off(name, cb);
}
/**
* Unregister an event handler.
* @param {string} name - The name of the event.
* @param cb - The callback for the event.
*/
export function off(name: string, cb: ProcedureListener){
if(arguments.length !== 2) throw 'off expects 2 arguments: "name" and "cb"';
const listeners = glob.__rpcEvListeners[name];
if(listeners){
listeners.delete(cb);
}
}
/**
* Triggers a local event. Only events registered in the same context will be triggered.
*
* Can be called from any environment.
*
* @param name - The name of the locally registered event.
* @param args - Any parameters for the event.
*/
export function trigger(name: string, args?: any){
if(arguments.length < 1 || arguments.length > 2) throw 'trigger expects 1 or 2 arguments: "name", and optional "args"';
callEvent(name, args, { environment });
}
/**
* Triggers an event registered on the client.
*
* Can be called from any environment.
*
* @param player - The player to call the procedure on.
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerClient(player: Player | string, name?: string | any, args?: any){
switch(environment){
case 'client': {
args = name;
name = player;
player = null;
if((arguments.length < 1 || arguments.length > 2) || typeof name !== 'string') throw 'triggerClient from the client expects 1 or 2 arguments: "name", and optional "args"';
break;
}
case 'server': {
if((arguments.length < 2 || arguments.length > 3) || typeof player !== 'object') throw 'triggerClient from the server expects 2 or 3 arguments: "player", "name", and optional "args"';
break;
}
case 'cef': {
args = name;
name = player;
player = null;
if((arguments.length < 1 || arguments.length > 2) || typeof name !== 'string') throw 'triggerClient from the browser expects 1 or 2 arguments: "name", and optional "args"';
break;
}
}
_callClient(player as Player, TRIGGER_EVENT, [name, args], { noRet: 1 });
}
/**
* Triggers an event registered on the server.
*
* Can be called from any environment.
*
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerServer(name: string, args?: any){
if(arguments.length < 1 || arguments.length > 2) throw 'triggerServer expects 1 or 2 arguments: "name", and optional "args"';
_callServer(TRIGGER_EVENT, [name, args], { noRet: 1 });
}
/**
* Triggers an event registered in any browser context.
*
* Can be called from any environment.
*
* @param player - The player to call the procedure on.
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerBrowsers(player: Player | string, name?: string | any, args?: any){
switch(environment){
case 'client':
case 'cef':
args = name;
name = player;
player = null;
if(arguments.length < 1 || arguments.length > 2) throw 'triggerBrowsers from the client or browser expects 1 or 2 arguments: "name", and optional "args"';
break;
case 'server':
if(arguments.length < 2 || arguments.length > 3) throw 'triggerBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"';
break;
}
_callClient(player as Player, TRIGGER_EVENT_BROWSERS, [name, args], { noRet: 1 });
}
/**
* Triggers an event registered in a specific browser instance.
*
* Client-side environment only.
*
* @param browser - The browser instance.
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerBrowser(browser: Browser, name: string, args?: any){
if(environment !== 'client') throw 'callBrowser can only be used in the client environment';
if(arguments.length < 2 || arguments.length > 4) throw 'callBrowser expects 2 or 3 arguments: "browser", "name", and optional "args"';
_callBrowser(browser, TRIGGER_EVENT, [name, args], { noRet: 1});
}
export default {
register,
unregister,
call,
callServer,
callClient,
callBrowsers,
callBrowser,
on,
off,
trigger,
triggerServer,
triggerClient,
triggerBrowsers,
triggerBrowser
};
+122
View File
@@ -0,0 +1,122 @@
enum MpTypes {
Blip = 'b',
Checkpoint = 'cp',
Colshape = 'c',
Label = 'l',
Marker = 'm',
Object = 'o',
Pickup = 'p',
Player = 'pl',
Vehicle = 'v'
}
function isObjectMpType(obj: any, type: MpTypes){
const client = getEnvironment() === 'client';
if(obj && typeof obj === 'object' && typeof obj.id !== 'undefined'){
const test = (type, collection, mpType) => client ? obj.type === type && collection.at(obj.id) === obj : obj instanceof mpType;
switch(type){
case MpTypes.Blip: return test('blip', mp.blips, mp.Blip);
case MpTypes.Checkpoint: return test('checkpoint', mp.checkpoints, mp.Checkpoint);
case MpTypes.Colshape: return test('colshape', mp.colshapes, mp.Colshape);
case MpTypes.Label: return test('textlabel', mp.labels, mp.TextLabel);
case MpTypes.Marker: return test('marker', mp.markers, mp.Marker);
case MpTypes.Object: return test('object', mp.objects, mp.Object);
case MpTypes.Pickup: return test('pickup', mp.pickups, mp.Pickup);
case MpTypes.Player: return test('player', mp.players, mp.Player);
case MpTypes.Vehicle: return test('vehicle', mp.vehicles, mp.Vehicle);
}
}
return false;
}
export function uid(): string {
const first = (Math.random() * 46656) | 0;
const second = (Math.random() * 46656) | 0;
const firstPart = ('000' + first.toString(36)).slice(-3);
const secondPart = ('000' + second.toString(36)).slice(-3);
return firstPart + secondPart;
}
export function getEnvironment(): string {
if ('mp' in window) return 'cef';
if (mp.joaat) return 'server';
else if (mp.game && mp.game.joaat) return 'client';
}
export function stringifyData(data: any): string {
const env = getEnvironment();
return JSON.stringify(data, (_, value) => {
if(env === 'client' || env === 'server' && value && typeof value === 'object'){
let type;
if(isObjectMpType(value, MpTypes.Blip)) type = MpTypes.Blip;
else if(isObjectMpType(value, MpTypes.Checkpoint)) type = MpTypes.Checkpoint;
else if(isObjectMpType(value, MpTypes.Colshape)) type = MpTypes.Colshape;
else if(isObjectMpType(value, MpTypes.Marker)) type = MpTypes.Marker;
else if(isObjectMpType(value, MpTypes.Object)) type = MpTypes.Object;
else if(isObjectMpType(value, MpTypes.Pickup)) type = MpTypes.Pickup;
else if(isObjectMpType(value, MpTypes.Player)) type = MpTypes.Player;
else if(isObjectMpType(value, MpTypes.Vehicle)) type = MpTypes.Vehicle;
if(type) return {
__t: type,
i: typeof value.remoteId === 'number' ? value.remoteId : value.id
};
}
return value;
});
}
export function parseData(data: string): any {
const env = getEnvironment();
return JSON.parse(data, (_, value) => {
if((env === 'client' || env === 'server') && value && typeof value === 'object' && typeof value['__t'] === 'string' && typeof value.i === 'number' && Object.keys(value).length === 2){
const id = value.i;
const type = value['__t'];
let collection;
switch(type){
case MpTypes.Blip: collection = mp.blips; break;
case MpTypes.Checkpoint: collection = mp.checkpoints; break;
case MpTypes.Colshape: collection = mp.colshapes; break;
case MpTypes.Label: collection = mp.labels; break;
case MpTypes.Marker: collection = mp.markers; break;
case MpTypes.Object: collection = mp.objects; break;
case MpTypes.Pickup: collection = mp.pickups; break;
case MpTypes.Player: collection = mp.players; break;
case MpTypes.Vehicle: collection = mp.vehicles; break;
}
if(collection) return collection[env === 'client' ? 'atRemoteId' : 'at'](id);
}
return value;
});
}
export function promiseResolve(result: any): Promise<any> {
return new Promise(resolve => setTimeout(() => resolve(result), 0));
}
export function promiseReject(error: any): Promise<any> {
return new Promise((_, reject) => setTimeout(() => reject(error), 0));
}
export function promiseTimeout(promise: Promise<any>, timeout?: number){
if(typeof timeout === 'number'){
return Promise.race([
new Promise((_, reject) => {
setTimeout(() => reject('TIMEOUT'), timeout);
}),
promise
]);
}else return promise;
}
export function isBrowserValid(browser: Browser): boolean {
try {
browser.url;
}catch(e){ return false; }
return true;
}
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Base",
"exclude": [
"node_modules"
],
"compilerOptions": {
"incremental": false,
"composite": false,
"target": "ES2022",
"experimentalDecorators": true,
"moduleDetection": "auto",
"module": "CommonJS",
"resolveJsonModule": true,
"declaration": false,
"declarationMap": false,
"sourceMap": false,
"downlevelIteration": false,
"inlineSourceMap": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
+12
View File
@@ -0,0 +1,12 @@
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
outDir: './dist',
format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true,
splitting: false,
sourcemap: false,
clean: true,
})
+32
View File
@@ -0,0 +1,32 @@
# Rage Framework (RageFW)
RageFW is a type-safe framework for developing Rage:MP servers. Designed with developers in mind, RageFW brings structure and efficiency to your Rage:MP servers, ensuring robust and maintainable code
## Features
- **Type-Safe Development:** Eliminate runtime errors and enhance code reliability with RageFW comprehensive type safety, making your RP server's development smoother than a Sunday drive through Los Santos
- **Wrapped RPC client:** Communicate effortlessly between server, client and cef with RPC system, wrapped in a cozy custom-typed blanket for your peace of mind
- **Logging System:** Keep track of server activities and debug like a pro with our built-in, feature-rich logging system. After all, even virtual cops need evidence
## Getting Started
You can find out more about our CLI [here](https://git.entityseven.com/entityseven/rage-framework/wiki/CLI)
At the moment automation we have only works via [pnpm](https://pnpm.io/). To scaffold a basic project with minor settings you can use our CLI:
``pnpm create rage-fw``
This will give you a few options, among them, you can find ``Initialize new project``. Use that option to scaffold a new project for yourself using the preferred frontend framework
## Documentation
[Available here](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs+%40+0.0.30-alpha.0.-)
## Contributing
Join our community of developers and contribute to the ongoing development of RageFW. At the moment the only way to contribute is opening issues
## Support
Need help? Reach out via our community forums or contact us directly through our support channels. We're committed to help you as we can
## License
Licensed under **Custom Attribution-NoDerivs Software License**
> *RageFW - because in the world of GTA:RP, nobody has time for type errors*
+7 -4
View File
@@ -1,19 +1,22 @@
{ {
"name": "rage-fw-server", "name": "rage-fw-server",
"version": "0.0.13-alpha.0", "version": "0.1.0",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
"files": [ "files": [
"dist/**/*" "dist/**/*",
"readme.md"
], ],
"scripts": { "scripts": {
"build": "tsup" "build": "tsup"
}, },
"dependencies": { "dependencies": {
"@ragempcommunity/types-server": "^2.1.8",
"rage-fw-shared-types": "workspace:^",
"rage-rpc": "^0.4.0" "rage-rpc": "^0.4.0"
}, },
"peerDependencies": {
"@ragempcommunity/types-server": "^2.1.8",
"rage-fw-shared-types": "workspace:^"
},
"keywords": [], "keywords": [],
"author": "SashaGoncharov19", "author": "SashaGoncharov19",
"license": "MIT", "license": "MIT",
+2
View File
@@ -0,0 +1,2 @@
# RageFW Server
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs)
+117 -19
View File
@@ -1,42 +1,121 @@
import rpc from 'rage-rpc' import rpc from 'rage-rpc'
import Logger from './logger'
import { import {
_CefEventHasArgs,
_ClientEventHasArgs,
_ServerEventHasArgs,
RageFW_CefArgs,
RageFW_CefEvent,
RageFW_CefReturn,
RageFW_ClientEvent, RageFW_ClientEvent,
RageFW_ClientEventArguments, RageFW_ICustomServerEvent,
RageFW_ClientEventReturn, RageFW_ServerClientEventArguments,
RageFW_ServerClientEventReturn,
RageFW_ServerEvent, RageFW_ServerEvent,
RageFW_ServerEventArguments,
RageFW_ServerEventCallback, RageFW_ServerEventCallback,
} from './types.js' RageFW_ServerEventCallbackCustom,
RageFW_ServerEventCallbackNative,
RageFW_ServerEventReturn,
} from './types'
import { nativeEvents } from './native.events' import { nativeEvents } from './native.events'
class Server { class Server {
private isNativeEvent(eventName: string): eventName is keyof IServerEvents {
return nativeEvents.includes(eventName)
}
private registerCustom<EventName extends keyof RageFW_ICustomServerEvent>(
eventName: EventName,
callback: RageFW_ServerEventCallbackCustom<EventName>,
): void {
rpc.register(
eventName,
async (args: RageFW_ServerEventArguments<EventName>, info) => {
callback([info.player as PlayerMp, ...args])
},
)
}
private registerNative<EventName extends keyof IServerEvents>(
eventName: EventName,
callback: RageFW_ServerEventCallbackNative<EventName>,
): void {
mp.events.add(
eventName,
(...args: Parameters<IServerEvents[EventName]>) =>
callback([...args]),
)
}
public register<EventName extends RageFW_ServerEvent>( public register<EventName extends RageFW_ServerEvent>(
eventName: EventName, eventName: EventName,
callback: RageFW_ServerEventCallback<EventName>, callback: RageFW_ServerEventCallback<EventName>,
): void { ): void {
if (nativeEvents.includes(eventName)) { if (this.isNativeEvent(eventName)) {
mp.events.add(eventName, callback) this.registerNative(
eventName,
callback as RageFW_ServerEventCallbackNative,
)
} else { } else {
rpc.register(eventName, callback as rpc.ProcedureListener) this.registerCustom(
eventName,
callback as unknown as RageFW_ServerEventCallbackCustom,
)
} }
} }
public registerMany<EventName extends RageFW_ServerEvent>(events: { public registerMany<EventName extends RageFW_ServerEvent>(events: {
[event in EventName]: RageFW_ServerEventCallback<event> [event in EventName]: RageFW_ServerEventCallback<event>
}): void { }): void {
Object.entries(events).map(([eventName, callback]) => { Object.entries<RageFW_ServerEventCallback<EventName>>(events).map(
if (nativeEvents.includes(eventName)) { ([eventName, callback]) => {
mp.events.add( if (this.isNativeEvent(eventName)) {
eventName as keyof IServerEvents, this.registerNative(
callback as (...arg: unknown[]) => void, eventName,
callback as RageFW_ServerEventCallbackNative,
) )
} else { } else {
rpc.register(eventName, (args: unknown[]) => { this.registerCustom(
return Array.isArray(args) eventName as keyof RageFW_ICustomServerEvent,
? (callback as (...arg: typeof args) => void)(...args) callback as unknown as RageFW_ServerEventCallbackCustom,
: (callback as (arg: typeof args) => void)(args) )
})
} }
}) },
)
}
private unregisterCustom<EventName extends keyof RageFW_ICustomServerEvent>(
eventName: EventName,
): void {
rpc.unregister(eventName)
}
private unregisterNative<EventName extends keyof IServerEvents>(
eventName: EventName,
): void {
mp.events.remove(eventName)
}
public unregister<EventName extends RageFW_ServerEvent>(
eventName: EventName,
): void {
if (this.isNativeEvent(eventName)) {
this.unregisterNative(eventName)
} else {
this.unregisterCustom(eventName)
}
}
public trigger<EventName extends keyof RageFW_ICustomServerEvent>(
eventName: EventName,
...args: _ServerEventHasArgs<EventName> extends true
? [RageFW_ServerEventArguments<EventName>]
: []
): Promise<RageFW_ServerEventReturn<EventName>> {
return rpc.call<RageFW_ServerEventReturn<EventName>>(eventName, args)
} }
} }
@@ -44,13 +123,32 @@ class Player {
public triggerClient<EventName extends RageFW_ClientEvent>( public triggerClient<EventName extends RageFW_ClientEvent>(
player: PlayerMp, player: PlayerMp,
eventName: EventName, eventName: EventName,
...args: RageFW_ClientEventArguments<EventName> ...args: _ClientEventHasArgs<EventName> extends true
): Promise<RageFW_ClientEventReturn<EventName>> { ? [RageFW_ServerClientEventArguments<EventName>]
: []
): Promise<RageFW_ServerClientEventReturn<EventName>> {
return rpc.callClient(player, eventName, args) return rpc.callClient(player, eventName, args)
} }
public triggerBrowser<EventName extends RageFW_CefEvent>(
player: PlayerMp,
eventName: EventName,
...args: _CefEventHasArgs<EventName> extends true
? [RageFW_CefArgs<EventName>]
: []
): Promise<RageFW_CefReturn<EventName>> {
return rpc.callBrowsers(player, eventName, args)
}
} }
export const fw = { export const fw = {
event: new Server(), event: new Server(),
player: new Player(), player: new Player(),
system: {
log: new Logger(),
},
} }
fw.system.log.info(
'Working on Rage Framework. RageFW © Powered by Entity Seven Group',
)
+36
View File
@@ -0,0 +1,36 @@
import winston, { format } from 'winston'
const { timestamp, printf, colorize } = format
export default class Logger {
private format = printf(({ message, level, timestamp }) => {
return `[${new Date(timestamp).toLocaleTimeString()}] [${level}]: ${message}`
})
private systemLogger = winston.createLogger({
transports: [new winston.transports.Console()],
format: format.combine(
colorize({
level: true,
colors: {
error: 'red',
warn: 'yellow',
info: 'white',
},
}),
timestamp(),
this.format,
),
})
public info(message: unknown) {
this.systemLogger.info(message)
}
public warn(message: unknown) {
this.systemLogger.warn(message)
}
public error(message: unknown) {
this.systemLogger.error(message)
}
}
-33
View File
@@ -1,33 +0,0 @@
/// <reference types="@ragempcommunity/types-server" />
import type {
RageFW_ICustomServerEvent,
RageFW_ICustomClientEvent,
} from 'rage-fw-shared-types'
export type RageFW_ServerEvent =
| keyof RageFW_ICustomServerEvent
| keyof IServerEvents
export type RageFW_ServerEventCallback<
K extends keyof RageFW_ICustomServerEvent | keyof IServerEvents,
> = K extends keyof RageFW_ICustomServerEvent
? (
player: PlayerMp,
...args: Parameters<RageFW_ICustomServerEvent[K]>
) => ReturnType<RageFW_ICustomServerEvent[K]>
: K extends keyof IServerEvents
? ThisifyServerEvents[K]
: never
export type RageFW_ClientEvent = keyof RageFW_ICustomClientEvent
export type RageFW_ClientEventArguments<K extends RageFW_ClientEvent> =
K extends RageFW_ClientEvent
? Parameters<RageFW_ICustomClientEvent[K]>
: never
export type RageFW_ClientEventReturn<K extends RageFW_ClientEvent> =
K extends RageFW_ClientEvent
? ReturnType<RageFW_ICustomClientEvent[K]>
: never
+18
View File
@@ -0,0 +1,18 @@
import type { RageFW_ICustomCefEvent } from 'rage-fw-shared-types'
export type RageFW_CefEvent = keyof RageFW_ICustomCefEvent
export type RageFW_CefArgs<K extends RageFW_CefEvent> = Parameters<
RageFW_ICustomCefEvent[K]
>
export type RageFW_CefReturn<K extends RageFW_CefEvent> = ReturnType<
RageFW_ICustomCefEvent[K]
>
export type _CefEventHasArgs<EventName extends keyof RageFW_ICustomCefEvent> =
keyof RageFW_ICustomCefEvent extends never
? false
: Parameters<RageFW_ICustomCefEvent[EventName]>[0] extends undefined
? false
: true
+35
View File
@@ -0,0 +1,35 @@
/// <reference types="@ragempcommunity/types-server" />
import type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types'
/**
* Union of all available client event names
* These only include custom events
*/
export type RageFW_ClientEvent = keyof RageFW_ICustomClientEvent
/**
* Array of arguments of an event you pass as a generic
* These only include custom events
*/
export type RageFW_ServerClientEventArguments<K extends RageFW_ClientEvent> =
K extends RageFW_ClientEvent
? Parameters<RageFW_ICustomClientEvent[K]>
: never
/**
* Return type of event you pass as a generic
* These only include custom events
*/
export type RageFW_ServerClientEventReturn<K extends RageFW_ClientEvent> =
K extends RageFW_ClientEvent
? ReturnType<RageFW_ICustomClientEvent[K]>
: never
export type _ClientEventHasArgs<
EventName extends keyof RageFW_ICustomClientEvent,
> = keyof RageFW_ICustomClientEvent extends never
? false
: Parameters<RageFW_ICustomClientEvent[EventName]>[0] extends undefined
? false
: true
+3
View File
@@ -0,0 +1,3 @@
export * from './client'
export * from './server'
export * from './browser'
+74
View File
@@ -0,0 +1,74 @@
/// <reference types="@ragempcommunity/types-server" />
import type {
RageFW_ICustomClientEvent,
RageFW_ICustomServerEvent,
} from 'rage-fw-shared-types'
export type { RageFW_ICustomServerEvent } from 'rage-fw-shared-types'
/**
* Union of all available server event names
* These also include system events
*/
export type RageFW_ServerEvent =
| keyof RageFW_ICustomServerEvent
| keyof IServerEvents
/**
* Array of arguments for an event, name of which you pass as a generic
* These also include system events
*/
export type RageFW_ServerEventArguments<K extends RageFW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent
? Parameters<RageFW_ICustomServerEvent[K]>
: K extends keyof IServerEvents
? Parameters<IServerEvents[K]>
: never
/**
* Callback (function) for an event, name of which you pass as a generic
* These include system and custom events
*/
export type RageFW_ServerEventCallback<K extends RageFW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent
? RageFW_ServerEventCallbackCustom<K>
: K extends keyof IServerEvents
? RageFW_ServerEventCallbackNative<K>
: never
/**
* Return type for an event, name of which you pass as a generic
* These include system and custom events
*/
export type RageFW_ServerEventReturn<K extends RageFW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent
? ReturnType<RageFW_ICustomServerEvent[K]>
: K extends keyof IServerEvents
? ReturnType<IServerEvents[K]>
: never
/**
* Array of arguments for an event, name of which you pass as a generic
* These only include custom events
*/
export type RageFW_ServerEventCallbackCustom<
K extends keyof RageFW_ICustomServerEvent = keyof RageFW_ICustomServerEvent,
> = (
payload: [player: PlayerMp, ...args: RageFW_ServerEventArguments<K>],
) => RageFW_ServerEventReturn<K>
/**
* Array of arguments for an event, name of which you pass as a generic
* These only include system events
*/
export type RageFW_ServerEventCallbackNative<
K extends keyof IServerEvents = keyof IServerEvents,
> = (payload: Parameters<IServerEvents[K]>) => ReturnType<IServerEvents[K]>
export type _ServerEventHasArgs<
EventName extends keyof RageFW_ICustomServerEvent,
> = keyof RageFW_ICustomClientEvent extends never
? false
: Parameters<RageFW_ICustomServerEvent[EventName]>[0] extends undefined
? false
: true
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "rage-fw-shared-types", "name": "rage-fw-shared-types",
"version": "0.0.13-alpha.0", "version": "0.1.0",
"types": "types/types/index.d.ts", "types": "types/types/index.d.ts",
"files": [ "files": [
"types/**/*" "types/**/*"
+2
View File
@@ -0,0 +1,2 @@
# RageFW Shared types
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs)
+2
View File
@@ -2,4 +2,6 @@ declare module 'rage-fw-shared-types' {
export interface RageFW_ICustomServerEvent {} export interface RageFW_ICustomServerEvent {}
export interface RageFW_ICustomClientEvent {} export interface RageFW_ICustomClientEvent {}
export interface RageFW_ICustomCefEvent {}
} }
-17
View File
@@ -1,17 +0,0 @@
{
"name": "rage-fw-shared",
"version": "0.0.13-alpha.0",
"type": "module",
"main": "index.d.ts",
"files": [
"index.d.ts"
],
"scripts": {
"test": "tsup"
},
"keywords": [],
"author": "SashaGoncharov19",
"license": "MIT",
"description": "Shared client types for rage-fw",
"gitHead": "053e4fd12aa120d53e11e0d2009c0df78c1a2ad0"
}
+5 -1
View File
@@ -1,7 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"lib": ["ESNext","ES2019"], "lib": [
"ESNext",
"ES2019",
"dom"
],
"moduleResolution": "node", "moduleResolution": "node",
"module": "ESNext", "module": "ESNext",
"esModuleInterop": true, "esModuleInterop": true,