Compare commits

..
60 Commits
Author SHA1 Message Date
rilaxik 12d2197e18 upd (fw/rpc):
- (rpc) removed unnecessary return type generics
- (fw) implemented synchronous versions of rpc events to omit waiting for response
- (fw) more descriptive docs
2025-01-31 21:40:16 +00:00
rilaxik 897fd5a9f5 upd (rpc):
- separated async functions from non-async functions
- correct timeout errors
- jsdoc tweaks
2025-01-31 18:43:27 +00:00
rilaxik d5dc6924aa upd (fw):
- temp removed internal event
- logger now also displays ms
2025-01-31 16:26:21 +00:00
rilaxik 78d1831bbc upd (fw):
- correct types export
2025-01-31 09:55:20 +00:00
rilaxik a820e9896b upd (fw):
- fixed import names for client/server
- added "access": "public" field for fw packages
2025-01-30 19:52:19 +00:00
rilaxik edafb5289e upd (fw):
- fixed inconsistent rpc build
- changed project-wide naming convention to prefix with "FW_" instead of "RageFW_"
2025-01-30 19:40:09 +00:00
rilaxik 7a37aa390e upd (fw):
- moved internal client events from extendable types
2025-01-30 19:03:30 +00:00
rilaxik e29dc20398 upd (fw):
- deployment config + scripts
- License bump
2025-01-29 17:34:03 +00:00
rilaxik 82050004f6 upd (fw):
- sharing same tsconfig
2025-01-29 14:57:58 +00:00
rilaxik 179d7b8b93 upd (fw):
- eslint config
- correct tsconfigs
2025-01-28 22:44:54 +00:00
rilaxik 978d15bd25 Merge remote-tracking branch 'origin/dev' into dev 2025-01-28 21:06:42 +00:00
rilaxik 4e48772950 upd (fw):
- using syncpack to maintain dependencies consistency
- testing lerna
2025-01-28 21:06:25 +00:00
rilaxik 84994ef121 upd (fw):
- using syncpack to maintain dependencies consistency
- testing lerna
2025-01-28 21:06:12 +00:00
rilaxik 166b933b71 upd (fw): configs 2025-01-28 19:04:16 +00:00
Oleksandr Honcharov e3d7a66646 license update 2025-01-28 17:06:01 +02:00
Oleksandr Honcharov f977e6191f added skipLibCheck for RPC 2025-01-28 16:56:57 +02:00
Oleksandr Honcharov 593f4650a3 versions changes / code improvements 2025-01-28 16:53:44 +02:00
Oleksandr Honcharov d56c52abf5 lock file 2025-01-28 16:38:10 +02:00
rilaxik f5648abb00 upd (fw): versions bump + configs 2025-01-28 14:37:29 +00:00
rilaxik 47375c0a15 upd (fw): versions bump + configs 2025-01-28 14:35:38 +00:00
rilaxik b55204ee61 upd (cli): repos bump 2025-01-27 01:35:19 +00:00
rilaxik 51f50141ab dx
removed type exports and changed export path to enable symlink while developing
2025-01-26 21:18:36 +00:00
rilaxik 495217ecd5 upd cli
- handled process exit
- updated docs + readme
- typos fix
2024-10-30 18:31:53 +00:00
rilaxik 7e43e0d106 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	cli/package.json
#	rpc/package.json
2024-10-30 17:01:42 +00:00
rilaxik 2f1d1d9f0d hotfix
- licenses
- package.json consistency
- links bump
2024-10-30 17:00:36 +00:00
rilaxik cc40d90070 hotfix
- licenses
- package.json consistency
- links bump
2024-10-30 16:59:33 +00:00
rilaxik 469d970654 Merge branch 'dev' 2024-10-30 16:29:29 +00:00
rilaxik 217e0ddf01 Merge pull request 'Server/Client middlewares + JSDoc' (#6) from unstable into dev
Reviewed-on: #6
2024-10-30 16:21:29 +00:00
rilaxik ea572070ef versions rollback 2024-10-30 16:21:03 +00:00
rilaxik 7308fc26e0 v0.2.1-0.2.0-alpha.2.0 2024-10-30 16:18:08 +00:00
rilaxik ffd542c1de v0.2.1-0.2.0-alpha.1.0 2024-10-30 16:17:28 +00:00
rilaxik 914300924a v0.2.0 2024-10-30 16:16:45 +00:00
rilaxik bc295a777b jsdoc 2024-10-30 15:38:06 +00:00
rilaxik cd27764a30 jsdoc 2024-10-30 15:27:22 +00:00
rilaxik 30d125ce8e version bump 2024-10-29 17:06:14 +00:00
rilaxik d56475d08a Merge pull request 'Consistency updates + cleanup' (#5) from unstable into dev
Reviewed-on: #5
2024-10-29 17:03:31 +00:00
rilaxik 622676d189 feat core
- server/client middlewares
2024-10-29 17:03:07 +00:00
rilaxik d8df9fa7ef fix core
- renaming done
2024-10-28 17:40:25 +00:00
rilaxik d9b917c024 core version bump 2024-10-28 17:37:17 +00:00
rilaxik cbbf4f38e6 upd core
- typed server inner rpc
- client minor fixes
- sharing browser with rpc on client
2024-10-28 17:30:28 +00:00
rilaxik 3bbf1e2209 upd core
- typed client inner rpc
- better type organization
2024-10-28 16:53:42 +00:00
rilaxik 9fe9bad840 upd core
- renamed cef -> browser
- added browser logger + custom logger option
2024-10-28 16:35:16 +00:00
rilaxik 588bb42d04 upd core
- types now use namespace to avoid bloating
- moved items for further usage into data folder
2024-10-28 15:20:43 +00:00
rilaxik 3a4fe19df0 upd readme bump 2024-10-28 15:14:41 +00:00
rilaxik c8e5bdf375 Merge branch 'dev' into unstable 2024-10-28 15:10:25 +00:00
rilaxik a675114b41 upd core
- changed local shared types package name
2024-10-28 15:10:00 +00:00
rilaxik 54781d50b2 upd core
- changed local rpc to remote dependency
2024-10-28 15:04:39 +00:00
rilaxik de33fe4763 versions bump 2024-10-28 13:20:51 +00:00
rilaxik d3d75b338d v0.2.5 2024-10-28 13:02:24 +00:00
rilaxik 04eb724073 v0.2.4 2024-10-28 12:59:44 +00:00
rilaxik e4950b6c99 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	lerna.json
#	rpc/package.json
2024-10-28 12:57:58 +00:00
Oleksandr Honcharov 8f856d929f v0.2.3 2024-10-28 14:54:48 +02:00
Oleksandr Honcharov ee8210f140 v0.2.2 2024-10-28 14:54:19 +02:00
Oleksandr Honcharov 74c0215366 v0.2.2 2024-10-28 14:53:05 +02:00
rilaxik 19b1b770f6 v0.2.1 2024-10-28 12:51:53 +00:00
rilaxik c2684a48fc Merge pull request 'Rpc integration + type fixes' (#3) from dev into master
Reviewed-on: #3
2024-10-28 12:13:19 +00:00
sashagoncharov19 0059a41d0d Update readme.md 2024-06-17 03:47:57 +00:00
rilaxik cc1ebbd66b quickfix issue template had extra optional label 2024-06-15 14:54:07 +01:00
sashagoncharov19 301a65e77f v0.1.0
Reviewed-on: #2
2024-06-15 13:50:13 +00:00
sashagoncharov19 57d80d41d6 Merge pull request 'v0.1.0' (#1) from dev into unstable
Reviewed-on: #1
Reviewed-by: Oleksandr Honcharov <[email protected]>
2024-06-15 13:48:40 +00:00
92 changed files with 6207 additions and 10346 deletions
+8
View File
@@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [
["@entityseven/rage-fw-browser","@entityseven/rage-fw-client","@entityseven/rage-fw-server"]
],
"linked": [],
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": []
}
-31
View File
@@ -1,31 +0,0 @@
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',
}
+2 -2
View File
@@ -28,7 +28,7 @@ body:
- type: textarea - type: textarea
id: reproduction id: reproduction
attributes: attributes:
label: Reproduction (Optional) label: Reproduction
description: Steps to reproduce the bug, if applicable description: Steps to reproduce the bug, if applicable
validations: validations:
required: true required: true
@@ -57,7 +57,7 @@ body:
- type: textarea - type: textarea
id: additional_information id: additional_information
attributes: attributes:
label: Additional Information label: Additional Information (Optional)
description: Add any other information that might be useful in diagnosing the issue description: Add any other information that might be useful in diagnosing the issue
validations: validations:
required: false required: false
+2 -1
View File
@@ -1,4 +1,5 @@
.idea .idea
node_modules node_modules
dist dist
.nx/cache
+33
View File
@@ -0,0 +1,33 @@
{
"dependencyTypes": ["prod", "peer", "dev"],
"versionGroups": [
{
"label": "Ensure @ragempcommunity/types-* always match",
"packages": ["**"],
"dependencies": ["@ragempcommunity/types-cef","@ragempcommunity/types-client", "@ragempcommunity/types-server"],
"pinVersion": "sameRange",
"range": "^"
},
{
"label": "Use workspace protocol when developing local packages",
"packages": ["**"],
"dependencies": ["@entityseven/rage-fw-shared-types", "@entityseven/rage-fw-rpc"]
"pinVersion": "workspace:*",
"range": ""
},
{
"label": "Ensure @types/node match same version (for tsup)",
"packages": ["**"],
"dependencies": ["@types/node"],
"pinVersion": "sameRange",
"range": ""
},
{
"label": "Ensure newest breaking Typescript version",
"packages": ["**"],
"dependencies": ["typescript"],
"pinVersion": "sameRange",
"range": "^"
}
]
}
+1 -1
View File
@@ -1,4 +1,4 @@
# Custom Attribution-NoDerivs Software License # Custom Attribution-NoDerives 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: This license allows you to use, copy, and distribute the RageFW (the "Software"), including for commercial purposes, provided that the following conditions are met:
+15
View File
@@ -0,0 +1,15 @@
Custom Attribution-NoDerives Software License
Copyright (c) 2024 Entity Seven Group
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.
+61
View File
@@ -0,0 +1,61 @@
{
"private": false,
"name": "@entityseven/rage-fw-browser",
"version": "0.2.0",
"files": [
"dist/**/*",
"readme.md",
"LICENSE",
"package.json"
],
"main": "src/index.ts",
"exports": {
".": "./src/index.ts"
},
"publishConfig": {
"access": "public",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js"
}
},
"scripts": {
"build": "tsup"
},
"dependencies": {
"@entityseven/rage-fw-rpc": "workspace:*"
},
"devDependencies": {
"@types/node": "22.12.0"
},
"peerDependencies": {
"@entityseven/rage-fw-shared-types": "workspace:*",
"@ragempcommunity/types-cef": "^2.1.8",
"typescript": "^5.7.3"
},
"description": "Package used on a browser-side of your Rage:MP Server",
"keywords": [
"ragefw",
"rage-fw",
"ragemp",
"rage:mp",
"rage",
"gta5"
],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
},
{
"name": "Oleksandr Honcharov",
"email": "[email protected]",
"url": "https://github.com/SashaGoncharov19/"
}
],
"license": "Custom-Attribution-NoDerivs",
"gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
}
+2 -2
View File
@@ -1,2 +1,2 @@
# RageFW CEF # RageFW Browser
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs) [Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki)
+268
View File
@@ -0,0 +1,268 @@
import { FW_Helper } from './helper'
import { rpc } from './rpc'
import type * as T from '../types'
/** Browser-side interactions */
export class FW_Browser extends FW_Helper {
constructor() {
super()
}
/**
* Setter. Enables console debug logs for events
*/
set debugLogs(debug: boolean) {
this.debugLogs_ = debug
}
/**
* Setter. Provides an ability to specify custom logger function to get special formatting. Using this enables ``debugLogs``
*/
set customLogger(
fn: (method: string, eventName: string, ...args: unknown[]) => unknown,
) {
this.customLogger_ = fn
}
/**
* Registers a browser event with an associated callback
*
* @param eventName - The name of the event to register
* @param callback - The callback function to be executed when the event is triggered
* @returns {FW_Browser} The current browser instance, enabling method chaining
*
* @example
* // Registering an event
* fw.event.register("showNotification", (message, color) => {
* // do something
* })
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public register<EventName extends T.FW_BrowserEvent>(
eventName: EventName,
callback: T.FW_BrowserCallback<EventName>,
): FW_Browser {
this.log_('register', eventName, callback)
rpc.register<
Parameters<typeof callback>,
ReturnType<typeof callback>,
EventName
>(eventName, async (...data) => await callback(...data))
return this
}
/**
* Unregisters a browser event, removing the associated callback
*
* @param eventName - The name of the event to unregister
* @returns {FW_Browser} The current browser instance, enabling method chaining
*
* @example
* // Unregistering an event
* fw.event.unregister("showNotification")
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public unregister<EventName extends T.FW_BrowserEvent>(
eventName: EventName,
): FW_Browser {
rpc.unregister<EventName>(eventName)
return this
}
/**
* Triggers a browser event from the browser with arguments from 'rage-fw-shared-types'
*
* Formerly known as ``call`` or ``emit``
*
* @param eventName - The name of the browser event to trigger
* @param [args] - Arguments for the browser event, if present
* @returns {void}
*
* @example
* // Triggering a browser event without arguments
* fw.event.trigger("browserEventName")
*
* @example
* // Triggering a browser event with arguments
* fw.event.trigger("browserEventName", ["message to me"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public trigger<EventName extends T.FW_BrowserEvent>(
eventName: EventName,
...args: T._BrowserEventHasArgs<EventName> extends true
? [T.FW_BrowserArgs<EventName>]
: []
): void {
this.log_('[RPC](trigger):', eventName, ...args)
rpc.call<typeof args, EventName>(eventName, args)
}
/**
* Triggers an asynchronous browser event from the browser with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callAsync`` or ``emitAsync``
*
* @param eventName - The name of the browser event to trigger
* @param [args] - Arguments for the browser event, if present
* @returns {Promise} resolving to the browser's response for the event
*
* @example
* // Triggering a browser event without arguments
* fw.event.trigger("browserEventName")
*
* @example
* // Triggering a browser event with arguments
* fw.event.trigger("browserEventName", ["message to me"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerAsync<EventName extends T.FW_BrowserEvent>(
eventName: EventName,
...args: T._BrowserEventHasArgs<EventName> extends true
? [T.FW_BrowserArgs<EventName>]
: []
): Promise<T.FW_BrowserReturn<EventName>> {
this.log_('[RPC](trigger):', eventName, ...args)
return await rpc.callAsync<
typeof args,
EventName,
T.FW_BrowserReturn<EventName>
>(eventName, args)
}
/**
* Triggers a server event from the browser with arguments from 'rage-fw-shared-types'
*
* Formerly known as ``callServer`` or ``emitServer``
*
* @param eventName - The name of the server event to trigger
* @param [args] - Arguments for the server event, if present
* @returns {void}
*
* @example
* // Triggering a server event without arguments
* fw.event.triggerServer("serverEventName")
*
* @example
* // Triggering a server event with arguments
* fw.event.triggerServer("serverEventName", ["message to server"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public triggerServer<EventName extends T.FW_ServerEvent>(
eventName: EventName,
...args: T._ServerEventHasArgs<EventName> extends true
? [T.FW_ServerArgs<EventName>]
: []
): void {
this.log_('[RPC](triggerServer):', eventName, ...args)
rpc.callServer<typeof args, EventName>(eventName, args)
}
/**
* Triggers an asynchronous server event from the browser with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callServerAsync`` or ``emitServerAsync``
*
* @param eventName - The name of the server event to trigger
* @param [args] - Arguments for the server event, if present
* @returns {Promise} resolving to the server's response for the event
*
* @example
* // Triggering a server event without arguments
* fw.event.triggerServer("serverEventName")
*
* @example
* // Triggering a server event with arguments
* fw.event.triggerServer("serverEventName", ["message to server"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerServerAsync<EventName extends T.FW_ServerEvent>(
eventName: EventName,
...args: T._ServerEventHasArgs<EventName> extends true
? [T.FW_ServerArgs<EventName>]
: []
): Promise<T.FW_ServerReturn<EventName>> {
this.log_('[RPC](triggerServer):', eventName, ...args)
return await rpc.callServerAsync<
typeof args,
EventName,
T.FW_ServerReturn<EventName>
>(eventName, args)
}
/**
* Triggers a browser event from the browser with arguments from 'rage-fw-shared-types'
*
* Formerly known as ``callClient`` or ``emitClient``
*
* @param eventName - The name of the client event to trigger
* @param [args] - Arguments for the client event, if present
* @returns {void}
*
* @example
* // Triggering a client event without arguments
* fw.event.triggerClient("clientEventName")
*
* @example
* // Triggering a client event with arguments
* fw.event.triggerClient("clientEventName", ["message to client"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public triggerClient<EventName extends T.FW_ClientEvent>(
eventName: EventName,
...args: T._ClientEventHasArgs<EventName> extends true
? [T.FW_ClientArgs<EventName>]
: []
): void {
this.log_('[RPC](triggerClient):', eventName, ...args)
rpc.callClient<typeof args, EventName>(eventName, args)
}
/**
* Triggers an asynchronous client event from the browser with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callClientAsync`` or ``emitClientAsync``
*
* @param eventName - The name of the client event to trigger
* @param [args] - Arguments for the client event, if present
* @returns {Promise} resolving to the client's response for the event
*
* @example
* // Triggering a client event without arguments
* fw.event.triggerClient("clientEventName")
*
* @example
* // Triggering a client event with arguments
* fw.event.triggerClient("clientEventName", ["message to client"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerClientAsync<EventName extends T.FW_ClientEvent>(
eventName: EventName,
...args: T._ClientEventHasArgs<EventName> extends true
? [T.FW_ClientArgs<EventName>]
: []
): Promise<T.FW_ClientReturn<EventName>> {
this.log_('[RPC](triggerClient):', eventName, ...args)
return await rpc.callClientAsync<
typeof args,
EventName,
T.FW_ClientReturn<EventName>
>(eventName, args)
}
}
+21
View File
@@ -0,0 +1,21 @@
export class FW_Helper {
protected debugLogs_: boolean = false
protected customLogger_:
| undefined
| ((method: string, eventName: string, ...args: unknown[]) => unknown) =
undefined
constructor() {}
protected log_(
method: string,
eventName: string,
...args: unknown[]
): void {
if (this.customLogger_) {
this.customLogger_(method, eventName, ...args)
} else if (this.debugLogs_) {
console.log('[RPC](' + method + ')', eventName + ':', ...args)
}
}
}
+3
View File
@@ -0,0 +1,3 @@
export * from './browser'
export * from './helper'
export * from './rpc'
+6
View File
@@ -0,0 +1,6 @@
import { Rpc } from '@entityseven/rage-fw-rpc'
export const rpc = new Rpc({
forceBrowserDevMode: process.env.RageFW_forceBrowserDevMode === 'true',
debugLogs: false,
})
+13
View File
@@ -0,0 +1,13 @@
import { FW_Browser, rpc } from './core'
/**
* Package used on a browser-side of your Rage:MP Server
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
export const fw = {
/** Browser-side interactions */
event: new FW_Browser(),
/** ``rage-fw-rpc`` instance used under the hood. It is highly recommended to use this one if you need it instead of creating a new instance */
rpc,
}
+41
View File
@@ -0,0 +1,41 @@
import type { FW_ICustomBrowserEvent } from '@entityseven/rage-fw-shared-types'
export type { FW_ICustomBrowserEvent } from '@entityseven/rage-fw-shared-types'
/**
* Union of all available browser event names
* These only include custom events
*/
export type FW_BrowserEvent = keyof FW_ICustomBrowserEvent
/**
* Array of arguments of an event you pass as a generic
* These only include custom browser events
*/
export type FW_BrowserArgs<K extends FW_BrowserEvent> = Parameters<
FW_ICustomBrowserEvent[K]
>
/**
* Return type of event you pass as a generic
* These only include custom browser events
*/
export type FW_BrowserReturn<K extends FW_BrowserEvent> = ReturnType<
FW_ICustomBrowserEvent[K]
>
/**
* Callback (function) of event you pass as a generic
* These only include custom browser events
*/
export type FW_BrowserCallback<K extends keyof FW_ICustomBrowserEvent> = (
...args: FW_BrowserArgs<K>
) => Promise<FW_BrowserReturn<K>>
export type _BrowserEventHasArgs<
EventName extends keyof FW_ICustomBrowserEvent,
> = keyof FW_ICustomBrowserEvent extends never
? false
: Parameters<FW_ICustomBrowserEvent[EventName]>[0] extends undefined
? false
: true
+32
View File
@@ -0,0 +1,32 @@
import type { FW_ICustomClientEvent } from '@entityseven/rage-fw-shared-types'
export type { FW_ICustomClientEvent } from '@entityseven/rage-fw-shared-types'
/**
* Union of all available client event names
* These only include custom events and some internals for RageFW
*/
export type FW_ClientEvent = keyof FW_ICustomClientEvent
/**
* Array of arguments of event you pass as a generic
* These only include custom client events
*/
export type FW_ClientArgs<K extends FW_ClientEvent> = Parameters<
FW_ICustomClientEvent[K]
>
/**
* Return type of event you pass as a generic
* These only include custom client events
*/
export type FW_ClientReturn<K extends FW_ClientEvent> = ReturnType<
FW_ICustomClientEvent[K]
>
export type _ClientEventHasArgs<EventName extends keyof FW_ICustomClientEvent> =
keyof FW_ICustomClientEvent extends never
? false
: Parameters<FW_ICustomClientEvent[EventName]>[0] extends undefined
? false
: true
+3
View File
@@ -0,0 +1,3 @@
export type * from './browser'
export type * from './client'
export type * from './server'
+32
View File
@@ -0,0 +1,32 @@
import type { FW_ICustomServerEvent } from '@entityseven/rage-fw-shared-types'
export type { FW_ICustomServerEvent } from '@entityseven/rage-fw-shared-types'
/**
* Union of all available server event names
* These only include custom events
*/
export type FW_ServerEvent = keyof FW_ICustomServerEvent
/**
* Array of arguments of event you pass as a generic
* These only include custom server events
*/
export type FW_ServerArgs<K extends FW_ServerEvent> = Parameters<
FW_ICustomServerEvent[K]
>
/**
* Return type of event you pass as a generic
* These only include custom server events
*/
export type FW_ServerReturn<K extends FW_ServerEvent> = ReturnType<
FW_ICustomServerEvent[K]
>
export type _ServerEventHasArgs<EventName extends keyof FW_ICustomServerEvent> =
keyof FW_ICustomServerEvent extends never
? false
: Parameters<FW_ICustomServerEvent[EventName]>[0] extends undefined
? false
: true
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Browser",
"compilerOptions": {
"types": ["./node_modules/@ragempcommunity/types-cef/index.d.ts", "node"]
},
"extends": ["../tsconfig.ragefw.json"]
}
@@ -1,12 +1,12 @@
import { defineConfig } from 'tsup' import { defineConfig } from 'tsup'
export default defineConfig({ export default defineConfig({
entry: ['src/index.ts'], entry: ['src/**/*.ts'],
outDir: './dist', outDir: './dist',
format: ['cjs'], format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true, experimentalDts: true,
splitting: false, splitting: false,
bundle: false,
sourcemap: false, sourcemap: false,
clean: true, clean: true,
}) })
-29
View File
@@ -1,29 +0,0 @@
{
"name": "@entityseven/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-fw-rpc": "workspace:^"
},
"peerDependencies": {
"@ragempcommunity/types-cef": "^2.1.8",
"rage-fw-shared-types": "workspace:^"
},
"description": "RageFW CEF side",
"keywords": [],
"author": "SashaGoncharov19",
"contributors": [{
"name": "rilaxik",
"email": "[email protected]",
"url": "https://github.com/rilaxik"
}],
"license": "MIT"
}
-113
View File
@@ -1,113 +0,0 @@
import { Rpc } from 'rage-fw-rpc'
import type {
_CefEventHasArgs,
_ClientEventHasArgs,
_ServerEventHasArgs,
RageFW_CefArgs,
RageFW_CefCallback,
RageFW_CefReturn,
RageFW_ClientArgs,
RageFW_ClientReturn,
RageFW_ICustomCefEvent,
RageFW_ICustomClientEvent,
RageFW_ICustomServerEvent,
RageFW_ServerArgs,
RageFW_ServerReturn,
} from './types'
class Cef {
private _debugMode: boolean = false
private _rpc: Rpc = new Rpc()
constructor() {}
set debug(debug: boolean) {
this._debugMode = debug
}
get rpc(): Rpc {
return this._rpc
}
public register<EventName extends keyof RageFW_ICustomCefEvent>(
eventName: EventName,
callback: RageFW_CefCallback<EventName>,
): void {
if (this._debugMode) {
console.log('[RPC](register):', eventName, callback)
}
if ('mp' in window) {
this._rpc.register(eventName, callback)
}
}
public async trigger<EventName extends keyof RageFW_ICustomCefEvent>(
eventName: EventName,
...args: _CefEventHasArgs<EventName> extends true
? [RageFW_CefArgs<EventName>]
: []
): Promise<RageFW_CefReturn<EventName>> {
if (this._debugMode) {
console.log('[RPC](trigger):', eventName, ...args)
}
if ('mp' in window) {
return await this._rpc.call(eventName, args)
}
return Promise.reject(
'RageFW was started in window which does not contain MP',
)
}
public async triggerServer<
EventName extends keyof RageFW_ICustomServerEvent,
>(
eventName: EventName,
...args: _ServerEventHasArgs<EventName> extends true
? [RageFW_ServerArgs<EventName>]
: []
): Promise<RageFW_ServerReturn<EventName>> {
if (this._debugMode) {
console.log('[RPC](triggerServer):', eventName, ...args)
}
if ('mp' in window) {
return await this._rpc.callServer(eventName, args)
}
return Promise.reject(
'RageFW was started in window which does not contain MP',
)
}
public async triggerClient<
EventName extends keyof RageFW_ICustomClientEvent,
>(
eventName: EventName,
...args: _ClientEventHasArgs<EventName> extends true
? [RageFW_ClientArgs<EventName>]
: []
): Promise<RageFW_ClientReturn<EventName>> {
if (this._debugMode) {
console.log('[RPC](triggerClient):', eventName, ...args)
}
if ('mp' in window) {
return await this._rpc.callClient(eventName, args)
}
return Promise.reject(
'RageFW was started in window which does not contain MP',
)
}
}
export const fw = {
event: new Cef(),
}
;(async () => {
await fw.event.triggerClient('cefReady')
})()
-40
View File
@@ -1,40 +0,0 @@
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_CefArgs<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_CefArgs<K>,
) => Promise<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
-33
View File
@@ -1,33 +0,0 @@
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 and some extras from RageFW
*/
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_ClientArgs<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
@@ -1,3 +0,0 @@
export * from './cef'
export * from './client'
export * from './server'
-33
View File
@@ -1,33 +0,0 @@
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_ServerArgs<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
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Entity Seven Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.
+20 -14
View File
@@ -1,19 +1,21 @@
{ {
"name": "@entityseven/create-rage-fw", "name": "@entityseven/create-rage-fw",
"version": "0.1.0", "version": "0.1.2",
"bin": { "bin": {
"rage-fw": "dist/index.js" "rage-fw": "dist/index.js"
}, },
"main": "dist/index.js", "main": "dist/index.js",
"files": [
"dist/**/*",
"readme.md",
"LICENSE"
],
"scripts": { "scripts": {
"watch": "tsc -w", "watch": "tsc -w",
"build": "tsup", "build": "tsup",
"start": "npx ./dist create" "start": "npx ./dist create",
"install": "pnpm install --ignore-workspace"
}, },
"files": [
"dist/**/*",
"readme.md"
],
"dependencies": { "dependencies": {
"@inquirer/prompts": "^5.0.5", "@inquirer/prompts": "^5.0.5",
"ky": "^1.7.2", "ky": "^1.7.2",
@@ -21,16 +23,20 @@
"yargs": "^17.7.2" "yargs": "^17.7.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.2", "@types/node": "^22.12.0",
"@types/yargs": "^17.0.32", "@types/yargs": "^17.0.32",
"prettier": "^3.3.2", "prettier": "^3.3.2",
"typescript": "^5.4.5" "typescript": "^5.7.3"
}, },
"description": "CLI to scaffold a template project for RageFW", "description": "CLI to scaffold a preview for Rage FW",
"keywords": [], "keywords": ["create-rage-fw","ragefw-cli", "ragefw", "rage-fw", "ragemp", "rage:mp", "rage", "gta5"],
"author": "rilaxik", "author": "Entity Seven Group",
"contributors": [{ "contributors": [
"name": "SashaGoncharov19" {
}], "name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
}
],
"license": "MIT" "license": "MIT"
} }
+488
View File
@@ -0,0 +1,488 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@inquirer/prompts':
specifier: ^5.0.5
version: 5.5.0
chalk:
specifier: 4.1.2
version: 4.1.2
ky:
specifier: ^1.7.2
version: 1.7.4
yargs:
specifier: ^17.7.2
version: 17.7.2
devDependencies:
'@types/node':
specifier: ^22.12.0
version: 22.12.0
'@types/yargs':
specifier: ^17.0.32
version: 17.0.33
prettier:
specifier: ^3.3.2
version: 3.4.2
typescript:
specifier: ^5.7.3
version: 5.7.3
packages:
'@inquirer/[email protected]':
resolution: {integrity: sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==}
engines: {node: '>=18'}
'@inquirer/[email protected]':
resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==}
engines: {node: '>=18'}
'@types/[email protected]':
resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==}
'@types/[email protected]':
resolution: {integrity: sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==}
'@types/[email protected]':
resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==}
'@types/[email protected]':
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
'@types/[email protected]':
resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==}
[email protected]:
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
[email protected]:
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
engines: {node: '>= 12'}
[email protected]:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
[email protected]:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
[email protected]:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
[email protected]:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
[email protected]:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
[email protected]:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
[email protected]:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
[email protected]:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
[email protected]:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-zYEr/gh7uLW2l4su11bmQ2M9xLgQLjyvx58UyNM/6nuqyWFHPX5ktMjvpev3F8QWdjSsHUpnWew4PBCswBNuMQ==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
[email protected]:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'}
[email protected]:
resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==}
engines: {node: '>=14'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
[email protected]:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
[email protected]:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
[email protected]:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
[email protected]:
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
engines: {node: '>=14.17'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
[email protected]:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
[email protected]:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
[email protected]:
resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==}
engines: {node: '>=18'}
snapshots:
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/figures': 1.0.9
'@inquirer/type': 1.5.5
ansi-escapes: 4.3.2
yoctocolors-cjs: 2.1.2
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/type': 1.5.5
'@inquirer/[email protected]':
dependencies:
'@inquirer/figures': 1.0.9
'@inquirer/type': 2.0.0
'@types/mute-stream': 0.0.4
'@types/node': 22.12.0
'@types/wrap-ansi': 3.0.0
ansi-escapes: 4.3.2
cli-width: 4.1.0
mute-stream: 1.0.0
signal-exit: 4.1.0
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.2
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/type': 1.5.5
external-editor: 3.1.0
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/type': 1.5.5
yoctocolors-cjs: 2.1.2
'@inquirer/[email protected]': {}
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/type': 1.5.5
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/type': 1.5.5
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/type': 1.5.5
ansi-escapes: 4.3.2
'@inquirer/[email protected]':
dependencies:
'@inquirer/checkbox': 2.5.0
'@inquirer/confirm': 3.2.0
'@inquirer/editor': 2.2.0
'@inquirer/expand': 2.3.0
'@inquirer/input': 2.3.0
'@inquirer/number': 1.1.0
'@inquirer/password': 2.2.0
'@inquirer/rawlist': 2.3.0
'@inquirer/search': 1.1.0
'@inquirer/select': 2.5.0
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/type': 1.5.5
yoctocolors-cjs: 2.1.2
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/figures': 1.0.9
'@inquirer/type': 1.5.5
yoctocolors-cjs: 2.1.2
'@inquirer/[email protected]':
dependencies:
'@inquirer/core': 9.2.1
'@inquirer/figures': 1.0.9
'@inquirer/type': 1.5.5
ansi-escapes: 4.3.2
yoctocolors-cjs: 2.1.2
'@inquirer/[email protected]':
dependencies:
mute-stream: 1.0.0
'@inquirer/[email protected]':
dependencies:
mute-stream: 1.0.0
'@types/[email protected]':
dependencies:
'@types/node': 22.12.0
'@types/[email protected]':
dependencies:
undici-types: 6.20.0
'@types/[email protected]': {}
'@types/[email protected]': {}
'@types/[email protected]':
dependencies:
'@types/yargs-parser': 21.0.3
[email protected]:
dependencies:
type-fest: 0.21.3
[email protected]: {}
[email protected]:
dependencies:
color-convert: 2.0.1
[email protected]:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
[email protected]:
dependencies:
color-name: 1.1.4
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
chardet: 0.7.0
iconv-lite: 0.4.24
tmp: 0.0.33
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
safer-buffer: 2.1.2
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
[email protected]:
dependencies:
ansi-regex: 5.0.1
[email protected]:
dependencies:
has-flag: 4.0.0
[email protected]:
dependencies:
os-tmpdir: 1.0.2
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
[email protected]:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
[email protected]: {}
[email protected]: {}
[email protected]:
dependencies:
cliui: 8.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
[email protected]: {}
+26 -14
View File
@@ -1,29 +1,41 @@
# 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/) and [bun](https://bun.sh/)
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 @entityseven/rage-fw@latest``
``pnpm create rage-fw@latest`` ``bun create @entityseven/rage-fw@latest``
## TL;DR # TL;DR
- ``Initialize new project`` - create new template project - ``Initialize new project`` - create new template project
- ``Test our RPC`` - scaffold an example for ``@entityseven/rage-fw-rpc``
- ``Install RAGE:MP updater`` - download and update RAGE:MP server files - ``Install RAGE:MP updater`` - download and update RAGE:MP server files
## Options # Options
For now, you will see a few available options. They are described in detail below For now, you will see a few available options. They are described in detail below
- ``Initialize new project`` - ``Initialize new project``
- ``Test our RPC``
- ``Install RAGE:MP updater`` - ``Install RAGE:MP updater``
### Initialize new project ## Initialize new project
Using this options will forward you to common project-creation menu Using this option 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** - ``Enter project name``
This option will specify a name for your project which is used as a folder name too. Defaults to **rage-fw-example**
- ``Select frontend`` - ``Select front-end``
Use selector menu to choose which front-end framework you want to use. We will do our best to expand this menu with various solutions
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. ## Test our RPC
Defaults to **React + TypeScript (Vite)** Using this option will forward you to common project creation menu
### Install Rage:MP updater - ``Enter project name``
This option will simplify installation process of Rage:MP server files required to start your server This option will specify a name for your project which is used as a folder name too. Defaults to **rage-fw-rpc-example**
- ``Select front-end``
Use selector menu to choose which front-end framework you want to use. We will do our best to expand this menu with various solutions
## Install Rage:MP updater
This option will simplify installation and update process of Rage:MP server files required to start your server
# Contribution
If you wish to help us in expanding examples selection with different framework you are very welcome to open PRs and Issues
+23 -15
View File
@@ -3,9 +3,22 @@ import { input, select } from '@inquirer/prompts'
import path from 'node:path' import path from 'node:path'
import { cloneBranch } from '../utils/cloner' import { cloneBranch } from '../utils/cloner'
const choices = {
'react-18': {
name: 'React 18',
value: 'react-18',
description: 'React 18 + TypeScript (Vite) as a front-end',
},
'svelte-5': {
name: 'Svelte 5',
value: 'svelte-5',
description: 'Svelte 5 + TypeScript (Vite) as a front-end',
},
} as const
export async function initProject() { export async function initProject() {
let folder let folder: string | undefined
let framework let framework: keyof typeof choices | undefined
if (!folder) { if (!folder) {
folder = await input({ folder = await input({
@@ -18,27 +31,21 @@ export async function initProject() {
if (!framework) { if (!framework) {
framework = await select({ framework = await select({
message: c.gray('Select frontend:'), message: c.gray('Select front-end:'),
default: 'react-18', default: 'react-18',
loop: true, loop: true,
choices: [ choices: Object.values(choices),
{
name: 'React + TypeScript (Vite)',
value: 'react-18',
description: 'React + TypeScript (Vite) as a frontend',
},
],
}) })
} else { } else {
console.log(c.gray('Frontend:'), framework) console.log(c.gray('Front-end:'), choices[framework].name)
} }
console.log( console.log(
c.gray('\nScaffolding template project into'), c.gray('\nScaffolding template project into'),
folder, folder,
c.gray('with'), c.gray('with'),
framework, choices[framework].name,
c.gray('as a frontend..'), c.gray('as a front-end..'),
) )
cloneBranch( cloneBranch(
@@ -50,11 +57,12 @@ export async function initProject() {
console.log(c.gray('Scaffolded project into'), folder) console.log(c.gray('Scaffolded project into'), folder)
console.log( console.log(
c.gray( c.gray(
`Project was created ar dir: ${path.join(process.cwd(), folder)}`, `Project was created in: ${path.join(process.cwd(), folder)}`,
), ),
) )
}) })
.catch(e => { .catch(e => {
console.log(c.red('Error occured: \n', e)) console.log(c.red('Error occurred: \n', e))
console.log(c.red('Please open an issue if you see this'))
}) })
} }
+11 -10
View File
@@ -5,20 +5,20 @@ import { cloneBranch } from '../utils/cloner'
const choices = { const choices = {
'rpc-react-18': { 'rpc-react-18': {
name: 'Vite + React 18 + TypeScript', name: 'React 18',
value: 'rpc-react-18', value: 'rpc-react-18',
description: 'Vite + React 18 + TypeScript as a frontend', description: 'Vite + React 18 + TypeScript as a front-end',
}, },
'rpc-svelte-5': { 'rpc-svelte-5': {
name: 'Vite + Svelte 5 + TypeScript', name: 'Svelte 5',
value: 'rpc-svelte-5', value: 'rpc-svelte-5',
description: 'Vite + Svelte 5 + TypeScript as a frontend', description: 'Vite + Svelte 5 + TypeScript as a front-end',
}, },
} as const } as const
export async function testRpc() { export async function testRpc() {
let folder let folder: string | undefined
let framework let framework: keyof typeof choices | undefined
if (!folder) { if (!folder) {
folder = await input({ folder = await input({
@@ -31,13 +31,13 @@ export async function testRpc() {
if (!framework) { if (!framework) {
framework = await select({ framework = await select({
message: c.gray('Select frontend:'), message: c.gray('Select front-end:'),
default: 'rpc-react-18', default: 'rpc-react-18',
loop: true, loop: true,
choices: Object.values(choices), choices: Object.values(choices),
}) })
} else { } else {
console.log(c.gray('Frontend:'), framework) console.log(c.gray('Front-end:'), choices[framework].name)
} }
console.log( console.log(
@@ -45,7 +45,7 @@ export async function testRpc() {
folder, folder,
c.gray('with'), c.gray('with'),
choices[framework].name, choices[framework].name,
c.gray('as a frontend..'), c.gray('as a frontend'),
) )
cloneBranch( cloneBranch(
@@ -62,6 +62,7 @@ export async function testRpc() {
) )
}) })
.catch(e => { .catch(e => {
console.log(c.red('Error occured: \n', e)) console.log(c.red('Error occurred: \n', e))
console.log(c.red('Please open an issue if you see this'))
}) })
} }
+14 -4
View File
@@ -12,10 +12,19 @@ enum Actions {
UPDATER = 'UPDATER', UPDATER = 'UPDATER',
} }
process.on('exit', () => {
console.log(c.blueBright('\n\nRage FW CLI | Exiting..'))
process.exit(0)
})
process.on('SIGINT', () => {
console.log(c.blueBright('\n\nRage FW CLI | Exiting..'))
process.exit(0)
})
;(async () => { ;(async () => {
await checkForUpdates() await checkForUpdates()
console.log(c.blueBright('Rage FW CLI | Powered by Entity Seven Group <3')) console.log(c.blueBright('Rage FW CLI | Powered by Entity Seven Group <3'))
const action = await select({ const action = await select({
message: c.gray('Select action:'), message: c.gray('Select action:'),
@@ -26,16 +35,16 @@ enum Actions {
description: 'Initialize a new project and start developing', description: 'Initialize a new project and start developing',
}, },
{ {
name: 'Test our RPC', name: 'Test our Rpc',
value: Actions.TEST_RPC, value: Actions.TEST_RPC,
description: description:
'Initialize a new skeleton project with our RPC set up', 'Initialize a new skeleton project with our Rpc all set',
}, },
{ {
name: 'Install RAGE:MP updater', name: 'Install RAGE:MP updater',
value: Actions.UPDATER, value: Actions.UPDATER,
description: description:
'Use our tool to download or update RAGE:MP server files in two clicks', 'Use our tool to download or update RAGE:MP server files in just two clicks',
}, },
], ],
loop: true, loop: true,
@@ -53,5 +62,6 @@ enum Actions {
break break
default: default:
console.log(c.red('Something went wrong..')) console.log(c.red('Something went wrong..'))
console.log(c.red('Please open an issue if you see this'))
} }
})() })()
+2 -2
View File
@@ -1,9 +1,9 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es6", "target": "esnext",
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"lib": ["DOM", "ES6"], "lib": ["DOM", "esnext"],
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"sourceMap": true, "sourceMap": true,
+15
View File
@@ -0,0 +1,15 @@
Custom Attribution-NoDerives Software License
Copyright (c) 2024 Entity Seven Group
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.
+59 -26
View File
@@ -1,30 +1,63 @@
{ {
"name": "@entityseven/rage-fw-client", "private": false,
"version": "0.1.0", "name": "@entityseven/rage-fw-client",
"version": "0.2.0",
"files": [
"dist/**/*",
"readme.md",
"LICENSE",
"package.json"
],
"main": "src/index.ts",
"exports": {
".": "./src/index.ts"
},
"publishConfig": {
"access": "public",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/src/index.d.ts", "types": "dist/index.d.ts",
"files": [ "exports": {
"dist/**/*", ".": "./dist/index.js"
"readme.md" }
], },
"scripts": { "scripts": {
"build": "tsup" "build": "tsup"
},
"dependencies": {
"@entityseven/rage-fw-rpc": "workspace:*",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/node": "22.12.0"
},
"peerDependencies": {
"@entityseven/rage-fw-shared-types": "workspace:*",
"@ragempcommunity/types-client": "^2.1.8",
"typescript": "^5.7.3"
},
"description": "Package used on a client-side of your Rage:MP Server",
"keywords": [
"rage-fw-client",
"ragefw",
"rage-fw",
"ragemp",
"rage:mp",
"rage",
"gta5"
],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
}, },
"dependencies": { {
"rage-fw-rpc": "workspace:^" "name": "Oleksandr Honcharov",
}, "email": "[email protected]",
"peerDependencies": { "url": "https://github.com/SashaGoncharov19/"
"@ragempcommunity/types-client": "^2.1.8", }
"rage-fw-shared-types": "workspace:^" ],
}, "license": "Custom-Attribution-NoDerivs",
"description": "RageFW Client side", "gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
"keywords": [],
"author": "SashaGoncharov19",
"contributors": [{
"name": "rilaxik",
"email": "[email protected]",
"url": "https://github.com/rilaxik"
}],
"license": "MIT",
"gitHead": "053e4fd12aa120d53e11e0d2009c0df78c1a2ad0"
} }
+1 -1
View File
@@ -1,2 +1,2 @@
# RageFW Client # RageFW Client
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs) [Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki)
+73 -24
View File
@@ -1,33 +1,82 @@
import { Rpc } from 'rage-fw-rpc' import { rpc } from './rpc'
import { FW_Middleware } from './middleware'
import type * as T from '../types'
import type { /** Client-side interactions */
RageFW_ClientArgs, export class FW_Client {
RageFW_ClientCallback, /**
RageFW_ClientEvent, * Registers a client event with an associated callback
} from '../types' *
* @param eventName - The name of the event to register
* @param callback - The callback function to be executed when the event is triggered
* @param [options] - Optional settings for callback execution
* @param [options.middlewares] - Middleware functions to be checked before the callback executes
* @returns {FW_Client} The current client instance, enabling method chaining
*
* @example
* // Registering an event
* fw.event.register("playerDeath", (player, reason, killer) => {
* fw.system.log.info(player, reason, killer)
* })
*
* @example
* // Registering an event with middlewares
* fw.event.register("playerDeath", (player, reason, killer) => {
* fw.system.log.info(player, reason, killer)
* }, {
* middlewares: [ignoreSuicide] // <- your middlewares here
* })
*
* // or
*
* fw.event.register("playerDeath", (player, reason, killer) => {
* fw.system.log.info(player, reason, killer)
* }, {
* middlewares: {
* executables: [ignoreSuicide], // <- your middlewares here
* onError: (msg) => fw.system.log.info(`${player.socialClub} has commited suicide`)
* }
* })
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public register<EventName extends T.FW_ClientEvent>(
eventName: EventName,
callback: T.FW_ClientCallback<EventName>,
options?: {
middlewares?: T.FW_MiddlewareOptions<EventName>
},
): FW_Client {
rpc.register<
Parameters<typeof callback>,
ReturnType<typeof callback> | Promise<unknown>,
EventName
>(eventName, async (...data) => {
if (!options?.middlewares) return await callback(...data)
export class Client { await FW_Middleware.process(options.middlewares, callback, data)
private _rpc: Rpc = new Rpc() })
get rpc(): Rpc { return this
return this._rpc
} }
public register<EventName extends RageFW_ClientEvent>( /**
* Unregisters a client event, removing the associated callback
*
* @param eventName - The name of the event to unregister
* @returns {Client} The current client instance, enabling method chaining
*
* @example
* // Unregistering an event
* fw.event.unregister("playerDeath")
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public unregister<EventName extends T.FW_ClientEvent>(
eventName: EventName, eventName: EventName,
callback: RageFW_ClientCallback<EventName>, ): FW_Client {
): void { rpc.unregister<EventName>(eventName)
this._rpc.register(
eventName,
async (data: RageFW_ClientArgs<EventName>) => {
return await callback(data)
},
)
}
public unregister<EventName extends RageFW_ClientEvent>( return this
eventName: EventName,
): void {
this._rpc.unregister(eventName)
} }
} }
+3 -1
View File
@@ -1,3 +1,5 @@
export * from './client' export * from './client'
export * from './player'
export * from './logger' export * from './logger'
export * from './middleware'
export * from './player'
export * from './rpc'
+35 -14
View File
@@ -1,19 +1,40 @@
export class Logger { /**
public error(...message: unknown[]) { * Used to log to a client in-game console
mp.console.logError( */
`[${new Date().toLocaleTimeString()}] [ERROR] ${message.join(' ')}`, export class FW_Logger {
) /**
} * Informational logs. Colored in white
*
public warn(...message: unknown[]) { * @example
mp.console.logWarning( * fw.system.log.info('some information to be logged')
`[${new Date().toLocaleTimeString()}] [WARN] ${message.join(' ')}`, */
)
}
public info(...message: unknown[]) { public info(...message: unknown[]) {
mp.console.logInfo( mp.console.logInfo(
`[${new Date().toLocaleTimeString()}] [INFO] ${message.join(' ')}`, `[${new Date().toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric', second: 'numeric', fractionalSecondDigits: 2 })}] [INFO] ${message.join(' ')}`,
)
}
/**
* Warning logs. Colored in yellow
*
* @example
* fw.system.log.warn('warning message')
*/
public warn(...message: unknown[]) {
mp.console.logWarning(
`[${new Date().toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric', second: 'numeric', fractionalSecondDigits: 2 })}] [WARN] ${message.join(' ')}`,
)
}
/**
* Error logs. Colored in red
*
* @example
* fw.system.log.info('some error information')
*/
public error(...message: unknown[]) {
mp.console.logError(
`[${new Date().toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric', second: 'numeric', fractionalSecondDigits: 2 })}] [ERROR] ${message.join(' ')}`,
) )
} }
} }
+54
View File
@@ -0,0 +1,54 @@
import type * as T from '../types'
export class FW_Middleware {
constructor() {}
private static async execute<EventName extends T.FW_ClientEvent>(
middlewares: T.FW_MiddlewareFunction<EventName>[],
args: T.FW_ClientArgs<EventName>,
): Promise<T.FW_MiddlewareResponseInternal> {
for (let i = 0; i < middlewares.length; i++) {
const result = await middlewares[i](...args)
if (typeof result === 'boolean' && !result)
return { success: result, id: i }
if (typeof result !== 'boolean' && !result.success)
return { ...result, id: i }
}
return {
success: true,
}
}
public static async process<EventName extends T.FW_ClientEvent>(
middlewareOptions: T.FW_MiddlewareOptions<EventName>,
callback: T.FW_ClientCallback<EventName>,
args: T.FW_ClientArgs<EventName>,
) {
if (Array.isArray(middlewareOptions)) {
const middlewaresResponse = await FW_Middleware.execute(
middlewareOptions,
args,
)
if (middlewaresResponse.success) return await callback(...args)
} else {
const middlewaresResponse = await FW_Middleware.execute(
middlewareOptions.executables,
args,
)
if (middlewaresResponse.success) {
return await callback(...args)
} else {
middlewareOptions.onError(
middlewaresResponse.message ??
'Middleware with id ' +
middlewaresResponse.id +
' failed',
)
}
}
}
}
+188 -40
View File
@@ -1,53 +1,201 @@
import { Rpc } from 'rage-fw-rpc' import { rpc } from './rpc'
import type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types' import type * as T from '../types'
import { /** Handles event manipulations that require player to be present in context */
_CefEventHasArgs, export class FW_Player {
_ClientEventHasArgs, private _browser: BrowserMp | undefined = undefined
_ServerEventHasArgs,
RageFW_CefArgs,
RageFW_CefEvent,
RageFW_CefReturn,
RageFW_ClientArgs,
RageFW_ClientReturn,
RageFW_ClientServerEvent,
RageFW_ClientServerArgs,
RageFW_ClientServerReturn,
} from '../types'
export class Player { /**
private _rpc: Rpc = new Rpc() * Setter. Also shares browser with ``rage-fw-rpc``
public browser: BrowserMp | undefined */
set browser(browser: BrowserMp) {
get rpc(): Rpc { this._browser = browser
return this._rpc rpc.browser = browser
}
public trigger<EventName extends keyof RageFW_ICustomClientEvent>(
eventName: EventName,
...args: _ClientEventHasArgs<EventName> extends true
? [RageFW_ClientArgs<EventName>]
: []
): Promise<RageFW_ClientReturn<EventName>> {
return this._rpc.call(eventName, args)
} }
public triggerServer<EventName extends RageFW_ClientServerEvent>( /**
* Triggers a client event from the client with arguments from 'rage-fw-shared-types'
*
* Formerly known as ``call`` or ``emit``
*
* @param eventName - The name of the client event to trigger
* @param [args] - Arguments for the client event, if present
* @returns {void}
*
* @example
* // Triggering a client event without arguments
* fw.player.trigger("clientEventName")
*
* @example
* // Triggering a client event with arguments
* fw.player.trigger("clientEventName", ["message to me"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public trigger<EventName extends keyof T.FW_ICustomClientEvent>(
eventName: EventName, eventName: EventName,
...args: _ServerEventHasArgs<EventName> extends true ...args: T._ClientEventHasArgs<EventName> extends true
? [RageFW_ClientServerArgs<EventName>] ? [T.FW_ClientArgs<EventName>]
: [] : []
): Promise<RageFW_ClientServerReturn<EventName>> { ): void {
return this._rpc.callServer(eventName, args) rpc.call<typeof args, EventName>(eventName, args)
} }
public triggerBrowser<EventName extends RageFW_CefEvent>( /**
* Triggers an asynchronous client event from the client with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callAsync`` or ``emitAsync``
*
* @param eventName - The name of the client event to trigger
* @param [args] - Arguments for the client event, if present
* @returns {Promise} resolving to the client's response for the event
*
* @example
* // Triggering a client event without arguments
* fw.player.trigger("clientEventName")
*
* @example
* // Triggering a client event with arguments
* fw.player.trigger("clientEventName", ["message to me"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerAsync<EventName extends keyof T.FW_ICustomClientEvent>(
eventName: EventName, eventName: EventName,
...args: _CefEventHasArgs<EventName> extends true ...args: T._ClientEventHasArgs<EventName> extends true
? [RageFW_CefArgs<EventName>] ? [T.FW_ClientArgs<EventName>]
: [] : []
): Promise<RageFW_CefReturn<EventName>> { ): Promise<T.FW_ClientReturn<EventName>> {
if (!this.browser) return await rpc.callAsync<
typeof args,
EventName,
T.FW_ClientReturn<EventName>
>(eventName, args)
}
/**
* Triggers a server event from the client with arguments from 'rage-fw-shared-types'
*
* Formerly known as ``callServer`` or ``emitServer``
*
* @param eventName - The name of the server event to trigger
* @param [args] - Arguments for the server event, if present
* @returns {void}
*
* @example
* // Triggering a server event without arguments
* fw.player.triggerServer("serverEventName")
*
* @example
* // Triggering a server event with arguments
* fw.player.triggerServer("serverEventName", ["message to server"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public triggerServer<EventName extends T.FW_ServerEvent>(
eventName: EventName,
...args: T._ServerEventHasArgs<EventName> extends true
? [T.FW_ServerArgs<EventName>]
: []
): void {
rpc.callServer<typeof args, EventName>(eventName, args)
}
/**
* Triggers an asynchronous server event from the client with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callServerAsync`` or ``emitServerAsync``
*
* @param eventName - The name of the server event to trigger
* @param [args] - Arguments for the server event, if present
* @returns {Promise} resolving to the server's response for the event
*
* @example
* // Triggering a server event without arguments
* fw.player.triggerServer("serverEventName")
*
* @example
* // Triggering a server event with arguments
* fw.player.triggerServer("serverEventName", ["message to server"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerServerAsync<EventName extends T.FW_ServerEvent>(
eventName: EventName,
...args: T._ServerEventHasArgs<EventName> extends true
? [T.FW_ServerArgs<EventName>]
: []
): Promise<T.FW_ClientServerReturn<EventName>> {
return await rpc.callServerAsync<
typeof args,
EventName,
T.FW_ClientServerReturn<EventName>
>(eventName, args)
}
/**
* Triggers a browser event from the client with arguments from 'rage-fw-shared-types'
*
* Formerly known as ``callBrowser`` or ``emitBrowser``
*
* @param eventName - The name of the browser event to trigger
* @param [args] - Arguments for the browser event, if present
* @returns {void}
*
* @example
* // Triggering a browser event without arguments
* fw.player.triggerBrowser("browserEventName")
*
* @example
* // Triggering a browser event with arguments
* fw.player.triggerBrowser("browserEventName", ["message to browser"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public triggerBrowser<EventName extends T.FW_BrowserEvent>(
eventName: EventName,
...args: T._BrowserEventHasArgs<EventName> extends true
? [T.FW_BrowserArgs<EventName>]
: []
): void {
if (!this._browser)
throw new Error('You need to initialize browser first') throw new Error('You need to initialize browser first')
return this._rpc.callBrowser(this.browser, eventName, args)
rpc.callBrowser<typeof args, EventName>(eventName, args)
}
/**
* Triggers an asynchronous browser event from the client with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callBrowserAsync`` or ``emitBrowserAsync``
*
* @param eventName - The name of the browser event to trigger
* @param [args] - Arguments for the browser event, if present
* @returns {Promise} resolving to the browser's response for the event
*
* @example
* // Triggering a browser event without arguments
* fw.player.triggerBrowser("browserEventName")
*
* @example
* // Triggering a browser event with arguments
* fw.player.triggerBrowser("browserEventName", ["message to browser"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerBrowserAsync<EventName extends T.FW_BrowserEvent>(
eventName: EventName,
...args: T._BrowserEventHasArgs<EventName> extends true
? [T.FW_BrowserArgs<EventName>]
: []
): Promise<T.FW_BrowserReturn<EventName>> {
if (!this._browser)
throw new Error('You need to initialize browser first')
return await rpc.callBrowserAsync<
typeof args,
EventName,
T.FW_BrowserReturn<EventName>
>(eventName, args)
} }
} }
+5
View File
@@ -0,0 +1,5 @@
import { Rpc } from '@entityseven/rage-fw-rpc'
export const rpc = new Rpc({
debugLogs: false,
})
+17 -4
View File
@@ -1,9 +1,22 @@
import { Client, Logger, Player } from './core' import { FW_Client, FW_Logger, FW_Player, rpc } from './core'
export type { FW_MiddlewareFunction } from './types'
/**
* Package used on a client-side of your Rage:MP Server
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
export const fw = { export const fw = {
event: new Client(), /** Client-side interactions */
player: new Player(), event: new FW_Client(),
/** Handles event manipulations that require player to be present in context */
player: new FW_Player(),
/** Handles functions used to interact with the client environment */
system: { system: {
log: new Logger(), /** Used to log in a client in-game console */
log: new FW_Logger(),
}, },
/** ``rage-fw-rpc`` instance used under the hood. It is highly recommended to use this one if you need it instead of creating a new instance */
rpc,
} }
+21 -12
View File
@@ -1,20 +1,29 @@
/// <reference types="@ragempcommunity/types-client" /> /// <reference types="@ragempcommunity/types-client" />
import type { RageFW_ICustomCefEvent } from 'rage-fw-shared-types' import type { FW_ICustomBrowserEvent } from '@entityseven/rage-fw-shared-types'
export type RageFW_CefEvent = keyof RageFW_ICustomCefEvent /**
* Union of all available browser event names callable from client
* These only include custom events
*/
export type FW_BrowserEvent = keyof FW_ICustomBrowserEvent
export type RageFW_CefArgs<K extends RageFW_CefEvent> = Parameters< /**
RageFW_ICustomCefEvent[K] * Array of arguments for an event, name of which you pass as a generic
* These only include custom events
*/
export type FW_BrowserArgs<K extends FW_BrowserEvent> = Parameters<
FW_ICustomBrowserEvent[K]
> >
export type RageFW_CefReturn<K extends RageFW_CefEvent> = ReturnType< export type FW_BrowserReturn<K extends FW_BrowserEvent> = ReturnType<
RageFW_ICustomCefEvent[K] FW_ICustomBrowserEvent[K]
> >
export type _CefEventHasArgs<EventName extends keyof RageFW_ICustomCefEvent> = export type _BrowserEventHasArgs<
keyof RageFW_ICustomCefEvent extends never EventName extends keyof FW_ICustomBrowserEvent,
? false > = keyof FW_ICustomBrowserEvent extends never
: Parameters<RageFW_ICustomCefEvent[EventName]>[0] extends undefined ? false
? false : Parameters<FW_ICustomBrowserEvent[EventName]>[0] extends undefined
: true ? false
: true
+29 -25
View File
@@ -1,47 +1,51 @@
/// <reference types="@ragempcommunity/types-client" /> /// <reference types="@ragempcommunity/types-client" />
import type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types' import type { FW_ICustomClientEvent } from '@entityseven/rage-fw-shared-types'
export type { FW_ICustomClientEvent } from '@entityseven/rage-fw-shared-types'
/** /**
* Union of all available client event names * Union of all available client event names
* These include custom and system events * These include custom and system events
*/ */
export type RageFW_ClientEvent = export type FW_ClientEvent = keyof FW_ICustomClientEvent | keyof IClientEvents
| keyof RageFW_ICustomClientEvent
| keyof IClientEvents
/** /**
* Array of arguments for an event, name of which you pass as a generic * Array of arguments for an event, name of which you pass as a generic
* These include custom and system events * These include custom and system events
*/ */
export type RageFW_ClientArgs<K extends RageFW_ClientEvent> = export type FW_ClientArgs<K extends FW_ClientEvent> =
K extends keyof RageFW_ICustomClientEvent K extends keyof FW_ICustomClientEvent
? Parameters<RageFW_ICustomClientEvent[K]> ? Parameters<FW_ICustomClientEvent[K]>
: K extends keyof IClientEvents : K extends keyof IClientEvents
? Parameters<IClientEvents[K]> ? Parameters<IClientEvents[K]>
: never : never
/** /**
* Callback (function) for an event, name of which you pass as a generic * Return type for an event, name of which you pass as a generic
* These only include custom events * These include custom and system events
*/ */
export type RageFW_ClientCallback<K extends RageFW_ClientEvent> = ( export type FW_ClientReturn<K extends FW_ClientEvent> =
args: RageFW_ClientArgs<K>, K extends keyof FW_ICustomClientEvent
) => Promise<RageFW_ClientReturn<K>> ? ReturnType<FW_ICustomClientEvent[K]>
: K extends keyof IClientEvents
? ReturnType<IClientEvents[K]>
: void
/** /**
* Return type for an event, name of which you pass as a generic * Callback (function) for an event, name of which you pass as a generic
* These only include custom events * These include custom and system events
*/ */
export type RageFW_ClientReturn<K extends RageFW_ClientEvent> = export type FW_ClientCallback<K extends FW_ClientEvent> = (
K extends keyof RageFW_ICustomClientEvent ...args: FW_ClientArgs<K>
? ReturnType<RageFW_ICustomClientEvent[K]> ) => Promise<FW_ClientReturn<K>>
: void
export type _ClientEventHasArgs< /**
EventName extends keyof RageFW_ICustomClientEvent, *
> = keyof RageFW_ICustomClientEvent extends never */
? false export type _ClientEventHasArgs<EventName extends keyof FW_ICustomClientEvent> =
: Parameters<RageFW_ICustomClientEvent[EventName]>[0] extends undefined keyof FW_ICustomClientEvent extends never
? false ? false
: true : Parameters<FW_ICustomClientEvent[EventName]>[0] extends undefined
? false
: true
+4 -3
View File
@@ -1,3 +1,4 @@
export * from './client' export type * from './browser'
export * from './server' export type * from './client'
export * from './browser' export type * from './middleware'
export type * from './server'
+25
View File
@@ -0,0 +1,25 @@
import type * as T from './client'
export type FW_MiddlewareResponse =
| {
success: boolean
message?: string
}
| boolean
export type FW_MiddlewareResponseInternal = {
success: boolean
message?: string
id?: number
}
export type FW_MiddlewareFunction<EventName extends T.FW_ClientEvent> = (
...args: T.FW_ClientArgs<EventName>
) => Promise<FW_MiddlewareResponse>
export type FW_MiddlewareOptions<EventName extends T.FW_ClientEvent> =
| FW_MiddlewareFunction<EventName>[]
| {
executables: FW_MiddlewareFunction<EventName>[]
onError: (error: string) => unknown
}
+16 -17
View File
@@ -1,38 +1,37 @@
/// <reference types="@ragempcommunity/types-client" /> /// <reference types="@ragempcommunity/types-client" />
import type { import type {
RageFW_ICustomClientEvent, FW_ICustomClientEvent,
RageFW_ICustomServerEvent, FW_ICustomServerEvent,
} from 'rage-fw-shared-types' } from '@entityseven/rage-fw-shared-types'
/** /**
* Union of all available server event names callable from client * Union of all available server event names callable from client
* These only include custom events * These only include custom events
*/ */
export type RageFW_ClientServerEvent = keyof RageFW_ICustomServerEvent export type FW_ServerEvent = keyof FW_ICustomServerEvent
/** /**
* Array of arguments for an event, name of which you pass as a generic * Array of arguments for an event, name of which you pass as a generic
* These only include custom events * These only include custom events
*/ */
export type RageFW_ClientServerArgs<K extends RageFW_ClientServerEvent> = export type FW_ServerArgs<K extends FW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent K extends keyof FW_ICustomServerEvent
? Parameters<RageFW_ICustomServerEvent[K]> ? Parameters<FW_ICustomServerEvent[K]>
: never : never
/** /**
* Return type for an event, name of which you pass as a generic * Return type for an event, name of which you pass as a generic
* These only include custom events * These only include custom events
*/ */
export type RageFW_ClientServerReturn<K extends RageFW_ClientServerEvent> = export type FW_ClientServerReturn<K extends FW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent K extends keyof FW_ICustomServerEvent
? ReturnType<RageFW_ICustomServerEvent[K]> ? ReturnType<FW_ICustomServerEvent[K]>
: never : never
export type _ServerEventHasArgs< export type _ServerEventHasArgs<EventName extends keyof FW_ICustomServerEvent> =
EventName extends keyof RageFW_ICustomServerEvent, keyof FW_ICustomClientEvent extends never
> = keyof RageFW_ICustomClientEvent extends never ? false
? false : Parameters<FW_ICustomServerEvent[EventName]>[0] extends undefined
: Parameters<RageFW_ICustomServerEvent[EventName]>[0] extends undefined ? false
? false : true
: true
+4 -21
View File
@@ -1,25 +1,8 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"display": "Base", "display": "Client",
"exclude": [
"node_modules"
],
"compilerOptions": { "compilerOptions": {
"incremental": false, "types": ["./node_modules/@ragempcommunity/types-client/index.d.ts"]
"composite": false, },
"target": "ES2022", "extends": ["../tsconfig.ragefw.json"]
"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
}
} }
+2 -2
View File
@@ -1,12 +1,12 @@
import { defineConfig } from 'tsup' import { defineConfig } from 'tsup'
export default defineConfig({ export default defineConfig({
entry: ['src/index.ts'], entry: ['src/**/*.ts'],
outDir: './dist', outDir: './dist',
format: ['cjs'], format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true, experimentalDts: true,
splitting: false, splitting: false,
bundle: false,
sourcemap: false, sourcemap: false,
clean: true, clean: true,
}) })
+57
View File
@@ -0,0 +1,57 @@
import globals from 'globals'
import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'
import type { Linter } from 'eslint'
export default [
{
ignores: ['**/.*/*', '**/dist/*', '**/node_modules/*', '*.config.*'],
},
pluginJs.configs.recommended,
...(tseslint.configs.recommended as Linter.Config[]),
{
name: 'global',
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': 'off',
'no-empty': 'off',
},
},
{
name: 'browser/rpc',
files: ['browser/src/*.{ts,js}', 'rpc/src/*.{ts,js}'],
languageOptions: {
globals: { ...globals.browser, ...globals.node, ...globals.es2022 },
},
},
{
name: 'cli',
files: ['cli/src/*.{ts,js}'],
languageOptions: {
globals: { ...globals.node, ...globals.es2024 },
ecmaVersion: 2024,
},
},
{
name: 'client/server/shared',
files: [
'client/**/*.{ts,js}',
'server/**/*.{ts,js}',
'shared/**/*.{ts,js}',
],
languageOptions: {
globals: { ...globals.node, ...globals.es2022 },
},
},
] satisfies Linter.Config[]
-5
View File
@@ -1,5 +0,0 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "0.2.1",
"npmClient": "pnpm"
}
+55 -20
View File
@@ -1,29 +1,64 @@
{ {
"private": true, "workspaces": [
"*",
"!cli"
],
"scripts": { "scripts": {
"publish": "pnpm build && lerna publish --force-publish", "lint": "eslint . --config eslint.config.ts",
"build": "lerna run build", "_____build": "echo build",
"lint": "eslint --c .eslintrc.yaml --ext .ts client/ server/ shared-types/", "build:browser": "cd browser && pnpm build",
"build:client": "cd client && pnpm build",
"rebuild:cef": "cd cef && pnpm build", "build:server": "cd server && pnpm build",
"rebuild:client": "cd client && pnpm build", "build:rpc": "cd rpc && pnpm build",
"rebuild:server": "cd server && pnpm build", "build": "pnpm build:browser && pnpm build:client && pnpm build:server && pnpm build:rpc",
"rebuild": "pnpm rebuild:cef && pnpm rebuild:client && pnpm rebuild:server" "_____deps": "echo must be ran before lerna commands",
"deps:check": "syncpack list-mismatches",
"deps:fix": "syncpack fix-mismatches",
"_____publish": "echo publish",
"publish:check": "pnpm run deps:check && pnpm run lint",
"publish:changeset": "pnpm changeset add",
"publish:version": "pnpm changeset version"
}, },
"dependencies": { "dependencies": {
"@microsoft/api-extractor": "^7.47.0", "@changesets/cli": "^2.27.12",
"@eslint/js": "^9.19.0",
"@microsoft/api-extractor": "^7.49.1",
"@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",
"@typescript-eslint/eslint-plugin": "^7.13.0", "eslint": "^9.19.0",
"@typescript-eslint/parser": "^7.13.0", "globals": "^15.14.0",
"eslint": "^8.56.0", "jiti": "^2.4.2",
"lerna": "^8.1.3", "prettier": "^3.4.2",
"prettier": "^3.3.1", "tsup": "^8.3.6",
"rage-rpc": "^0.4.0", "typescript": "^5.7.3",
"tsup": "^8.1.0", "typescript-eslint": "^8.22.0",
"typescript": "^5.4.5", "winston": "^3.17.0"
"winston": "^3.13.0"
}, },
"type": "module" "engines": {
"node": ">=14",
"pnpm": ">=10"
},
"pnpm": {
"requiredScripts": []
},
"private": true,
"type": "module",
"license": "SEE INDIVIDUALLY FOR EACH PACKAGE",
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
},
{
"name": "Oleksandr Honcharov",
"email": "[email protected]",
"url": "https://github.com/SashaGoncharov19/"
}
],
"devDependencies": {
"syncpack": "^13.0.0"
}
} }
+3294 -9285
View File
File diff suppressed because it is too large Load Diff
+2 -6
View File
@@ -1,7 +1,3 @@
packages: packages:
- "server" - "*"
- "client" - "!cli"
- "cef"
- "rpc"
- "cli"
- "shared-types"
+1 -1
View File
@@ -13,7 +13,7 @@ You can find out more about our CLI [here](https://git.entityseven.com/entitysev
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: 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`` ``pnpm create @entityseven/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 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
+1 -18
View File
@@ -1,25 +1,8 @@
export {} export {}
interface Mp { declare var global: Record<string, (...args: any[]) => unknown>
trigger(event: string, data?: any): void
events: {
add(event: string, data: any): void
call(event: string, data: any): void
callRemote(event: string, data: any): void
remove(event: string): void
}
joaat?: unknown
game: {
joaat?: unknown
}
console: {
logInfo: (...args: unknown[]) => void
}
}
declare global { declare global {
const mp: Mp
const global: Record<string, (...args: any[]) => unknown>
interface Window { interface Window {
[p: string]: any [p: string]: any
} }
+50 -38
View File
@@ -1,41 +1,53 @@
{ {
"name": "rage-fw-rpc", "private": false,
"description": "Rage FW RPC", "name": "@entityseven/rage-fw-rpc",
"version": "0.2.1", "version": "0.2.5",
"scripts": { "main": "src/index.ts",
"build": "tsup", "exports": {
"start": "npx ./dist create" ".": "./src/index.ts"
}, },
"files": [ "publishConfig": {
"dist/**/*" "access": "public",
],
"devDependencies": {
"@microsoft/api-extractor": "^7.47.9",
"prettier": "^3.3.2",
"tsup": "^8.3.0"
},
"peerDependencies": {
"typescript": "^5"
},
"keywords": [
"ragemp",
"rage",
"rpc",
"rage-rpc",
"ragerpc"
],
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/src/index.d.ts", "types": "dist/index.d.ts",
"author": { "exports": {
"name": "rilaxik", ".": "./dist/index.js"
"email": "[email protected]", }
"url": "https://github.com/rilaxik" },
}, "scripts": {
"contributors": [ "build": "tsup",
{ "start": "npx ./dist create"
"name": "SashaGoncharov19", },
"url": "https://github.com/SashaGoncharov19" "files": [
} "dist/**/*",
], "readme.md",
"license": "MIT" "LICENSE"
],
"devDependencies": {
"@types/node": "22.12.0"
},
"peerDependencies": {
"typescript": "^5.7.3"
},
"description": "Rage-FW RPC (Remote procedure caller) for Rage:MP",
"keywords": [
"ragefw",
"rage-fw",
"ragemp",
"rage",
"rpc",
"rage-rpc",
"ragerpc",
"gta5"
],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
}
],
"license": "MIT",
"gitHead": "04eb7240735c4a0e4855ebabbe8d5b326819fa76"
} }
+12 -7
View File
@@ -3,23 +3,28 @@ is an all-in package with asynchronous RPC implementation for RageMP servers in
# Installation # Installation
``` shell ``` shell
npm i rage-fw-rpc npm i @entityseven/rage-fw-rpc
``` ```
```shell ```shell
pnpm i rage-fw-rpc pnpm i @entityseven/rage-fw-rpc
``` ```
```shell ```shell
yarn add rage-fw-rpc yarn add @entityseven/rage-fw-rpc
``` ```
Import installed package and initialize rpc: Import installed package and initialize rpc:
```ts ```ts
// lib/rpc.js // lib/rpc.js
import { Rpc } from 'rage-fw-rpc' import { Rpc } from 'rage-fw-rpc'
export const rpc = new Rpc(/* options */) export const rpc = new Rpc(/* options */)
``` ```
# Motivation
The idea was to create an extensible package, with various features to simplify the development process and provide as much comfort as possible. It should also be using similar architecture as the framework it was specially built for
Inspired by usage of [rage-rpc](https://github.com/micaww/rage-rpc)
# Features # Features
- Type-safe events via [TS generics](https://www.typescriptlang.org/docs/handbook/2/generics.html), avoiding type wrappers - Type-safe events via [TS generics](https://www.typescriptlang.org/docs/handbook/2/generics.html), avoiding type wrappers
- Built-in logging options for each environment - Built-in logging options for each environment
@@ -28,7 +33,7 @@ Import installed package and initialize rpc:
- Actual human-readable errors - Actual human-readable errors
# Docs # Docs
*Extended version with details coming soon* ## [Extended version available here](https://git.entityseven.com/entityseven/rage-framework/wiki/RPC%400.2.5)
## register ## register
Registers a callback function for a specified event Registers a callback function for a specified event
+3 -1
View File
@@ -1,3 +1,5 @@
/// <reference types="@ragempcommunity/types-cef" />
import { Wrapper } from './wrapper' import { Wrapper } from './wrapper'
import { import {
Environment, Environment,
@@ -24,7 +26,7 @@ export class Browser extends Wrapper {
* NOT INTENDED FOR OUT-OF-CONTEXT USE * NOT INTENDED FOR OUT-OF-CONTEXT USE
*/ */
public _resolveEmitDestination(dataRaw: string) { public _resolveEmitDestination(dataRaw: string) {
let state = Utils.prepareExecution(dataRaw) const state = Utils.prepareExecution(dataRaw)
switch (state.calledTo) { switch (state.calledTo) {
case Environment.BROWSER: case Environment.BROWSER:
+2
View File
@@ -1,3 +1,5 @@
/// <reference types="@ragempcommunity/types-client" />
import { Wrapper } from './wrapper' import { Wrapper } from './wrapper'
import { import {
Environment, Environment,
+440 -99
View File
@@ -77,7 +77,7 @@ class Rpc extends Wrapper {
* *
* @template CallbackArguments - An array of argument types that the callback function accepts * @template CallbackArguments - An array of argument types that the callback function accepts
* @template CallbackReturn - The type of the value returned by the callback function * @template CallbackReturn - The type of the value returned by the callback function
* @template EventName - A string representing the event name or union of names * @template EventName - A string or union representing the event name
* *
* @param {EventName} eventName - The name of the event to register the callback for * @param {EventName} eventName - The name of the event to register the callback for
* @param {(...args: CallbackArguments) => CallbackReturn} cb - The callback function that is called when the event is triggered * @param {(...args: CallbackArguments) => CallbackReturn} cb - The callback function that is called when the event is triggered
@@ -91,7 +91,7 @@ class Rpc extends Wrapper {
*/ */
public register< public register<
CallbackArguments extends unknown[] = unknown[], CallbackArguments extends unknown[] = unknown[],
CallbackReturn extends unknown = unknown, CallbackReturn = unknown,
EventName extends string = string, EventName extends string = string,
>( >(
eventName: EventName, eventName: EventName,
@@ -116,7 +116,7 @@ class Rpc extends Wrapper {
/** /**
* Unregisters callback function for a specified event * Unregisters callback function for a specified event
* *
* @template EventName - A string representing the event name or union of names * @template EventName - A string or union representing the event name
* *
* @param {EventName} eventName - The name of the event to register the callback for * @param {EventName} eventName - The name of the event to register the callback for
* *
@@ -133,88 +133,69 @@ class Rpc extends Wrapper {
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
delete this.state_[eventName] delete this.state_[eventName]
mp.events.remove(eventName)
} }
/** /**
* Calls a client-side event from server or browser * Calls a client-side event from browser or server. Use 'call' to call client from client
* *
* @template Arguments - An array of argument types to be passed to the client event * @template Arguments - An array of argument types to be passed to the client event
* @template EventName - A string representing the client event name or union of names * @template EventName - A string or union representing the client event name
* @template Return - The type of the value returned by the client event
* *
* @param {EventName} eventName - The name of the client event to be called * @param {EventName} eventName - The name of the client event to be called
* @param {Arguments} [args] - Optional arguments to pass to the client event * @param {Arguments} [args] - Optional arguments to pass to the client event
* @returns {Promise<Return>} A promise resolving to the return value of the client event * @returns {void}
* *
* @example * @example
* // Calls an event on client without specifying a player * // Calls an event on a client without specifying a player
* callClient<[], string, object>('onDataRequest').then(response => { * callClient<[], string, object>('onDataRequest')
* console.log(`Received: ${response}`) // ^ object
* })
*/ */
public async callClient< public callClient<
Arguments extends unknown[] = unknown[], Arguments extends unknown[] = unknown[],
EventName extends string = string, EventName extends string = string,
Return extends unknown = unknown, >(eventName: EventName, args?: Arguments): void
>(eventName: EventName, args?: Arguments): Promise<Return>
/** /**
* Calls a client-side event from server or browser * Calls a client-side event from server or server. Use 'call' to call client from client
* *
* @template Arguments - An array of argument types to be passed to the client event * @template Arguments - An array of argument types to be passed to the client event
* @template EventName - A string representing the client event name or union of names * @template EventName - A string or union representing the client event name
* @template Return - The type of the value returned by the client event
* *
* @param {PlayerMp} player - The player for whom the client event is called * @param {PlayerMp} player - The player for whom the client event is called
* @param {EventName} eventName - The name of the client event to be called * @param {EventName} eventName - The name of the client event to be called
* @param {Arguments} [args] - Optional arguments to pass to the client event * @param {Arguments} [args] - Optional arguments to pass to the client event
* @returns {Promise<Return>} A promise resolving to the return value of the client event * @returns {void} A promise resolving to the return value of the client event
* *
* @example * @example
* // Calls an event on client for a specific player * // Calls an event on a client for a specific player
* callClient<[string, number], string, boolean>(player, 'onPlayerAction', ['jump', 2]).then(result => { * callClient<[string, number], string, boolean>(player, 'onPlayerAction', ['jump', 2])
* console.log(`Action success: ${result}`) // ^ boolean
* })
*/ */
public async callClient< public callClient<
Arguments extends unknown[] = unknown[], Arguments extends unknown[] = unknown[],
EventName extends string = string, EventName extends string = string,
Return extends unknown = unknown, >(player: PlayerMp, eventName: EventName, args?: Arguments): void
>(player: PlayerMp, eventName: EventName, args?: Arguments): Promise<Return> public callClient(
public async callClient(
playerOrEventName: PlayerMp | string, playerOrEventName: PlayerMp | string,
eventNameOrArgs?: string | unknown[], eventNameOrArgs?: string | unknown[],
args?: unknown[], args?: unknown[],
) { ) {
_is1StParamPlayer(playerOrEventName) if (_is1StParamPlayer(playerOrEventName)) {
? this.log( this.log(
'callClient', 'callClient',
eventNameOrArgs as string, eventNameOrArgs as string,
playerOrEventName, playerOrEventName,
eventNameOrArgs, eventNameOrArgs,
args, args,
) )
: this.log( } else {
'callClient', this.log('callClient', playerOrEventName as string, eventNameOrArgs)
playerOrEventName as string, }
eventNameOrArgs,
)
if (this.forceBrowserDevMode_) return if (this.forceBrowserDevMode_) return
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
function _is1StParamPlayer(x: unknown): x is PlayerMp {
return typeof x === 'object'
}
function _is2NdParamEventName(x: unknown): x is string {
return typeof x === 'string'
}
if (this.environment_ === Environment.CLIENT) { if (this.environment_ === Environment.CLIENT) {
// client // client
return await this.call( this.call(playerOrEventName as string, args as unknown[])
playerOrEventName as string,
args as unknown[],
)
} }
// server // server
@@ -236,7 +217,6 @@ class Rpc extends Wrapper {
const dataRaw = Utils.prepareTransfer(state) const dataRaw = Utils.prepareTransfer(state)
playerOrEventName.call(Events.LOCAL_EVENT_LISTENER, [dataRaw]) playerOrEventName.call(Events.LOCAL_EVENT_LISTENER, [dataRaw])
return (await this.responseHandler(state.uuid)).data
} }
// browser // browser
@@ -258,15 +238,213 @@ class Rpc extends Wrapper {
const dataRaw = Utils.prepareTransfer(state) const dataRaw = Utils.prepareTransfer(state)
mp.trigger(Events.LOCAL_EVENT_LISTENER, dataRaw) mp.trigger(Events.LOCAL_EVENT_LISTENER, dataRaw)
return (await this.responseHandler(state.uuid)).data }
function _is1StParamPlayer(x: unknown): x is PlayerMp {
return typeof x === 'object'
}
function _is2NdParamEventName(x: unknown): x is string {
return typeof x === 'string'
} }
} }
/** /**
* Calls a server-side event from browser or client * Calls an asynchronous client-side event from server or browser, expecting a response. Use 'callAsync' to call client from client
*
* @template Arguments - An array of argument types to be passed to the client event
* @template EventName - A string or union representing the client event name
* @template Return - The type of the value returned by the client event
*
* @param {EventName} eventName - The name of the client event to be called
* @param {Arguments} [args] - Optional arguments to pass to the client event
* @returns {Promise<Return>} A promise resolving to the return value of the client event
*
* @example
* // Calls an event on a client without specifying a player
* callClient<[], string, object>('onDataRequest').then(response => {
* console.log(`Received: ${response}`) // ^ object
* })
*/
public async callClientAsync<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
Return = unknown,
>(eventName: EventName, args?: Arguments): Promise<Return>
/**
* Calls an asynchronous client-side event from server or browser, expecting a response. Use 'callAsync' to call client from client
*
* @template Arguments - An array of argument types to be passed to the client event
* @template EventName - A string or union representing the client event name
* @template Return - The type of the value returned by the client event
*
* @param {PlayerMp} player - The player for whom the client event is called
* @param {EventName} eventName - The name of the client event to be called
* @param {Arguments} [args] - Optional arguments to pass to the client event
* @returns {Promise<Return>} A promise resolving to the return value of the client event
*
* @example
* // Calls an event on a client for a specific player
* callClient<[string, number], string, boolean>(player, 'onPlayerAction', ['jump', 2]).then(result => {
* console.log(`Action success: ${result}`) // ^ boolean
* })
*/
public async callClientAsync<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
Return = unknown,
>(player: PlayerMp, eventName: EventName, args?: Arguments): Promise<Return>
public async callClientAsync(
playerOrEventName: PlayerMp | string,
eventNameOrArgs?: string | unknown[],
args?: unknown[],
) {
if (_is1StParamPlayer(playerOrEventName)) {
this.log(
'callClientAsync',
eventNameOrArgs as string,
playerOrEventName,
eventNameOrArgs,
args,
)
} else {
this.log(
'callClientAsync',
playerOrEventName as string,
eventNameOrArgs,
)
}
if (this.forceBrowserDevMode_) return
Utils.errorUnknownEnvironment(this.environment_)
if (this.environment_ === Environment.CLIENT) {
// client
return await this.callAsync(
playerOrEventName as string,
args as unknown[],
)
}
// server
if (
this.environment_ === Environment.SERVER &&
_is1StParamPlayer(playerOrEventName) &&
_is2NdParamEventName(eventNameOrArgs)
) {
let state: RPCState = {
uuid: Utils.generateUUID(),
eventName: eventNameOrArgs,
calledTo: Environment.CLIENT,
calledFrom: this.environment_,
knownError: undefined,
data: args as unknown[],
type: RPCEventType.EVENT,
}
const dataRaw = Utils.prepareTransfer(state)
playerOrEventName.call(Events.LOCAL_EVENT_LISTENER, [dataRaw])
return await this.responseHandler(state.uuid)
.then(r => r.data)
.catch((e: string) => {
state.knownError = e
return this.triggerError_(state)
})
}
// browser
if (
this.environment_ === Environment.BROWSER &&
!_is1StParamPlayer(playerOrEventName) &&
!_is2NdParamEventName(eventNameOrArgs)
) {
const state: RPCState = {
uuid: Utils.generateUUID(),
eventName: playerOrEventName,
calledTo: Environment.CLIENT,
calledFrom: this.environment_,
knownError: undefined,
data: eventNameOrArgs,
type: RPCEventType.EVENT,
}
const dataRaw = Utils.prepareTransfer(state)
mp.trigger(Events.LOCAL_EVENT_LISTENER, dataRaw)
return await this.responseHandler(state.uuid)
.then(r => r.data)
.catch((e: string) => {
state.knownError = e
return this.triggerError_(state)
})
}
function _is1StParamPlayer(x: unknown): x is PlayerMp {
return typeof x === 'object'
}
function _is2NdParamEventName(x: unknown): x is string {
return typeof x === 'string'
}
}
/**
* Calls a server-side event from browser or client. Use 'call' to call server from server
* *
* @template Arguments - An array of argument types to be passed to the server event * @template Arguments - An array of argument types to be passed to the server event
* @template EventName - A string representing the server event name or union of names * @template EventName - A string or union representing the server event name
*
* @param {EventName} eventName - The name of the server event to be called
* @param {Arguments} [args] - Optional arguments to pass to the server event
* @returns {void}
*
* @example
* // Calls an event on a server
* callServer<[], string, object>('onDataRequest')
*/
public callServer<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(eventName: EventName, args?: Arguments): void {
this.log('callServer', eventName, args)
if (this.forceBrowserDevMode_) return
Utils.errorUnknownEnvironment(this.environment_)
const state: RPCState = {
uuid: Utils.generateUUID(),
eventName,
calledTo: Environment.SERVER,
calledFrom: this.environment_,
knownError: undefined,
data: args,
type: RPCEventType.EVENT,
}
const dataRaw = Utils.prepareTransfer(state)
switch (this.environment_) {
case Environment.SERVER:
this.callSelf(state)
break
case Environment.CLIENT:
mp.events.callRemote(Events.LOCAL_EVENT_LISTENER, dataRaw)
break
case Environment.BROWSER:
mp.trigger(Events.LOCAL_EVENT_LISTENER, dataRaw)
break
}
}
/**
* Calls an asynchronous server-side event from browser or client, expecting a response. Use 'callAsync' to call server from server
*
* @template Arguments - An array of argument types to be passed to the server event
* @template EventName - A string or union representing the server event name
* @template Return - The type of the value returned by the server event * @template Return - The type of the value returned by the server event
* *
* @param {EventName} eventName - The name of the server event to be called * @param {EventName} eventName - The name of the server event to be called
@@ -274,17 +452,17 @@ class Rpc extends Wrapper {
* @returns {Promise<Return>} A promise resolving to the return value of the server event * @returns {Promise<Return>} A promise resolving to the return value of the server event
* *
* @example * @example
* // Calls an event on server * // Calls an event on a server
* callServer<[], string, object>('onDataRequest').then(response => { * callServer<[], string, object>('onDataRequest').then(response => {
* console.log(`Received: ${response}`) // ^ object * console.log(`Received: ${response}`) // ^ object
* }) * })
*/ */
public async callServer< public async callServerAsync<
Arguments extends unknown[] = unknown[], Arguments extends unknown[] = unknown[],
EventName extends string = string, EventName extends string = string,
Return extends unknown = unknown, Return = unknown,
>(eventName: EventName, args?: Arguments): Promise<Return> { >(eventName: EventName, args?: Arguments): Promise<Return> {
this.log('callServer', eventName, args) this.log('callServerAsync', eventName, args)
if (this.forceBrowserDevMode_) if (this.forceBrowserDevMode_)
return undefined as unknown as Promise<Return> return undefined as unknown as Promise<Return>
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
@@ -303,7 +481,7 @@ class Rpc extends Wrapper {
switch (this.environment_) { switch (this.environment_) {
case Environment.SERVER: case Environment.SERVER:
return this.callSelf(state) return this.callSelfAsync(state)
case Environment.CLIENT: case Environment.CLIENT:
mp.events.callRemote(Events.LOCAL_EVENT_LISTENER, dataRaw) mp.events.callRemote(Events.LOCAL_EVENT_LISTENER, dataRaw)
@@ -314,14 +492,122 @@ class Rpc extends Wrapper {
break break
} }
return (await this.responseHandler(state.uuid)).data return await this.responseHandler(state.uuid)
.then(r => r.data)
.catch((e: string) => {
state.knownError = e
return this.triggerError_(state)
})
} }
/** /**
* Calls a browser-side event from server or client * Calls a browser-side event from client or server. Use 'call' to call browser from browser
*
* @template Arguments - An array of argument types to be passed to the browser event
* @template EventName - A string or union representing the browser event name
*
* @param {EventName} eventName - The name of the browser event to be called
* @param {Arguments} [args] - Optional arguments to pass to the browser event
* @returns {void}
*
* @example
* // Calls an event on a browser without specifying a player
* callBrowser<[], string, object>('onDataRequest')
*/
public callBrowser<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(eventName: EventName, args?: Arguments): void
/**
* Calls a browser-side event from client or server. Use 'call' to call browser from browser
* *
* @template Arguments - An array of argument types to be passed to the browser event * @template Arguments - An array of argument types to be passed to the browser event
* @template EventName - A string representing the browser event name or union of names * @template EventName - A string representing the browser event name or union of names
*
* @param {PlayerMp} player - The player for whom the browser event is called
* @param {EventName} eventName - The name of the browser event to be called
* @param {Arguments} [args] - Optional arguments to pass to the browser event
* @returns {void}
*
* @example
* // Calls an event on a browser for a specific player
* callBrowser<[string, number], string, boolean>(player, 'onPlayerAction', ['jump', 2])
*/
public callBrowser<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(player: PlayerMp, eventName: EventName, args?: Arguments): void
public callBrowser(
playerOrEventName: PlayerMp | string,
eventNameOrArgs?: string | unknown[],
args?: unknown[],
) {
if (_is1StParamPlayer(playerOrEventName)) {
this.log(
'callBrowser',
eventNameOrArgs as string,
playerOrEventName,
eventNameOrArgs,
args,
)
} else {
this.log(
'callBrowser',
playerOrEventName as string,
eventNameOrArgs,
)
}
if (this.forceBrowserDevMode_) return
Utils.errorUnknownEnvironment(this.environment_)
const state: RPCState = {
uuid: Utils.generateUUID(),
eventName: !_is1StParamPlayer(playerOrEventName)
? playerOrEventName
: _is2NdParamEventName(eventNameOrArgs)
? eventNameOrArgs
: '',
calledTo: Environment.BROWSER,
calledFrom: this.environment_,
knownError: undefined,
data: _is1StParamPlayer(playerOrEventName) ? args : eventNameOrArgs,
type: RPCEventType.EVENT,
}
const dataRaw = Utils.prepareTransfer(state)
switch (this.environment_) {
case Environment.BROWSER:
this.callSelf(state)
break
case Environment.CLIENT:
mp.events.callRemote(Events.LOCAL_EVENT_LISTENER, dataRaw)
break
case Environment.SERVER:
;(playerOrEventName as PlayerMp).call(
Events.LOCAL_EVENT_LISTENER,
[dataRaw],
)
break
}
function _is1StParamPlayer(x: unknown): x is PlayerMp {
return typeof x === 'object'
}
function _is2NdParamEventName(x: unknown): x is string {
return typeof x === 'string'
}
}
/**
* Calls an asynchronous browser-side event from client or server, expecting response. Use 'callAsync' to call browser from browser
*
* @template Arguments - An array of argument types to be passed to the browser event
* @template EventName - A string or union representing the browser event name
* @template Return - The type of the value returned by the browser event * @template Return - The type of the value returned by the browser event
* *
* @param {EventName} eventName - The name of the browser event to be called * @param {EventName} eventName - The name of the browser event to be called
@@ -329,18 +615,18 @@ class Rpc extends Wrapper {
* @returns {Promise<Return>} A promise resolving to the return value of the browser event * @returns {Promise<Return>} A promise resolving to the return value of the browser event
* *
* @example * @example
* // Calls an event on browser without specifying a player * // Calls an event on a browser without specifying a player
* callBrowser<[], string, object>('onDataRequest').then(response => { * callBrowser<[], string, object>('onDataRequest').then(response => {
* console.log(`Received: ${response}`) // ^ object * console.log(`Received: ${response}`) // ^ object
* }) * })
*/ */
public async callBrowser< public async callBrowserAsync<
Arguments extends unknown[] = unknown[], Arguments extends unknown[] = unknown[],
EventName extends string = string, EventName extends string = string,
Return extends unknown = unknown, Return = unknown,
>(eventName: EventName, args?: Arguments): Promise<Return> >(eventName: EventName, args?: Arguments): Promise<Return>
/** /**
* Calls a browser-side event from server or client * Calls an asynchronous browser-side event from client or server, expecting response. Use 'callAsync' to call browser from browser
* *
* @template Arguments - An array of argument types to be passed to the browser event * @template Arguments - An array of argument types to be passed to the browser event
* @template EventName - A string representing the browser event name or union of names * @template EventName - A string representing the browser event name or union of names
@@ -352,45 +638,40 @@ class Rpc extends Wrapper {
* @returns {Promise<Return>} A promise resolving to the return value of the browser event * @returns {Promise<Return>} A promise resolving to the return value of the browser event
* *
* @example * @example
* // Calls an event on a browser for a specific player * // Calls an asynchronous event on a browser for a specific player
* callBrowser<[string, number], string, boolean>(player, 'onPlayerAction', ['jump', 2]).then(result => { * callBrowser<[string, number], string, boolean>(player, 'onPlayerAction', ['jump', 2]).then(result => {
* console.log(`Action success: ${result}`) // ^ boolean * console.log(`Action success: ${result}`) // ^ boolean
* }) * })
*/ */
public async callBrowser< public async callBrowserAsync<
Arguments extends unknown[] = unknown[], Arguments extends unknown[] = unknown[],
EventName extends string = string, EventName extends string = string,
Return extends unknown = unknown, Return = unknown,
>(player: PlayerMp, eventName: EventName, args?: Arguments): Promise<Return> >(player: PlayerMp, eventName: EventName, args?: Arguments): Promise<Return>
public async callBrowser( public async callBrowserAsync(
playerOrEventName: PlayerMp | string, playerOrEventName: PlayerMp | string,
eventNameOrArgs?: string | unknown[], eventNameOrArgs?: string | unknown[],
args?: unknown[], args?: unknown[],
) { ) {
_is1StParamPlayer(playerOrEventName) if (_is1StParamPlayer(playerOrEventName)) {
? this.log( this.log(
'DEV callClient', 'callBrowserAsync',
eventNameOrArgs as string, eventNameOrArgs as string,
playerOrEventName, playerOrEventName,
eventNameOrArgs, eventNameOrArgs,
args, args,
) )
: this.log( } else {
'DEV callClient', this.log(
playerOrEventName as string, 'callBrowserAsync',
eventNameOrArgs, playerOrEventName as string,
) eventNameOrArgs,
)
}
if (this.forceBrowserDevMode_) return if (this.forceBrowserDevMode_) return
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
function _is1StParamPlayer(x: unknown): x is PlayerMp {
return typeof x === 'object'
}
function _is2NdParamEventName(x: unknown): x is string {
return typeof x === 'string'
}
const state: RPCState = { const state: RPCState = {
uuid: Utils.generateUUID(), uuid: Utils.generateUUID(),
eventName: !_is1StParamPlayer(playerOrEventName) eventName: !_is1StParamPlayer(playerOrEventName)
@@ -423,14 +704,62 @@ class Rpc extends Wrapper {
break break
} }
return (await this.responseHandler(state.uuid)).data return await this.responseHandler(state.uuid)
.then(r => r.data)
.catch((e: string) => {
state.knownError = e
return this.triggerError_(state)
})
function _is1StParamPlayer(x: unknown): x is PlayerMp {
return typeof x === 'object'
}
function _is2NdParamEventName(x: unknown): x is string {
return typeof x === 'string'
}
} }
/** /**
* Calls an event in current environment * Calls an event in current environment
* *
* @template Arguments - An array of argument types to be passed to the event * @template Arguments - An array of argument types to be passed to the event
* @template EventName - A string representing the event name or union of names * @template EventName - A string or union representing the event name
*
* @param {EventName} eventName - The name of the event to be called
* @param {Arguments} [args] - Optional arguments to pass to the event
* @returns {void}
*
* @example
* // Calls an event in current environment
* call<[], string>('getSomething')
*/
public call<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(eventName: EventName, args?: Arguments): void {
this.log('call', eventName, args)
if (this.forceBrowserDevMode_) return
Utils.errorUnknownEnvironment(this.environment_)
const state: RPCState = {
uuid: Utils.generateUUID(),
eventName,
calledTo: this.environment_,
calledFrom: this.environment_,
knownError: undefined,
data: args,
type: RPCEventType.EVENT,
}
this.callSelf(state)
}
/**
* Calls an asynchronous event in current environment, expecting a response
*
* @template Arguments - An array of argument types to be passed to the event
* @template EventName - A string or union representing the event name
* @template Return - The type of the value returned by the event * @template Return - The type of the value returned by the event
* *
* @param {EventName} eventName - The name of the event to be called * @param {EventName} eventName - The name of the event to be called
@@ -443,17 +772,17 @@ class Rpc extends Wrapper {
* console.log(`Received: ${response}`) // ^ number * console.log(`Received: ${response}`) // ^ number
* }) * })
*/ */
public async call< public async callAsync<
Arguments extends unknown[] = unknown[], Arguments extends unknown[] = unknown[],
EventName extends string = string, EventName extends string = string,
Return extends unknown = unknown, Return = unknown,
>(eventName: EventName, args?: Arguments): Promise<Return> { >(eventName: EventName, args?: Arguments): Promise<Return> {
this.log('call', eventName, args) this.log('callAsync', eventName, args)
if (this.forceBrowserDevMode_) if (this.forceBrowserDevMode_)
return undefined as unknown as Promise<Return> return undefined as unknown as Promise<Return>
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
let state: RPCState = { const state: RPCState = {
uuid: Utils.generateUUID(), uuid: Utils.generateUUID(),
eventName, eventName,
calledTo: this.environment_, calledTo: this.environment_,
@@ -463,13 +792,25 @@ class Rpc extends Wrapper {
type: RPCEventType.EVENT, type: RPCEventType.EVENT,
} }
return await this.callSelf<Return>(state) return await this.callSelfAsync<Return>(state)
} }
/** /**
* redirects an event in cases of it calling its own environment * redirects an event in cases of it calling its own environment
*/ */
private async callSelf<Return extends unknown = unknown>( private callSelf(state: RPCState): void {
state = this.verifyEvent_(state)
if (state.knownError) {
this.triggerError_(state, state.knownError)
}
this.state_[state.eventName](...state.data)
}
/**
* redirects an asynchronous event in cases of it calling its own environment
*/
private async callSelfAsync<Return = unknown>(
state: RPCState, state: RPCState,
): Promise<Return> { ): Promise<Return> {
state = this.verifyEvent_(state) state = this.verifyEvent_(state)
+5 -3
View File
@@ -1,8 +1,10 @@
/// <reference types="@ragempcommunity/types-server" />
import { Wrapper } from './wrapper' import { Wrapper } from './wrapper'
import { import {
type PlayerMp,
Environment, Environment,
Events, Events,
type PlayerMp,
RPCEventType, RPCEventType,
RPCState, RPCState,
RpcWrapperConfig, RpcWrapperConfig,
@@ -20,7 +22,7 @@ export class Server extends Wrapper {
) { ) {
super(options) super(options)
if (!!options.forceBrowserDevMode) return if (options.forceBrowserDevMode) return
// specific event to save player in context as it is not available on server -> server calls // specific event to save player in context as it is not available on server -> server calls
mp.events.add( mp.events.add(
@@ -35,7 +37,7 @@ export class Server extends Wrapper {
* NOT INTENDED FOR OUT-OF-CONTEXT USE * NOT INTENDED FOR OUT-OF-CONTEXT USE
*/ */
public _resolveEmitDestination(player: PlayerMp, dataRaw: string) { public _resolveEmitDestination(player: PlayerMp, dataRaw: string) {
let state = Utils.prepareExecution(dataRaw) const state = Utils.prepareExecution(dataRaw)
switch (state.calledTo) { switch (state.calledTo) {
case Environment.SERVER: case Environment.SERVER:
+1 -1
View File
@@ -29,7 +29,7 @@ export class Wrapper {
// checks if event is available (registered) in current environment // checks if event is available (registered) in current environment
protected verifyEvent_(data: string | RPCState): RPCState { protected verifyEvent_(data: string | RPCState): RPCState {
let rpcData = const rpcData =
typeof data === 'string' ? Utils.prepareExecution(data) : data typeof data === 'string' ? Utils.prepareExecution(data) : data
if (!this.state_[rpcData.eventName]) { if (!this.state_[rpcData.eventName]) {
+5 -16
View File
@@ -1,29 +1,18 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig",
"display": "RPC",
"compilerOptions": { "compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"lib": [ "lib": [
"ES6", "es2022",
"dom" "dom"
], ],
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"sourceMap": true, "sourceMap": true,
"outDir": "bin",
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
}, },
"include": [ "include": [
"src/**/*", "src"
"./index.d.ts"
], ],
"exclude": [ "extends": ["../tsconfig.ragefw.json"]
"node_modules",
"dist"
]
} }
+15
View File
@@ -0,0 +1,15 @@
Custom Attribution-NoDerives Software License
Copyright (c) 2024 Entity Seven Group
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.
+59 -26
View File
@@ -1,30 +1,63 @@
{ {
"name": "@entityseven/rage-fw-server", "private": false,
"version": "0.1.0", "name": "@entityseven/rage-fw-server",
"version": "0.2.0",
"files": [
"dist/**/*",
"readme.md",
"LICENSE",
"package.json"
],
"main": "src/index.ts",
"exports": {
".": "./src/index.ts"
},
"publishConfig": {
"access": "public",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/src/index.d.ts", "types": "dist/index.d.ts",
"files": [ "exports": {
"dist/**/*", ".": "./dist/index.js"
"readme.md" }
], },
"scripts": { "scripts": {
"build": "tsup" "build": "tsup"
},
"dependencies": {
"@entityseven/rage-fw-rpc": "workspace:*",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/node": "22.12.0"
},
"peerDependencies": {
"@entityseven/rage-fw-shared-types": "workspace:*",
"@ragempcommunity/types-server": "^2.1.8",
"typescript": "^5.7.3"
},
"description": "Package used on a server-side of your Rage:MP Server",
"keywords": [
"rage-fw-server",
"ragefw",
"rage-fw",
"ragemp",
"rage:mp",
"rage",
"gta5"
],
"author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
}, },
"dependencies": { {
"rage-fw-rpc": "workspace:^" "name": "Oleksandr Honcharov",
}, "email": "[email protected]",
"peerDependencies": { "url": "https://github.com/SashaGoncharov19/"
"@ragempcommunity/types-server": "^2.1.8", }
"rage-fw-shared-types": "workspace:^" ],
}, "license": "Custom-Attribution-NoDerivs",
"description": "RageFW Server side", "gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
"keywords": [],
"author": "SashaGoncharov19",
"contributors": [{
"name": "rilaxik",
"email": "[email protected]",
"url": "https://github.com/rilaxik"
}],
"license": "MIT",
"gitHead": "053e4fd12aa120d53e11e0d2009c0df78c1a2ad0"
} }
+1 -1
View File
@@ -1,2 +1,2 @@
# RageFW Server # RageFW Server
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs) [Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki)
+4 -2
View File
@@ -1,3 +1,5 @@
export * from './player'
export * from './server'
export * from './logger' export * from './logger'
export * from './middleware'
export * from './player'
export * from './rpc'
export * from './server'
+22 -3
View File
@@ -2,9 +2,10 @@ import winston, { format } from 'winston'
const { timestamp, printf, colorize } = format const { timestamp, printf, colorize } = format
export class Logger { /** Used to log in a server console */
private format = printf(({ message, level, timestamp }) => { export class FW_Logger {
return `[${new Date(timestamp).toLocaleTimeString()}] [${level}]: ${message}` private format = printf(({ message, level }) => {
return `[${new Date().toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric', second: 'numeric', fractionalSecondDigits: 2 })}] [${level}]: ${message}`
}) })
private systemLogger = winston.createLogger({ private systemLogger = winston.createLogger({
@@ -23,14 +24,32 @@ export class Logger {
), ),
}) })
/**
* Informational logs. Colored in white
*
* @example
* fw.system.log.info('some information to be logged')
*/
public info(...message: unknown[]) { public info(...message: unknown[]) {
this.systemLogger.info(message.join(' ')) this.systemLogger.info(message.join(' '))
} }
/**
* Warning logs. Colored in yellow
*
* @example
* fw.system.log.warn('warning message')
*/
public warn(...message: unknown[]) { public warn(...message: unknown[]) {
this.systemLogger.warn(message.join(' ')) this.systemLogger.warn(message.join(' '))
} }
/**
* Error logs. Colored in red
*
* @example
* fw.system.log.info('some error information')
*/
public error(...message: unknown[]) { public error(...message: unknown[]) {
this.systemLogger.error(message.join(' ')) this.systemLogger.error(message.join(' '))
} }
+54
View File
@@ -0,0 +1,54 @@
import type * as T from '../types'
export class FW_Middleware {
constructor() {}
private static async execute<EventName extends T.FW_ServerEvent>(
middlewares: T.FW_MiddlewareFunction<EventName>[],
args: T.FW_ServerArgs<EventName>,
): Promise<T.FW_MiddlewareResponseInternal> {
for (let i = 0; i < middlewares.length; i++) {
const result = await middlewares[i](...args)
if (typeof result === 'boolean' && !result)
return { success: result, id: i }
if (typeof result !== 'boolean' && !result.success)
return { ...result, id: i }
}
return {
success: true,
}
}
public static async process<EventName extends T.FW_ServerEvent>(
middlewareOptions: T.FW_MiddlewareOptions<EventName>,
callback: T.FW_ServerCallback<EventName>,
args: T.FW_ServerArgs<EventName>,
) {
if (Array.isArray(middlewareOptions)) {
const middlewaresResponse = await FW_Middleware.execute(
middlewareOptions,
args,
)
if (middlewaresResponse.success) return await callback(...args)
} else {
const middlewaresResponse = await FW_Middleware.execute(
middlewareOptions.executables,
args,
)
if (middlewaresResponse.success) {
return await callback(...args)
} else {
middlewareOptions.onError(
middlewaresResponse.message ??
'Middleware with id ' +
middlewaresResponse.id +
' failed',
)
}
}
}
}
+122 -29
View File
@@ -1,40 +1,133 @@
import { Rpc } from 'rage-fw-rpc' import { rpc } from './rpc'
import type * as T from '../types'
import type { /** Handles event manipulations that require player to be present in context */
_CefEventHasArgs, export class FW_Player {
_ClientEventHasArgs, /**
RageFW_CefArgs, * Triggers a client event from the server with arguments from 'rage-fw-shared-types'
RageFW_CefEvent, *
RageFW_CefReturn, * Formerly known as ``callClient`` or ``emitClient``
RageFW_ClientEvent, *
RageFW_ServerClientArgs, * @param {PlayerMp} player - Player object as an event target
RageFW_ServerClientReturn, * @param eventName - The name of the client event to trigger
} from '../types' * @param [args] - Arguments for the client event, if present
* @returns {void}
export class Player { *
private _rpc: Rpc = new Rpc() * @example
* // Triggering a client event without arguments
get rpc(): Rpc { * fw.player.triggerClient("clientEventName")
return this._rpc *
} * @example
* // Triggering a client event with arguments
public triggerClient<EventName extends RageFW_ClientEvent>( * fw.player.triggerClient("clientEventName", ["message to client"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public triggerClient<EventName extends T.FW_ClientEvent>(
player: PlayerMp, player: PlayerMp,
eventName: EventName, eventName: EventName,
...args: _ClientEventHasArgs<EventName> extends true ...args: T._ClientEventHasArgs<EventName> extends true
? [RageFW_ServerClientArgs<EventName>] ? [T.FW_ClientArgs<EventName>]
: [] : []
): Promise<RageFW_ServerClientReturn<EventName>> { ): void {
return this._rpc.callClient(player, eventName, args) rpc.callClient<typeof args, EventName>(player, eventName, args)
} }
public triggerBrowser<EventName extends RageFW_CefEvent>( /**
* Triggers an asynchronous client event from the server with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callClientAsync`` or ``emitClientAsync``
*
* @param {PlayerMp} player - Player object as an event target
* @param eventName - The name of the client event to trigger
* @param [args] - Arguments for the client event, if present
* @returns {Promise} resolving to the client's response for the event
*
* @example
* // Triggering a client event without arguments
* fw.player.triggerClient("clientEventName")
*
* @example
* // Triggering a client event with arguments
* fw.player.triggerClient("clientEventName", ["message to client"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerClientAsync<EventName extends T.FW_ClientEvent>(
player: PlayerMp, player: PlayerMp,
eventName: EventName, eventName: EventName,
...args: _CefEventHasArgs<EventName> extends true ...args: T._ClientEventHasArgs<EventName> extends true
? [RageFW_CefArgs<EventName>] ? [T.FW_ClientArgs<EventName>]
: [] : []
): Promise<RageFW_CefReturn<EventName>> { ): Promise<T.FW_ClientReturn<EventName>> {
return this._rpc.callBrowser(player, eventName, args) return await rpc.callClientAsync<
typeof args,
EventName,
T.FW_ClientReturn<EventName>
>(player, eventName, args)
}
/**
* Triggers a browser event from the server with arguments from 'rage-fw-shared-types'
*
* Formerly known as ``callBrowser`` or ``emitBrowser``
*
* @param {PlayerMp} player - Player object as an event target
* @param eventName - The name of the browser event to trigger
* @param [args] - Arguments for the browser event, if present
* @returns {void}
*
* @example
* // Triggering a browser event without arguments
* fw.player.triggerBrowser("browserEventName")
*
* @example
* // Triggering a browser event with arguments
* fw.player.triggerBrowser("browserEventName", ["message to browser"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public triggerBrowser<EventName extends T.FW_BrowserEvent>(
player: PlayerMp,
eventName: EventName,
...args: T._BrowserEventHasArgs<EventName> extends true
? [T.FW_BrowserArgs<EventName>]
: []
): void {
rpc.callBrowser<typeof args, EventName>(player, eventName, args)
}
/**
* Triggers an asynchronous browser event from the server with arguments from 'rage-fw-shared-types', and expects to receive a response from an event
*
* Formerly known as ``callBrowserAsync`` or ``emitBrowserAsync``
*
* @param {PlayerMp} player - Player object as an event target
* @param eventName - The name of the browser event to trigger
* @param [args] - Arguments for the browser event, if present
* @returns {Promise} resolving to the browser's response for the event
*
* @example
* // Triggering a browser event without arguments
* fw.player.triggerBrowserAsync("browserEventName")
*
* @example
* // Triggering a browser event with arguments
* fw.player.triggerBrowserAsync("browserEventName", ["message to browser"])
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public async triggerBrowserAsync<EventName extends T.FW_BrowserEvent>(
player: PlayerMp,
eventName: EventName,
...args: T._BrowserEventHasArgs<EventName> extends true
? [T.FW_BrowserArgs<EventName>]
: []
): Promise<T.FW_BrowserReturn<EventName>> {
return await rpc.callBrowserAsync<
typeof args,
EventName,
T.FW_BrowserReturn<EventName>
>(player, eventName, args)
} }
} }
+5
View File
@@ -0,0 +1,5 @@
import { Rpc } from '@entityseven/rage-fw-rpc'
export const rpc = new Rpc({
debugLogs: false,
})
+84 -109
View File
@@ -1,117 +1,92 @@
import { Rpc } from 'rage-fw-rpc' import { rpc } from './rpc'
import { RageFW_ICustomServerEvent } from 'rage-fw-shared-types' import { FW_Middleware } from './middleware'
import type * as T from '../types'
import { nativeEvents } from '../native.events' /** Server-side interactions */
import type { export class FW_Server {
_ServerEventHasArgs, /**
RageFW_ServerArgs, * Registers a server event with an associated callback
RageFW_ServerCallback, *
RageFW_ServerCallbackCustom, * @param eventName - The name of the event to register
RageFW_ServerCallbackNative, * @param callback - The callback function to be executed when the event is triggered
RageFW_ServerEvent, * @param [options] - Optional settings for callback execution
RageFW_ServerReturn, * @param [options.middlewares] - Middleware functions to be checked before the callback executes
} from '../types' * @returns {FW_Server} The current server instance, enabling method chaining
*
export class Server { * @example
private _rpc: Rpc = new Rpc() * // Registering an event
* fw.event.register("playerJoin", (player) => {
get rpc(): Rpc { * fw.system.log.info(`${player.socialClub} has joined the game`)
return this._rpc * })
} *
* @example
private isNativeEvent(eventName: string): eventName is keyof IServerEvents { * // Registering an event with middlewares
return nativeEvents.includes(eventName) * fw.event.register("playerJoin", (player) => {
} * fw.system.log.info(`${player.name} has joined the game`)
* }, {
private registerCustom<EventName extends keyof RageFW_ICustomServerEvent>( * middlewares: [ignoreBots] // <- your middlewares here
* })
*
* // or
*
* fw.event.register("playerJoin", (player) => {
* fw.system.log.info(`${player.socialClub} has joined the game`)
* }, {
* middlewares: {
* executables: [ignoreBots], // <- your middlewares here
* onError: (msg) => fw.system.log.info(`[BOT] ${player.socialClub} has joined the game`)
* }
* })
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public register<EventName extends T.FW_ServerEvent>(
eventName: EventName, eventName: EventName,
callback: RageFW_ServerCallbackCustom<EventName>, callback: T.FW_ServerCallback<EventName>,
): void { options?: {
this._rpc.register( middlewares?: T.FW_MiddlewareOptions<EventName>
eventName, },
// fixme ): FW_Server {
async (args: RageFW_ServerArgs<EventName>, info) => { rpc.register<
await callback([info.player as PlayerMp, ...args]) Parameters<typeof callback>,
}, ReturnType<typeof callback> | Promise<unknown>,
) EventName
>(eventName, async (...data) => {
if (!options?.middlewares) return await callback(...data)
await FW_Middleware.process(options.middlewares, callback, data)
})
return this
} }
private registerNative<EventName extends keyof IServerEvents>( /**
* Unregisters a server event, removing the associated callback
*
* @param eventName - The name of the event to unregister
* @returns {FW_Server} The current server instance, enabling method chaining
*
* @example
* // Unregistering an event
* fw.event.unregister("playerJoin")
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
public unregister<EventName extends T.FW_ServerEvent>(
eventName: EventName, eventName: EventName,
callback: RageFW_ServerCallbackNative<EventName>, ): FW_Server {
): void { rpc.unregister<EventName>(eventName)
mp.events.add(
eventName, return this
(...args: Parameters<IServerEvents[EventName]>) =>
callback([...args]),
)
} }
public register<EventName extends RageFW_ServerEvent>( // fixme
eventName: EventName, // public trigger<EventName extends keyof T.FW_ICustomServerEvent>(
callback: RageFW_ServerCallback<EventName>, // eventName: EventName,
): void { // ...args: T._ServerEventHasArgs<EventName> extends true
if (this.isNativeEvent(eventName)) { // ? [T.FW_ServerArgs<EventName>]
this.registerNative( // : []
eventName, // ): Promise<T.FW_ServerReturn<EventName>> {
callback as RageFW_ServerCallbackNative, // return rpc.call(eventName, args)
) // }
} else {
this.registerCustom(
eventName,
callback as unknown as RageFW_ServerCallbackCustom,
)
}
}
public registerMany<EventName extends RageFW_ServerEvent>(events: {
[event in EventName]: RageFW_ServerCallback<event>
}): void {
Object.entries<RageFW_ServerCallback<EventName>>(events).map(
([eventName, callback]) => {
if (this.isNativeEvent(eventName)) {
this.registerNative(
eventName,
callback as RageFW_ServerCallbackNative,
)
} else {
this.registerCustom(
eventName as keyof RageFW_ICustomServerEvent,
callback as unknown as RageFW_ServerCallbackCustom,
)
}
},
)
}
private unregisterCustom<EventName extends keyof RageFW_ICustomServerEvent>(
eventName: EventName,
): void {
this._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_ServerArgs<EventName>]
: []
): Promise<RageFW_ServerReturn<EventName>> {
return this._rpc.call(eventName, args)
}
} }
+17 -4
View File
@@ -1,11 +1,24 @@
import { Logger, Player, Server } from './core' import { FW_Logger, FW_Player, FW_Server, rpc } from './core'
export type { FW_MiddlewareFunction } from './types'
/**
* Package used on a server-side of your Rage:MP Server
*
* @see {@link https://git.entityseven.com/entityseven/rage-framework/wiki Wiki}
*/
export const fw = { export const fw = {
event: new Server(), /** Server-side interactions */
player: new Player(), event: new FW_Server(),
/** Handles event manipulations that require player to be present in context */
player: new FW_Player(),
/** Handles functions used to interact with the client environment */
system: { system: {
log: new Logger(), /** Used to log in a server console */
log: new FW_Logger(),
}, },
/** ``rage-fw-rpc`` instance used under the hood. It is highly recommended to use this one if you need it instead of creating a new instance */
rpc,
} }
fw.system.log.info( fw.system.log.info(
+28 -12
View File
@@ -1,18 +1,34 @@
import type { RageFW_ICustomCefEvent } from 'rage-fw-shared-types' import type { FW_ICustomBrowserEvent } from '@entityseven/rage-fw-shared-types'
export type RageFW_CefEvent = keyof RageFW_ICustomCefEvent /**
* Union of all available browser event names
* These only include custom events
*/
export type FW_BrowserEvent = keyof FW_ICustomBrowserEvent
export type RageFW_CefArgs<K extends RageFW_CefEvent> = Parameters< /**
RageFW_ICustomCefEvent[K] * Array of arguments of an event you pass as a generic
* These only include custom events
*/
export type FW_BrowserArgs<K extends FW_BrowserEvent> = Parameters<
FW_ICustomBrowserEvent[K]
> >
export type RageFW_CefReturn<K extends RageFW_CefEvent> = ReturnType< /**
RageFW_ICustomCefEvent[K] * Return type of event you pass as a generic
* These only include custom events
*/
export type FW_BrowserReturn<K extends FW_BrowserEvent> = ReturnType<
FW_ICustomBrowserEvent[K]
> >
export type _CefEventHasArgs<EventName extends keyof RageFW_ICustomCefEvent> = /**
keyof RageFW_ICustomCefEvent extends never *
? false */
: Parameters<RageFW_ICustomCefEvent[EventName]>[0] extends undefined export type _BrowserEventHasArgs<
? false EventName extends keyof FW_ICustomBrowserEvent,
: true > = keyof FW_ICustomBrowserEvent extends never
? false
: Parameters<FW_ICustomBrowserEvent[EventName]>[0] extends undefined
? false
: true
+17 -17
View File
@@ -1,35 +1,35 @@
/// <reference types="@ragempcommunity/types-server" /> /// <reference types="@ragempcommunity/types-server" />
import type { RageFW_ICustomClientEvent } from 'rage-fw-shared-types' import type { FW_ICustomClientEvent } from '@entityseven/rage-fw-shared-types'
/** /**
* Union of all available client event names * Union of all available client event names
* These only include custom events * These only include custom events
*/ */
export type RageFW_ClientEvent = keyof RageFW_ICustomClientEvent export type FW_ClientEvent = keyof FW_ICustomClientEvent
/** /**
* Array of arguments of an event you pass as a generic * Array of arguments of an event you pass as a generic
* These only include custom events * These only include custom events
*/ */
export type RageFW_ServerClientArgs<K extends RageFW_ClientEvent> = export type FW_ClientArgs<K extends FW_ClientEvent> = K extends FW_ClientEvent
K extends RageFW_ClientEvent ? Parameters<FW_ICustomClientEvent[K]>
? Parameters<RageFW_ICustomClientEvent[K]> : never
: never
/** /**
* Return type of event you pass as a generic * Return type of event you pass as a generic
* These only include custom events * These only include custom events
*/ */
export type RageFW_ServerClientReturn<K extends RageFW_ClientEvent> = export type FW_ClientReturn<K extends FW_ClientEvent> = K extends FW_ClientEvent
K extends RageFW_ClientEvent ? ReturnType<FW_ICustomClientEvent[K]>
? ReturnType<RageFW_ICustomClientEvent[K]> : never
: never
export type _ClientEventHasArgs< /**
EventName extends keyof RageFW_ICustomClientEvent, *
> = keyof RageFW_ICustomClientEvent extends never */
? false export type _ClientEventHasArgs<EventName extends keyof FW_ICustomClientEvent> =
: Parameters<RageFW_ICustomClientEvent[EventName]>[0] extends undefined keyof FW_ICustomClientEvent extends never
? false ? false
: true : Parameters<FW_ICustomClientEvent[EventName]>[0] extends undefined
? false
: true
+4 -3
View File
@@ -1,3 +1,4 @@
export * from './client' export type * from './browser'
export * from './server' export type * from './client'
export * from './browser' export type * from './middleware'
export type * from './server'
+25
View File
@@ -0,0 +1,25 @@
import type * as T from './server'
export type FW_MiddlewareResponse =
| {
success: boolean
message?: string
}
| boolean
export type FW_MiddlewareResponseInternal = {
success: boolean
message?: string
id?: number
}
export type FW_MiddlewareFunction<EventName extends T.FW_ServerEvent> = (
...args: T.FW_ServerArgs<EventName>
) => Promise<FW_MiddlewareResponse>
export type FW_MiddlewareOptions<EventName extends T.FW_ServerEvent> =
| FW_MiddlewareFunction<EventName>[]
| {
executables: FW_MiddlewareFunction<EventName>[]
onError: (error: string) => unknown
}
+24 -47
View File
@@ -1,79 +1,56 @@
/// <reference types="@ragempcommunity/types-server" /> /// <reference types="@ragempcommunity/types-server" />
import type { import type {
RageFW_ICustomClientEvent, FW_ICustomClientEvent,
RageFW_ICustomServerEvent, FW_ICustomServerEvent,
} from 'rage-fw-shared-types' } from '@entityseven/rage-fw-shared-types'
export type { RageFW_ICustomServerEvent } from 'rage-fw-shared-types'
export type { FW_ICustomServerEvent } from '@entityseven/rage-fw-shared-types'
/** /**
* Union of all available server event names * Union of all available server event names
* These also include system events * These also include system events
*/ */
export type RageFW_ServerEvent = export type FW_ServerEvent = keyof FW_ICustomServerEvent | keyof IServerEvents
| keyof RageFW_ICustomServerEvent
| keyof IServerEvents
/** /**
* Array of arguments for an event, name of which you pass as a generic * Array of arguments for an event, name of which you pass as a generic
* These also include system events * These also include system events
*/ */
export type RageFW_ServerArgs<K extends RageFW_ServerEvent> = export type FW_ServerArgs<K extends FW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent K extends keyof FW_ICustomServerEvent
? Parameters<RageFW_ICustomServerEvent[K]> ? [PlayerMp, ...Parameters<FW_ICustomServerEvent[K]>]
: K extends keyof IServerEvents : K extends keyof IServerEvents
? Parameters<IServerEvents[K]> ? [PlayerMp, 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_ServerCallback<K extends RageFW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent
? RageFW_ServerCallbackCustom<K>
: K extends keyof IServerEvents
? RageFW_ServerCallbackNative<K>
: never : never
/** /**
* Return type for an event, name of which you pass as a generic * Return type for an event, name of which you pass as a generic
* These include system and custom events * These include system and custom events
*/ */
export type RageFW_ServerReturn<K extends RageFW_ServerEvent> = export type FW_ServerReturn<K extends FW_ServerEvent> =
K extends keyof RageFW_ICustomServerEvent K extends keyof FW_ICustomServerEvent
? ReturnType<RageFW_ICustomServerEvent[K]> ? ReturnType<FW_ICustomServerEvent[K]>
: K extends keyof IServerEvents : K extends keyof IServerEvents
? ReturnType<IServerEvents[K]> ? ReturnType<IServerEvents[K]>
: never : void
/** /**
* Array of arguments for an event, name of which you pass as a generic * Callback (function) for an event, name of which you pass as a generic
* These only include custom events * These include system and custom events
*/ */
export type RageFW_ServerCallbackCustom< export type FW_ServerCallback<K extends FW_ServerEvent> = (
K extends keyof RageFW_ICustomServerEvent = keyof RageFW_ICustomServerEvent, ...args: FW_ServerArgs<K>
> = ( ) => Promise<FW_ServerReturn<K>>
payload: [player: PlayerMp, ...args: RageFW_ServerArgs<K>],
) => Promise<RageFW_ServerReturn<K>>
/** /**
* Array of arguments for an event, name of which you pass as a generic *
* These only include system events
*/ */
export type RageFW_ServerCallbackNative< export type _ServerEventHasArgs<EventName extends FW_ServerEvent> =
K extends keyof IServerEvents = keyof IServerEvents, EventName extends keyof FW_ICustomServerEvent
> = ( ? keyof FW_ICustomClientEvent extends never
payload: Parameters<IServerEvents[K]>,
) => Promise<ReturnType<IServerEvents[K]>>
export type _ServerEventHasArgs<EventName extends RageFW_ServerEvent> =
EventName extends keyof RageFW_ICustomServerEvent
? keyof RageFW_ICustomClientEvent extends never
? false ? false
: Parameters< : Parameters<FW_ICustomServerEvent[EventName]>[0] extends undefined
RageFW_ICustomServerEvent[EventName]
>[0] extends undefined
? false ? false
: true : true
: EventName extends keyof IServerEvents : EventName extends keyof IServerEvents
+4 -21
View File
@@ -1,25 +1,8 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"display": "Base", "display": "Server",
"exclude": [
"node_modules"
],
"compilerOptions": { "compilerOptions": {
"incremental": false, "types": ["./node_modules/@ragempcommunity/types-server/index.d.ts"]
"composite": false, },
"target": "ES2022", "extends": ["../tsconfig.ragefw.json"]
"experimentalDecorators": true,
"moduleDetection": "auto",
"module": "CommonJS",
"resolveJsonModule": true,
"declaration": true,
"declarationMap": false,
"sourceMap": false,
"downlevelIteration": false,
"inlineSourceMap": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
} }
+2 -2
View File
@@ -1,12 +1,12 @@
import { defineConfig } from 'tsup' import { defineConfig } from 'tsup'
export default defineConfig({ export default defineConfig({
entry: ['src/index.ts'], entry: ['src/**/*.ts'],
outDir: './dist', outDir: './dist',
format: ['cjs'], format: ['cjs'],
noExternal: ['rage-rpc'],
experimentalDts: true, experimentalDts: true,
splitting: false, splitting: false,
bundle: false,
sourcemap: false, sourcemap: false,
clean: true, clean: true,
}) })
+13
View File
@@ -0,0 +1,13 @@
# Custom Attribution-NoDerives 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.
+25 -7
View File
@@ -1,12 +1,30 @@
{ {
"name": "rage-fw-shared-types", "private": false,
"version": "0.1.0",
"types": "types/types/index.d.ts",
"files": [ "files": [
"types/**/*" "types/**/*",
"readme.md",
"LICENSE",
"package.json"
], ],
"author": "SashaGoncharov19", "types": "types/types/index.d.ts",
"license": "MIT", "name": "@entityseven/rage-fw-shared-types",
"version": "0.2.0",
"description": "Package used among all environments of your Rage-FW based server",
"keywords": ["rage-fw-shared-types", "rage-fw-shared","ragefw", "rage-fw", "ragemp", "rage:mp", "rage", "gta5"],
"type": "module", "type": "module",
"gitHead": "053e4fd12aa120d53e11e0d2009c0df78c1a2ad0" "author": "Entity Seven Group",
"contributors": [
{
"name": "Danya H",
"email": "[email protected]",
"url": "https://github.com/rilaxik/"
},
{
"name": "Oleksandr Honcharov",
"email": "[email protected]",
"url": "https://github.com/SashaGoncharov19/"
}
],
"license": "Custom-Attribution-NoDerives",
"gitHead": "ffd542c1deddb3033e16e0dae7557313ae09b05f"
} }
+1 -1
View File
@@ -1,2 +1,2 @@
# RageFW Shared types # RageFW Shared types
[Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki/Docs) [Read docs for details](https://git.entityseven.com/entityseven/rage-framework/wiki)
+10 -6
View File
@@ -1,9 +1,13 @@
declare module 'rage-fw-shared-types' { declare module '@entityseven/rage-fw-shared-types' {
export interface RageFW_ICustomServerEvent {} export interface FW_ICustomServerEvent {
// customServerEvent(arg1: string, arg2: number): boolean // example event
export interface RageFW_ICustomClientEvent {
cefReady(): void
} }
export interface RageFW_ICustomCefEvent {} export interface FW_ICustomClientEvent {
// customClientEvent(arg1: string, arg2: number): boolean // example event
}
export interface FW_ICustomBrowserEvent {
// customCefEvent(arg1: string, arg2: number): boolean // example event
}
} }
+2 -2
View File
@@ -1,9 +1,9 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "es2022",
"lib": [ "lib": [
"ESNext", "ESNext",
"ES2019", "es2022",
"dom" "dom"
], ],
"moduleResolution": "node", "moduleResolution": "node",
+19 -15
View File
@@ -1,25 +1,29 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Base",
"exclude": [
"node_modules"
],
"compilerOptions": { "compilerOptions": {
"incremental": false, "incremental": false,
"composite": false, "composite": false,
"target": "ES2022",
"experimentalDecorators": true, "target": "es2022",
"moduleDetection": "auto", "moduleDetection": "auto",
"module": "CommonJS", "module": "commonjs",
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"declaration": false, "declaration": false,
"declarationMap": false, "declarationMap": false,
"sourceMap": false,
"downlevelIteration": false, "downlevelIteration": false,
"inlineSourceMap": false, "inlineSourceMap": false,
"esModuleInterop": true, "sourceMap": false
"forceConsistentCasingInFileNames": true, },
"strict": true, "exclude": [
"skipLibCheck": true "node_modules",
} "dist",
} ".tsup"
]
}