From 6ae665ee64314aea6ff578f74652dd25e1121b55 Mon Sep 17 00:00:00 2001 From: Danya H Date: Wed, 5 Feb 2025 15:56:38 +0000 Subject: [PATCH] rage-fw 0.2.0 sidebars --- .prettierrc | 7 + docker-compose.yml | 6 +- docs/.vitepress/config.mts | 141 +++++++++--- docs/.vitepress/theme/index.ts | 10 +- docs/index.md | 5 +- docs/rage-fw-cli/index.md | 31 +-- docs/rage-fw-rpc/0.2.5/index.md | 138 ++++++++---- docs/rage-fw-rpc/0.3.0/index.md | 235 ++++++++++++++++++++ docs/rage-fw-rpc/index.md | 193 +--------------- docs/rage-fw/0.2.0/browser.md | 108 +++++++++ docs/rage-fw/0.2.0/client.md | 150 +++++++++++++ docs/rage-fw/0.2.0/features.md | 92 ++++++++ docs/rage-fw/0.2.0/index.md | 59 +++++ docs/rage-fw/0.2.0/server.md | 139 ++++++++++++ docs/rage-fw/0.2.0/shared-types.md | 24 ++ docs/rage-fw/0.3.0/browser.md | 108 +++++++++ docs/rage-fw/0.3.0/client.md | 150 +++++++++++++ docs/rage-fw/0.3.0/features.md | 92 ++++++++ docs/rage-fw/0.3.0/index.md | 59 +++++ docs/rage-fw/0.3.0/server.md | 139 ++++++++++++ docs/rage-fw/0.3.0/shared-types.md | 24 ++ docs/rage-fw/index.md | 8 + docs/snippets/rage-fw-rpc/0.3.0/snippets.md | 111 +++++++-- index.ts | 1 - package.json | 6 +- readme.md | 3 +- 26 files changed, 1729 insertions(+), 310 deletions(-) create mode 100644 .prettierrc create mode 100644 docs/rage-fw-rpc/0.3.0/index.md create mode 100644 docs/rage-fw/0.2.0/browser.md create mode 100644 docs/rage-fw/0.2.0/client.md create mode 100644 docs/rage-fw/0.2.0/features.md create mode 100644 docs/rage-fw/0.2.0/index.md create mode 100644 docs/rage-fw/0.2.0/server.md create mode 100644 docs/rage-fw/0.2.0/shared-types.md create mode 100644 docs/rage-fw/0.3.0/browser.md create mode 100644 docs/rage-fw/0.3.0/client.md create mode 100644 docs/rage-fw/0.3.0/features.md create mode 100644 docs/rage-fw/0.3.0/index.md create mode 100644 docs/rage-fw/0.3.0/server.md create mode 100644 docs/rage-fw/0.3.0/shared-types.md create mode 100644 docs/rage-fw/index.md delete mode 100644 index.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..39a1f1e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "tabWidth": 2, + "printWidth": 80, + "semi": false, + "singleQuote": true, + "trailingComma": "none" +} diff --git a/docker-compose.yml b/docker-compose.yml index 0a562f0..06b6513 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: entity-seven-docs: -# image: uristri/entity-seven-docs + # image: uristri/entity-seven-docs container_name: entity-seven-docs ports: - - 1339:80 # : - restart: always \ No newline at end of file + - 1339:80 # : + restart: always diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 310de04..ef17bf5 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,45 +1,134 @@ -import { defineConfig } from 'vitepress' +import { SidebarItem, defineConfig } from 'vitepress' import { transformerTwoslash } from '@shikijs/vitepress-twoslash' +import fs from 'node:fs' +import path from 'node:path' // https://vitepress.dev/reference/site-config export default defineConfig({ - title: "Entity Seven Docs", - description: "Short and descriptive", + title: 'Entity Seven Docs', + description: 'Short and descriptive', lastUpdated: true, - markdown: { - codeTransformers: [ - transformerTwoslash({ - - }) - ], - languages: ['js', 'ts'] - }, - themeConfig: { // https://vitepress.dev/reference/default-theme-config + sidebar: { + '/rage-fw-rpc/': [getVersionsByFolder('rage-fw-rpc')], + ...getFwSidebar() + }, + search: { provider: 'local' }, - sidebar: { - '/rage-fw-rpc/': [ - { - text: 'Versions', - collapsed: true, - items: [ - { text: '0.3.0 (latest)', link: '/rage-fw-rpc/' }, - { text: '0.2.5', link: '/rage-fw-rpc/0.2.5' }, - ] - } - ], - }, - socialLinks: [ { icon: 'github', link: 'https://git.entityseven.com/entityseven' }, - { icon: 'discord', link: 'https://discord.com/qPb7CmDF4C' }, + { icon: 'discord', link: 'https://discord.com/qPb7CmDF4C' } ], + + footer: { + copyright: 'Copyright Entity Seven Group © 2024 - Present' + } + }, + + markdown: { + codeTransformers: [transformerTwoslash()], + languages: ['js', 'ts'] } }) + +function getFwSidebar() { + const dir = 'rage-fw' + const versions = getVersionsByFolder(dir) + + const base = { + text: 'Documentation', + collapsed: false + } + + const sidebar = { + [`/${dir}/`]: [versions] + // [`/${dir}`]: [documentation('..'), versions], + } + + for (const version of versions.items) { + if (version.text.includes('latest')) { + const files = listChildFiles(path.resolve('docs', dir)) + + sidebar[`/${dir}/`] = [ + { + ...base, + items: files.map((f) => ({ text: formatFilename(f), link: `./${f}` })) + }, + versions + ] + + continue + } + const v = version.text.replace('(latest)', '').trim() + const files = listChildFiles(path.resolve('docs', dir, v)) + + sidebar[`/${dir}/${v}/`] = [ + { + ...base, + items: files.map((f) => ({ text: formatFilename(f), link: `./${f}` })) + }, + versions + ] + } + + return sidebar + + function formatFilename(filename: string) { + filename = filename.split('.')[0].replace('-', ' ').replace('_', ' ') + return filename.charAt(0).toUpperCase() + filename.slice(1) + } +} + +function getVersionsByFolder(folder: string) { + const folders = listChildFolders(path.resolve('docs', folder)) + const versions: SidebarItem = { + text: 'Versions', + collapsed: true, + items: [] + } + for (const v of folders) { + versions.items.push({ + text: v, + link: `/${folder}/` + v + }) + } + versions.items = versions.items.sort((a, b) => { + const vA = parseInt(a.text.replaceAll('.', ''), 10) + const vB = parseInt(b.text.replaceAll('.', ''), 10) + return vB - vA + }) + versions.items[0].text += ' (latest)' + versions.items[0].link = `/${folder}/` + return versions +} + +function listChildFolders(dir: string) { + const result: string[] = [] + const items = fs.readdirSync(dir) + for (const item of items) { + const fullPath = path.join(dir, item) + const stats = fs.statSync(fullPath) + if (stats.isDirectory()) result.push(item) + } + return result +} + +function listChildFiles(dir: string) { + const result: string[] = [] + const items = fs.readdirSync(dir) + for (const item of items) { + const fullPath = path.join(dir, item) + const stats = fs.statSync(fullPath) + if (stats.isFile()) result.push(item) + } + return result + .filter((e) => !e.startsWith('index')) + .sort((a, b) => ('' + a.attr).localeCompare(b.attr)) +} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index a0ecf17..4e06505 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -5,8 +5,8 @@ import Theme from 'vitepress/theme' import '@shikijs/vitepress-twoslash/style.css' export default { - extends: Theme, - enhanceApp({ app }: EnhanceAppContext) { - app.use(TwoslashFloatingVue) - }, -} \ No newline at end of file + extends: Theme, + enhanceApp({ app }: EnhanceAppContext) { + app.use(TwoslashFloatingVue) + } +} diff --git a/docs/index.md b/docs/index.md index 6677e03..803f18b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,8 +3,8 @@ layout: home hero: - name: "Entity Seven Docs" - text: "Short and descriptive" + name: 'Entity Seven Docs' + text: 'Short and descriptive' tagline: welcome to the future actions: - theme: brand @@ -28,4 +28,3 @@ features: icon: ❔ details: Where may I even put it? --- - diff --git a/docs/rage-fw-cli/index.md b/docs/rage-fw-cli/index.md index 7fd6f84..f26ea37 100644 --- a/docs/rage-fw-cli/index.md +++ b/docs/rage-fw-cli/index.md @@ -1,5 +1,4 @@ --- -lastUpdated: false description: Use Rage FW CLI to minimize scaffolding issues prev: false next: false @@ -19,37 +18,43 @@ bun create @entityseven/rage-fw@latest ``` ## TL;DR -- ``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 + +- `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 ## Options + For now, you will see a few available options. They are described in detail below -- ``Initialize new project`` -- ``Test our RPC`` -- ``Install RAGE:MP updater`` +- `Initialize new project` +- `Test our RPC` +- `Install RAGE:MP updater` ### Initialize new project + 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-example*__ +- `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 front-end`` +- `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 (Also [Contribution](#contribution)) ### Test our RPC + Using this option will forward you to common project creation menu -- ``Enter project name`` +- `Enter project name` 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`` +- `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](https://git.entityseven.com/entityseven/rage-framework) \ No newline at end of file + +If you wish to help us in expanding examples selection with different framework you are very welcome to [open PRs and Issues](https://git.entityseven.com/entityseven/rage-framework) diff --git a/docs/rage-fw-rpc/0.2.5/index.md b/docs/rage-fw-rpc/0.2.5/index.md index 287b54b..fe512fe 100644 --- a/docs/rage-fw-rpc/0.2.5/index.md +++ b/docs/rage-fw-rpc/0.2.5/index.md @@ -1,26 +1,30 @@ --- -lastUpdated: true description: Rage-FW-RPC is an all-in package with asynchronous RPC implementation for RageMP servers in JS/TS prev: false next: false outline: [1, 4] --- -# Rage-FW-RPC +# Rage-FW-RPC + is an all-in package with asynchronous RPC implementation for RageMP servers in JS/TS # Installation -``` shell + +```shell npm i @entityseven/rage-fw-rpc@0.2.5 ``` -```shell + +```shell pnpm i @entityseven/rage-fw-rpc@0.2.5 ``` -```shell + +```shell yarn add @entityseven/rage-fw-rpc@0.2.5 ``` Import installed package and initialize rpc: + ```ts // lib/rpc.js @@ -29,6 +33,7 @@ export const rpc = new Rpc(/* options */) ``` On client-side you have to also specify the browser you want to refer to for events + ```ts // client/index.js @@ -36,14 +41,17 @@ import { Rpc } from 'rage-fw-rpc' export const rpc = new Rpc(/* options */) rpc.browser = mp.browsers.new('package://path-to-your-cef-assets/index.html') ``` + Also see [Rpc Config](#rpc-config) # 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 + - 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 - Error-safe developer mode for browser @@ -51,17 +59,27 @@ Inspired by usage of [rage-rpc](https://github.com/micaww/rage-rpc) - Actual human-readable errors # Points -Before reading docs you should at least barely be acknowledged about the patterns used and how ``rage-fw-rpc`` is different in usage than community favorite ``rage-rpc`` -- Arguments when calling any event must be wrapped in an array. This is done for proper argument typing. Be aware that as of now arguments in ``register`` are SPREAD and not wrapped in array. It is recommended to at least loosely type events, that require passing arrays as arguments to avoid unwanted outcomes. +Before reading docs you should at least barely be acknowledged about the patterns used and how `rage-fw-rpc` is different in usage than community favorite `rage-rpc` + +- Arguments when calling any event must be wrapped in an array. This is done for proper argument typing. Be aware that as of now arguments in `register` are SPREAD and not wrapped in array. It is recommended to at least loosely type events, that require passing arrays as arguments to avoid unwanted outcomes. + ```ts // client-side rpc.callServer('event', [2, 3]) ``` + ```ts // server-side expects number[], but gets two separate numbers as arguments instead -rpc.register('event', (player, argument1: number[] /* (actual: number), argument2 (actual: number) */) => {}) +rpc.register( + 'event', + ( + player, + argument1: number[] /* (actual: number), argument2 (actual: number) */ + ) => {} +) ``` + ```ts // this will save the day rpc.callServer<[number[]]>('event', [2, 3]) // type-error @@ -69,27 +87,33 @@ rpc.callServer<[number[]]>('event', [[2, 3]]) // ✓ ``` - Keep in mind that chaining events this way - + ```ts // eg. client called server rpc.register('customServerEvent', async (args: string) => { - const response: string = await rpc.callBrowser('customCefEvent', [ - 'hello from server', - ]) - // do something with response - return 'response from server' + const response: string = await rpc.callBrowser('customCefEvent', [ + 'hello from server' + ]) + // do something with response + return 'response from server' }) ``` + has two major issues: + 1. Events are marked as timed out after 10s and are throwing if no response was received. This can occur when player has high ping or browser delays the response in any way. Means your event chain may not continue in those cases. Your codebase is entirely up to you, but avoid such issues try not to chain events inside one another -2. await stops function execution. This means any operations in place of ``do something with res`` that do not rely on response are unnecessarily stopped. This can be avoided using ``.then(response => ...)`` giving function an opportunity to execute side-operations in parallel with promises +2. await stops function execution. This means any operations in place of `do something with res` that do not rely on response are unnecessarily stopped. This can be avoided using `.then(response => ...)` giving function an opportunity to execute side-operations in parallel with promises [Ref: await docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#description) [Ref: in detail](https://dev.to/masteringjs/using-then-vs-async-await-in-javascript-2pma) - Every environment can call any environment and get a response via Promise + ```ts server <-> client <-> browser as well as server <-> browser ``` -You can still use ``call`` methods to refer to environment itself + +You can still use `call` methods to refer to environment itself + ```ts // server rpc.callServer(...) @@ -98,9 +122,11 @@ rpc.callClient(...) // browser rpc.callBrowser(...) ``` -It will behave the same way as using ``call`` by just being redirected to it. For better code clearance we do recommend using ``call`` instead of ``call`` to also same a tiny bit of computation resources + +It will behave the same way as using `call` by just being redirected to it. For better code clearance we do recommend using `call` instead of `call` to also same a tiny bit of computation resources - (Extra) Due to async-based logic some IDEs or tools like ESlint can soft-warn you for ignoring promises on events you do not want response from. In that cases you can either + ```ts // await it, but you will have to mark parent function as async await rpc.callServer('event') @@ -112,26 +138,32 @@ rpc.callServer('event').then() # Docs ## Rpc Config + These are the options you can specify when creating Rpc instance. Options can be omitted at all if you want so. All options only have effect in current context and have to be specified individually on server/client/browser + ```ts interface RpcWrapperConfig { - forceBrowserDevMode?: boolean // defaults to false - debugLogs?: boolean // defaults to false + forceBrowserDevMode?: boolean // defaults to false + debugLogs?: boolean // defaults to false } ``` -``forceBrowserDevMode`` - only has effect on browser-side. Fallback for browser to launch without mp context and without errors (eg. for development in browser). Keep in mind that using this makes browser-side unavailable at all preventing from all operations except logging. Therefore is recommended to use in pair with ``debugLogs`` -``debugLogs`` - enables logging all exposed methods to available console. Server/browser: ``console.log``; client: ``mp.console.logInfo`` +`forceBrowserDevMode` - only has effect on browser-side. Fallback for browser to launch without mp context and without errors (eg. for development in browser). Keep in mind that using this makes browser-side unavailable at all preventing from all operations except logging. Therefore is recommended to use in pair with `debugLogs` + +`debugLogs` - enables logging all exposed methods to available console. Server/browser: `console.log`; client: `mp.console.logInfo` + ```ts import { Rpc } from '@entityseven/rage-fw-rpc' export const rpc = new Rpc({ - forceBrowserDevMode: false, - debugLogs: true + forceBrowserDevMode: false, + debugLogs: true }) ``` ## register + Registers a callback function for a specified event + ```ts rpc.register('playerJoin', (player) => { console.log(`Connected: ${player.socialClub}`) @@ -139,75 +171,95 @@ rpc.register('playerJoin', (player) => { ``` ## unregister + Unregisters callback function for a specified event + ```ts rpc.unregister('playerDamage') ``` ## callClient + Calls a client-side event from server or browser From browser: + ```ts -rpc.callClient('updatePlayerData', ['argument']).then(response => { - console.log(`Received: ${response}`) +rpc.callClient('updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) }) ``` + From server (requires player): + ```ts -rpc.callClient(player, 'updatePlayerData', ['argument']).then(response => { - console.log(`Received: ${response}`) +rpc.callClient(player, 'updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) }) ``` ## callServer + Calls a server-side event from browser or client + ```ts -rpc.callServer('updatePlayerData', ['argument']).then(response => { +rpc.callServer('updatePlayerData', ['argument']).then((response) => { console.log(`Received: ${response}`) }) ``` ## callBrowser + Calls a server-side event from browser or client From client: + ```ts -rpc.callBrowser('updatePlayerData', ['argument']).then(response => { - console.log(`Received: ${response}`) +rpc.callBrowser('updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) }) ``` + From client (requires player): + ```ts -rpc.callBrowser(player, 'updatePlayerData', ['argument']).then(response => { - console.log(`Received: ${response}`) +rpc.callBrowser(player, 'updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) }) ``` ## call + Calls an event in current environment + ```ts -rpc.call('triggerSomething').then(response => { - console.log(`Received: ${response}`) +rpc.call('triggerSomething').then((response) => { + console.log(`Received: ${response}`) }) ``` # Errors + - When error is thrown you will get a message of such form + ```ts -`${rpcData.knownError}\n` + // error message -`Caller: ${rpcData.calledFrom}\n` + // server/client/browser -`Receiver: ${this.environment_}\n` + // server/client/browser -`Event: ${rpcData.eventName}\n` + -`Additional Info: ${error}` // actual error object, could be more than one +;`${rpcData.knownError}\n` + // error message + `Caller: ${rpcData.calledFrom}\n` + // server/client/browser + `Receiver: ${this.environment_}\n` + // server/client/browser + `Event: ${rpcData.eventName}\n` + + `Additional Info: ${error}` // actual error object, could be more than one ``` + Hopefully this will give you enough information about the event which throws. Error information is definitely subject to change in future + - Keep in mind that event timeouts are throwing with almost no information due to their current implementation (at least your console can show line number) -- Under the hood we use ``JSON.stringify`` to pass data between environments, but in JS there are a few which [cannot be serialized](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) +- Under the hood we use `JSON.stringify` to pass data between environments, but in JS there are a few which [cannot be serialized](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) ## Codes + These should be clear enough themselves, in other cases refer to here -- ``EVENT_NOT_REGISTERED`` - throws in Promise (rejects) in called environment when event is either already unregistered or not registered yet. If you see this its almost always calling an event before registering -- ``UNKNOWN_ENVIRONMENT`` - throws in any environment that is not recognized as server/client/browser in Rage. Unlike ``rage-rpc`` this is not thrown in browser when launched without mp context IF you specify it in browser [Rpc Config](#rpc-config) -- ``NO_BROWSER`` - throws on client if you failed to specify valid browser for it to refer to when calling browser -- ``EVENT_RESPONSE_TIMEOUT`` - throws in Promise (rejects) when failed to receive a response data from called environment. You may not always want to receive it at all, for now it just works like this. Prefer adding ``catch`` on your events + +- `EVENT_NOT_REGISTERED` - throws in Promise (rejects) in called environment when event is either already unregistered or not registered yet. If you see this its almost always calling an event before registering +- `UNKNOWN_ENVIRONMENT` - throws in any environment that is not recognized as server/client/browser in Rage. Unlike `rage-rpc` this is not thrown in browser when launched without mp context IF you specify it in browser [Rpc Config](#rpc-config) +- `NO_BROWSER` - throws on client if you failed to specify valid browser for it to refer to when calling browser +- `EVENT_RESPONSE_TIMEOUT` - throws in Promise (rejects) when failed to receive a response data from called environment. You may not always want to receive it at all, for now it just works like this. Prefer adding `catch` on your events diff --git a/docs/rage-fw-rpc/0.3.0/index.md b/docs/rage-fw-rpc/0.3.0/index.md new file mode 100644 index 0000000..b21b3c5 --- /dev/null +++ b/docs/rage-fw-rpc/0.3.0/index.md @@ -0,0 +1,235 @@ +--- +description: Rage-FW-RPC is an all-in package with asynchronous RPC implementation for RageMP servers in JS/TS +prev: false +next: false +outline: [2, 4] +--- + +# Rage FW RPC + +is an all-in package with asynchronous RPC implementation for RageMP servers in JS/TS + +## Installation + +```shell +npm i @entityseven/rage-fw-rpc +``` + +```shell +pnpm i @entityseven/rage-fw-rpc +``` + +```shell +yarn add @entityseven/rage-fw-rpc +``` + +Import installed package and initialize rpc: + + + +On client-side you have to also specify the browser you want to refer to for events + + + +::: info +Also see [Rpc Config](#rpc-config) +::: + +## 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 + +- 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 +- Error-safe developer mode for browser +- Calls can receive response from called environments via Promises (browser -> server -> browser, etc.) +- Actual human-readable errors + +## Points + +Before reading docs you should at least barely be acknowledged about the patterns used and how `rage-fw-rpc` is different in usage than community favorite `rage-rpc` + +### Syntax + +Arguments when calling any event must be wrapped in an array. This is done for proper argument typing. Be aware that as of now arguments in `register` are SPREAD and not wrapped in array. It is recommended to at least loosely type events, that require passing arrays as arguments to avoid unwanted outcomes. + + + +### Asynchronous events + +Keep in mind that chaining events this way: + +```ts +// eg. client called server +rpc.register('customServerEvent', async (args: string) => { + const response: string = await rpc.callBrowserAsync('customBrowserEvent', [ + 'hello from server' + ]) + // do something with response + return 'response from server' +}) +``` + +has two major issues: + +1. Asynchronous events are marked as timed out after 10s and are throwing error if no response was received. This can occur when player has high ping or browser delays the response in any way. Means your event chain may not continue in those cases. Your codebase is entirely up to you, but avoid such issues try not to chain events inside one another +2. [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#description) stops function execution. This means any operations in place of `do something with response` that do not rely on response are unnecessarily stopped. This can be avoided using [`.then(response => ...)`](https://dev.to/masteringjs/using-then-vs-async-await-in-javascript-2pma) giving function an opportunity to execute side-operations in parallel with promises + +### Cross-env communication + +Every environment can call any environment and get a response via Promise + +``` +server <-> client <-> browser AND server <-> browser +``` + +You can still use `call` methods to refer to environment itself + +```ts +// server +rpc.callServer(...) +rpc.callServerAsync(...) +// client +rpc.callClient(...) +rpc.callClientAsync(...) +// browser +rpc.callBrowser(...) +rpc.callBrowserAsync(...) +``` + +It will behave the same way as using `call/callAsync` by just being redirected to it. For better code clearance we do recommend using `call/callAsync` instead of `call/callAsync` to also same a tiny bit of computation resources + +- (Extra) Due to async-based logic some IDEs or tools like ESlint can soft-warn you for ignoring promises on events you do not want response from. In that cases you can either + +```ts twoslash + +// ---cut--- +await rpc.callServerAsync('event') +// @annotate: await it, but you will have to mark parent function as async + +rpc.callServerAsync('event').then() +// @annotate: or use .then() in cases you do not want/unable to mark it as async (eg. React.useEffect) +``` + +## Docs + +### Rpc Config + +These are the options you can specify when creating Rpc instance. Options can be omitted at all if you want so. All options only have effect in current context and have to be specified individually on server/client/browser + +```ts +interface RpcWrapperConfig { + forceBrowserDevMode?: boolean // defaults to false + debugLogs?: boolean // defaults to false +} +``` + +`forceBrowserDevMode` - only has effect on browser-side. Fallback for browser to launch without mp context and without errors (e.g. for development in browser). Keep in mind that using this makes browser-side unavailable at all preventing from all operations except logging. Therefore, is recommended to use in pair with `debugLogs` + +`debugLogs` - enables logging all exposed methods to available console. + +::: info Console methods used in different environments +Server/browser: `console.log` + +Client: [`mp.console.logInfo`](https://wiki.rage.mp/wiki/Console::logInfo) +::: + + + +### register + +Registers a callback function for a specified event + + + +### unregister + +Unregisters callback function for a specified event + + + +### callClient + +Calls a synchronous client-side event from server or browser. Won't return any response from client + + + +### callClientAsync + +Calls a synchronous client-side event from server or browser. Will return a response from client + + + +### callServer + +Calls a synchronous server-side event from browser or client. Won't return any response from server + + + +### callServerAsync + +Calls an asynchronous server-side event from browser or client. Will return a response from server + + + +### callBrowser + +Calls a synchronous server-side event from browser or client. Won't return any response from browser + + + +### callBrowserAsync + +Calls a synchronous server-side event from browser or client. Will return a response from browser + + + +### call + +Calls a synchronous event in current environment. Won't return any response. + + + +::: warning +You cannot call server on itself +::: + +### callAsync + +Calls an asynchronous event in current environment. Will return a response. You cannot call server on itself + + + +::: warning +You cannot call server on itself +::: + +## Errors + +- When error is thrown you will get a message of such form + +```ts +;`${rpcData.knownError}\n` + // error message + `Caller: ${rpcData.calledFrom}\n` + // server/client/browser + `Receiver: ${this.environment_}\n` + // server/client/browser + `Event: ${rpcData.eventName}\n` + + `Additional Info: ${error}` // actual error object, could be more than one +``` + +Hopefully this will give you enough information about the event which throws. Error information is definitely subject to change in future + +- Keep in mind that event timeouts are throwing with almost no information due to their current implementation (at least your console can show line number) +- Under the hood we use `JSON.stringify` to pass data between environments, but in JS there are a few which [cannot be serialized](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) + +### Codes + +These should be clear enough themselves, in other cases refer to here + +- `EVENT_NOT_REGISTERED` - throws in Promise (rejects) in called environment when event is either already unregistered or not registered yet. If you see this its almost always calling an event before registering +- `UNKNOWN_ENVIRONMENT` - throws in any environment that is not recognized as server/client/browser in Rage. Unlike `rage-rpc` this is not thrown in browser when launched without mp context IF you specify it in browser [Rpc Config](#rpc-config) +- `NO_BROWSER` - throws on client if you failed to specify valid browser for it to refer to when calling browser +- `EVENT_RESPONSE_TIMEOUT` - throws in Promise (rejects) when failed to receive a response data from called environment. You may not always want to receive it at all, for now it just works like this. Prefer adding `catch` on your events diff --git a/docs/rage-fw-rpc/index.md b/docs/rage-fw-rpc/index.md index 74d9ae9..9c271df 100644 --- a/docs/rage-fw-rpc/index.md +++ b/docs/rage-fw-rpc/index.md @@ -1,198 +1,7 @@ --- -lastUpdated: true description: Rage-FW-RPC is an all-in package with asynchronous RPC implementation for RageMP servers in JS/TS prev: false next: false outline: [2, 4] --- - -# Rage FW RPC -is an all-in package with asynchronous RPC implementation for RageMP servers in JS/TS -## Installation -```shell -npm i @entityseven/rage-fw-rpc -``` -```shell -pnpm i @entityseven/rage-fw-rpc -``` -```shell -yarn add @entityseven/rage-fw-rpc -``` - -Import installed package and initialize rpc: - - - -On client-side you have to also specify the browser you want to refer to for events - - - -::: info -Also see [Rpc Config](#rpc-config) -::: - -## 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 -- 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 -- Error-safe developer mode for browser -- Calls can receive response from called environments via Promises (browser -> server -> browser, etc.) -- Actual human-readable errors - -## Points -Before reading docs you should at least barely be acknowledged about the patterns used and how ``rage-fw-rpc`` is different in usage than community favorite ``rage-rpc`` - -### Syntax -Arguments when calling any event must be wrapped in an array. This is done for proper argument typing. Be aware that as of now arguments in ``register`` are SPREAD and not wrapped in array. It is recommended to at least loosely type events, that require passing arrays as arguments to avoid unwanted outcomes. - - - -### Asynchronous events -Keep in mind that chaining events this way: -```ts -// eg. client called server -rpc.register('customServerEvent', async (args: string) => { - const response: string = await rpc.callBrowserAsync('customBrowserEvent', [ - 'hello from server', - ]) - // do something with response - return 'response from server' -}) -``` -has two major issues: -1. Asynchronous events are marked as timed out after 10s and are throwing error if no response was received. This can occur when player has high ping or browser delays the response in any way. Means your event chain may not continue in those cases. Your codebase is entirely up to you, but avoid such issues try not to chain events inside one another -2. [``await``](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#description) stops function execution. This means any operations in place of ``do something with response`` that do not rely on response are unnecessarily stopped. This can be avoided using [``.then(response => ...)``](https://dev.to/masteringjs/using-then-vs-async-await-in-javascript-2pma) giving function an opportunity to execute side-operations in parallel with promises - -### Cross-env communication -Every environment can call any environment and get a response via Promise -``` -server <-> client <-> browser AND server <-> browser -``` -You can still use ``call`` methods to refer to environment itself -```ts -// server -rpc.callServer(...) -rpc.callServerAsync(...) -// client -rpc.callClient(...) -rpc.callClientAsync(...) -// browser -rpc.callBrowser(...) -rpc.callBrowserAsync(...) -``` -It will behave the same way as using ``call/callAsync`` by just being redirected to it. For better code clearance we do recommend using ``call/callAsync`` instead of ``call/callAsync`` to also same a tiny bit of computation resources - -- (Extra) Due to async-based logic some IDEs or tools like ESlint can soft-warn you for ignoring promises on events you do not want response from. In that cases you can either -```ts twoslash - -// ---cut--- -await rpc.callServerAsync('event') -// @annotate: await it, but you will have to mark parent function as async - -rpc.callServerAsync('event').then() -// @annotate: or use .then() in cases you do not want/unable to mark it as async (eg. React.useEffect) -``` - -## Docs - -### Rpc Config -These are the options you can specify when creating Rpc instance. Options can be omitted at all if you want so. All options only have effect in current context and have to be specified individually on server/client/browser -```ts -interface RpcWrapperConfig { - forceBrowserDevMode?: boolean // defaults to false - debugLogs?: boolean // defaults to false -} -``` -``forceBrowserDevMode`` - only has effect on browser-side. Fallback for browser to launch without mp context and without errors (e.g. for development in browser). Keep in mind that using this makes browser-side unavailable at all preventing from all operations except logging. Therefore, is recommended to use in pair with ``debugLogs`` - -``debugLogs`` - enables logging all exposed methods to available console. - -::: info Console methods used in different environments -Server/browser: ``console.log`` - -Client: [``mp.console.logInfo``](https://wiki.rage.mp/wiki/Console::logInfo) -::: - - - -### register -Registers a callback function for a specified event - - - -### unregister -Unregisters callback function for a specified event - - - -### callClient -Calls a synchronous client-side event from server or browser. Won't return any response from client - - - -### callClientAsync -Calls a synchronous client-side event from server or browser. Will return a response from client - - - -### callServer -Calls a synchronous server-side event from browser or client. Won't return any response from server - - - -### callServerAsync -Calls an asynchronous server-side event from browser or client. Will return a response from server - - - -### callBrowser -Calls a synchronous server-side event from browser or client. Won't return any response from browser - - - -### callBrowserAsync -Calls a synchronous server-side event from browser or client. Will return a response from browser - - - -### call -Calls a synchronous event in current environment. Won't return any response. - - - -::: warning -You cannot call server on itself -::: - -### callAsync -Calls an asynchronous event in current environment. Will return a response. You cannot call server on itself - - - -::: warning -You cannot call server on itself -::: - -## Errors -- When error is thrown you will get a message of such form -```ts -`${rpcData.knownError}\n` + // error message -`Caller: ${rpcData.calledFrom}\n` + // server/client/browser -`Receiver: ${this.environment_}\n` + // server/client/browser -`Event: ${rpcData.eventName}\n` + -`Additional Info: ${error}` // actual error object, could be more than one -``` -Hopefully this will give you enough information about the event which throws. Error information is definitely subject to change in future -- Keep in mind that event timeouts are throwing with almost no information due to their current implementation (at least your console can show line number) -- Under the hood we use ``JSON.stringify`` to pass data between environments, but in JS there are a few which [cannot be serialized](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) - -### Codes -These should be clear enough themselves, in other cases refer to here -- ``EVENT_NOT_REGISTERED`` - throws in Promise (rejects) in called environment when event is either already unregistered or not registered yet. If you see this its almost always calling an event before registering -- ``UNKNOWN_ENVIRONMENT`` - throws in any environment that is not recognized as server/client/browser in Rage. Unlike ``rage-rpc`` this is not thrown in browser when launched without mp context IF you specify it in browser [Rpc Config](#rpc-config) -- ``NO_BROWSER`` - throws on client if you failed to specify valid browser for it to refer to when calling browser -- ``EVENT_RESPONSE_TIMEOUT`` - throws in Promise (rejects) when failed to receive a response data from called environment. You may not always want to receive it at all, for now it just works like this. Prefer adding ``catch`` on your events + \ No newline at end of file diff --git a/docs/rage-fw/0.2.0/browser.md b/docs/rage-fw/0.2.0/browser.md new file mode 100644 index 0000000..6314a6e --- /dev/null +++ b/docs/rage-fw/0.2.0/browser.md @@ -0,0 +1,108 @@ +--- +outline: [2, 4] +--- +# Browser + +Package used on a browser-side of your Rage:MP Server + +## Usage + +```ts +import { fw } from '@entityseven/rage-fw-browser' +``` + +Only usable in Browser environment. For usage in [Server](./server) and [Client](./client) refer to their docs + +## Declaration + +```ts +fw = { + event: Browser, + rpc: Rpc +} +``` + +Further documentation will describe the fields included in `fw` + +## Browser + +Browser-side interactions + +### debugLogs + +Setter. Enables console debug logs for events + +```ts +fw.event.debugLogs = true +``` + +### customLogger + +Setter. Enables console debug logs for events + +```ts +fw.event.customLogger = + () => + (method: string, eventName: string, ...args: unknown[]) => { + // log with desired formatting + } +``` + +### register + +Registers a browser event with an associated callback + +```ts +fw.event.register('showNotification', (message, color) => { + // do something +}) +``` + +### unregister + +Unregisters a browser event, removing the associated callback + +```ts +fw.event.unregister('showNotification') +``` + +### trigger + +Triggers a browser event from the browser with arguments from shared types. Formerly known as `call` or `emit` + +```ts +// without args +fw.event.trigger('browserEventName') +// with args +fw.event.trigger('browserEventName', ['message to me']) +``` + +### triggerServer + +Triggers a server event from the browser with arguments from shared types. Formerly known as `callServer` or `emitServer` + +```ts +// without args +fw.event.triggerServer('serverEventName') +// with args +fw.event.triggerServer('serverEventName', ['message to server']) +``` + +### triggerClient + +Triggers a client event from the browser with arguments from shared types. Formerly known as `callClient` or `emitClient` + +```ts +// without args +fw.event.triggerClient('clientEventName') +// with args +fw.event.triggerClient('clientEventName', ['message to client']) +``` + +### Rpc + +`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 + +#### `forceBrowserDevMode`? + +`rage-fw-rpc` has a setting which enables it to be ran in browser dev mode without MP context ([see Rpc Config](/rage-fw-rpc/#rpc-config)). As `rage-fw-browser` does not expose Rpc constructor, you can use `.env` with `RageFW_forceBrowserDevMode=true` to enable it diff --git a/docs/rage-fw/0.2.0/client.md b/docs/rage-fw/0.2.0/client.md new file mode 100644 index 0000000..cbe1f24 --- /dev/null +++ b/docs/rage-fw/0.2.0/client.md @@ -0,0 +1,150 @@ +--- +outline: [2, 4] +--- +# Client + +Package used on a client-side of your Rage:MP Server + +## Usage + +```ts +import { fw } from '@entityseven/rage-fw-client' +``` + +Only usable in client environment. For usage in [Server](./server) and [Browser](./browser) refer to their docs + +## Declaration + +```ts +fw = { + event: Client, + player: Player, + system: { + log: Logger + }, + rpc: Rpc +} +``` + +Further documentation will describe the fields included in `fw` + +## Client + +Client-side interactions + +### register + +Registers a client event with an associated callback + +```ts +fw.event.register("playerDeath", (player, reason, killer) => { + // do something +}) + +// Registering an event with middlewares +fw.event.register("playerDeath", (player, reason, killer) => { + // do something +}, { + middlewares: [...] // <- your middlewares here +}) + +// or + +fw.event.register("playerDeath", (player, reason, killer) => { + // do something +}, { + middlewares: { + executables: [...], // <- your middlewares here + onError: (msg) => void msg // <- error handling here + } +}) +``` + +### unregister + +Unregisters a client event, removing the associated callback + +```ts +fw.event.unregister('playerDeath') +``` + +## Player + +Handles event manipulations that require player to be present in context + +### browser + +Setter. Also shares browser with `rage-fw-rpc` + +```ts +fw.player.browser = mp.browsers.new('package://index.html') +``` + +### trigger + +Triggers registered client event with passed arguments. Formerly known as `call` or `emit` + +```ts +// without args +fw.player.trigger('clientEventName') +// with args +fw.player.trigger('clientEventName', ['message to me']) +``` + +### triggerServer + +Triggers a server event from the client with arguments from shared types. Formerly known as `callServer` or `emitServer` + +```ts +// without args +fw.player.triggerServer('serverEventName') +// with args +fw.player.triggerServer('serverEventName', ['message to server']) +``` + +### triggerBrowser + +Triggers a browser event from the client with arguments from shared types. Formerly known as `callBrowser` or `emitBrowser` + +```ts +// without args +fw.player.triggerBrowser('browserEventName') +// with args +fw.player.triggerBrowser('browserEventName', ['message to browser']) +``` + +## System + +Handles functions used to interact with the client environment + +### Logger + +Used to log to a client in-game console + +### info + +Informational logs. Colored in white + +```ts +fw.system.log.info('some information to be logged') +``` + +### warn + +Warning logs. Colored in yellow + +```ts +fw.system.log.warn('warning message') +``` + +### error + +Error logs. Colored in red + +```ts +fw.system.log.info('some error information') +``` + +## Rpc + +`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 diff --git a/docs/rage-fw/0.2.0/features.md b/docs/rage-fw/0.2.0/features.md new file mode 100644 index 0000000..f5699c7 --- /dev/null +++ b/docs/rage-fw/0.2.0/features.md @@ -0,0 +1,92 @@ +--- +outline: [2, 4] +--- +# Features + +## Event Middlewares + +Rage FW offers you to add middlewares when registering events in Client and Server environments, to check if callback should be executed or should not. Core class functionality is not exposed to user, but some types are for easier code managing. Here are some key points (in context of Server environment, but also applicable to Client) + +### Declaration + +```ts +register( + eventName, + callback, + options?: { + middlewares?: RageFW_MiddlewareOptions + }, +) + +type RageFW_MiddlewareOptions = + | RageFW_MiddlewareFunction[] + | { + executables: RageFW_MiddlewareFunction[] + onError: (error: string) => unknown + } + +type RageFW_MiddlewareFunction = (...args: T.RageFW_ServerArgs) => + Promise + +type RageFW_MiddlewareResponse = + | { + success: boolean + message?: string + } + | boolean +``` + +### Usage + +`RageFW_MiddlewareFunction` can be imported to type your middlewares, eg.: + +```ts +import { type RageFW_MiddlewareFunction } from '@entityseven/rage-fw-server' + +const isPlayerAdmin: RageFW_MiddlewareFunction<'yourEventName'> = async ( + player, + ...args +) => { + if (player.adminLvl >= 2) { + return true + } else { + return { + success: false, + message: 'You must have administrator rights for this action' + } + } +} + +// or function-like if you want to omit some argument types or hoisting is required + +async function isPlayerAdmin( + player: PlayerMp, + ...args +): ReturnType> { + if (player.adminLvl >= 2) { + return true + } else { + return { + success: false, + message: 'You must have administrator rights for this action' + } + } +} + +fw.event.register( + 'yourEventName', + async (player, ...args) => { + // do an action which requires administrator rights + }, + { + middlewares: { + executables: [isPlayerAdmin], + onError: (e) => {} // notify player about missing permissions + } + } +) +``` + +### Implementation TL;DR (why no next()?) + +Unfortunately in Client-side of Rage:MP every thrown `Error` or `Promise.reject` is not `catch`able, meaning a naughty error window will pop-up on player's screen. Due to this implementation variant we were forced to not throw `Errors` from middlewares and just return middleware result. Server-side can handle errors, but to keep everything consistent we stick up to this variant diff --git a/docs/rage-fw/0.2.0/index.md b/docs/rage-fw/0.2.0/index.md new file mode 100644 index 0000000..64e7473 --- /dev/null +++ b/docs/rage-fw/0.2.0/index.md @@ -0,0 +1,59 @@ +--- +description: RageFW is a ty1pe-safe framework for developing Rage:MP servers +prev: false +next: false +outline: [2, 4] +--- + +# Rage Framework (RageFW) + +RageFW is a type-safe framework for developing Rage:MP servers. Designed with developers in mind, RageFW brings structure and efficiency to your Rage:MP servers, ensuring robust and maintainable code + +## Features + +- **Type-Safe Development:** Eliminate runtime errors and enhance code reliability with RageFW comprehensive type safety, making your RP server's development smoother than a Sunday drive through Los Santos + +- **Wrapped RPC client:** Communicate effortlessly between server, client and cef with RPC system, wrapped in a cozy custom-typed blanket for your peace of mind + +- **Logging System:** Keep track of server activities and debug like a pro with our built-in, feature-rich logging system. After all, even virtual cops need evidence + +## Getting Started + +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/) + +You can find out more about our CLI [here](/rage-fw-cli/) + +```shell +pnpm create @entityseven/rage-fw@latest +``` + +```shell +bun create @entityseven/rage-fw@latest +``` + +This will give you a few options, among them, you can find `Initialize new project`. Use that option to scaffold a new project for yourself using the preferred frontend framework + +## Documentation + +RageFW distibutes under a list of packages: + +- [`@entityseven/rage-fw-server`](./server) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-server) +- [`@entityseven/rage-fw-client`](./client) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-client) +- [`@entityseven/rage-fw-browser`](./browser) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-browser) +- [`@entityseven/rage-fw-shared-types`](./shared-types) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-shared-types) + +These are setup as-should if you are using our CLI, if you still want to setup yourself, please refer to [examples](https://git.entityseven.com/entityseven/rage-framework-example) + +## Contributing + +Join our community of developers and contribute to the ongoing development of RageFW. At the moment the only way to contribute is opening issues + +## Support + +Need help? Reach out via our community forums or contact us directly through our support channels. We're committed to help you as we can + +## License + +Licensed under **Custom Attribution-NoDerives Software License** + +> _RageFW - because in the world of GTA:RP, nobody has time for type errors_ diff --git a/docs/rage-fw/0.2.0/server.md b/docs/rage-fw/0.2.0/server.md new file mode 100644 index 0000000..da6d56b --- /dev/null +++ b/docs/rage-fw/0.2.0/server.md @@ -0,0 +1,139 @@ +--- +outline: [2, 4] +--- +# Server + +Package used on a server-side of your Rage:MP Server + +## Usage + +```ts +import { fw } from '@entityseven/rage-fw-server' +``` + +Only usable in server environment. For usage in [Client](./client) and [Browser](./browser) refer to their sections + +## Declaration + +```ts +fw = { + event: Server, + player: Player, + system: { + log: Logger + }, + rpc: Rpc +} +``` + +Further documentation will describe the fields included in `fw` + +## Server + +Server-side interactions + +### register + +Registers a server event with an associated callback + +```ts +fw.event.register("playerJoin", (player) => { + // do something +}) + +// Registering an event with middlewares +fw.event.register("playerJoin", (player) => { + // do something +}, { + middlewares: [...] // <- your middlewares here +}) + +// or + +fw.event.register("playerJoin", (player) => { + // do something +}, { + middlewares: { + executables: [...], // <- your middlewares here + onError: (msg) => void msg // <- error handling here + } +}) +``` + +### unregister + +Unregisters a server event, removing the associated callback + +```ts +fw.event.unregister('playerJoin') +``` + +### trigger + +Triggers registered server event. Formerly known as `call` or `emit` + +```ts +fw.event.trigger('serverEventName', ['arg1', 2]) +``` + +## Player + +Handles event manipulations that require player to be present in context + +### triggerClient + +Triggers a client event from the server with arguments from shared types. Formerly known as `callClient` or `emitClient` + +```ts +// without args +fw.player.triggerClient('clientEventName') +// with args +fw.player.triggerClient('clientEventName', ['message to client']) +``` + +### triggerBrowser + +Triggers a browser event from the server with arguments from shared types. Formerly known as `callBrowser` or `emitBrowser` + +```ts +// without args +fw.player.triggerBrowser('browserEventName') +// with args +fw.player.triggerBrowser('browserEventName', ['message to browser']) +``` + +## System + +Handles functions used to interact with system environment + +### Logger + +Used to log to a client in-game console + +#### info + +Informational logs. Colored in white + +```ts +fw.system.log.info('some information to be logged') +``` + +#### warn + +Warning logs. Colored in yellow + +```ts +fw.system.log.warn('warning message') +``` + +#### error + +Error logs. Colored in red + +```ts +fw.system.log.info('some error information') +``` + +### Rpc + +`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 diff --git a/docs/rage-fw/0.2.0/shared-types.md b/docs/rage-fw/0.2.0/shared-types.md new file mode 100644 index 0000000..b3f24f3 --- /dev/null +++ b/docs/rage-fw/0.2.0/shared-types.md @@ -0,0 +1,24 @@ +--- +outline: [2, 4] +--- +# Shared types + +Core element of type-safety in our framework system. It allows you to manually type any **custom** events you have in your server system. +There are three interfaces, which share between the system to apply types: + +- `RageFW_ICustomClientEvent` +- `RageFW_ICustomServerEvent` +- `RageFW_ICustomBrowserEvent` + +To apply types for events we use the following system and below is a sample how it applies them your code + +```ts +customEventName(arg1: string, arg2: number): boolean +``` + +```ts +register('customEventName', async (arg1 /*string*/, arg2 /*number*/) => { + // your logic + return true /*Promise*/ +}) +``` \ No newline at end of file diff --git a/docs/rage-fw/0.3.0/browser.md b/docs/rage-fw/0.3.0/browser.md new file mode 100644 index 0000000..79c9bd5 --- /dev/null +++ b/docs/rage-fw/0.3.0/browser.md @@ -0,0 +1,108 @@ +--- +outline: [2, 4] +--- +# Browser + +Package used on a browser-side of your Rage:MP Server + +## Usage + +```ts +import { fw } from '@entityseven/rage-fw-browser' +``` + +Only usable in Browser environment. For usage in [Server](./server) and [Client](./client) refer to their docs + +## Declaration + +```ts +fw = { + event: Browser, + rpc: Rpc +} +``` + +Further documentation will describe the fields included in `fw` + +## Browser + +Browser-side interactions + +### debugLogs + +Setter. Enables console debug logs for events + +```ts +fw.event.debugLogs = true +``` + +### customLogger + +Setter. Enables console debug logs for events + +```ts +fw.event.customLogger = + () => + (method: string, eventName: string, ...args: unknown[]) => { + // log with desired formatting + } +``` + +### register + +Registers a browser event with an associated callback + +```ts +fw.event.register('showNotification', (message, color) => { + // do something +}) +``` + +### unregister + +Unregisters a browser event, removing the associated callback + +```ts +fw.event.unregister('showNotification') +``` + +### trigger + +Triggers a browser event from the browser with arguments from shared types. Formerly known as `call` or `emit` + +```ts +// without args +fw.event.trigger('browserEventName') +// with args +fw.event.trigger('browserEventName', ['message to me']) +``` + +### triggerServer + +Triggers a server event from the browser with arguments from shared types. Formerly known as `callServer` or `emitServer` + +```ts +// without args +fw.event.triggerServer('serverEventName') +// with args +fw.event.triggerServer('serverEventName', ['message to server']) +``` + +### triggerClient + +Triggers a client event from the browser with arguments from shared types. Formerly known as `callClient` or `emitClient` + +```ts +// without args +fw.event.triggerClient('clientEventName') +// with args +fw.event.triggerClient('clientEventName', ['message to client']) +``` + +### Rpc + +`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 + +#### `forceBrowserDevMode`? + +`rage-fw-rpc` has a setting which enables it to be ran in browser dev mode without MP context ([see Rpc Config](/rage-fw-rpc/#rpc-config)). As `rage-fw-browser` does not expose Rpc constructor, you can use `.env` with `RageFW_forceBrowserDevMode=true` to enable it diff --git a/docs/rage-fw/0.3.0/client.md b/docs/rage-fw/0.3.0/client.md new file mode 100644 index 0000000..616b40d --- /dev/null +++ b/docs/rage-fw/0.3.0/client.md @@ -0,0 +1,150 @@ +--- +outline: [2, 4] +--- +# Client + +Package used on a client-side of your Rage:MP Server + +## Usage + +```ts +import { fw } from '@entityseven/rage-fw-client' +``` + +Only usable in client environment. For usage in [Server](./server) and [Browser](./browser) refer to their docs + +## Declaration + +```ts +fw = { + event: Client, + player: Player, + system: { + log: Logger + }, + rpc: Rpc +} +``` + +Further documentation will describe the fields included in `fw` + +## Client + +Client-side interactions + +### register + +Registers a client event with an associated callback + +```ts +fw.event.register("playerDeath", (player, reason, killer) => { + // do something +}) + +// Registering an event with middlewares +fw.event.register("playerDeath", (player, reason, killer) => { + // do something +}, { + middlewares: [...] // <- your middlewares here +}) + +// or + +fw.event.register("playerDeath", (player, reason, killer) => { + // do something +}, { + middlewares: { + executables: [...], // <- your middlewares here + onError: (msg) => void msg // <- error handling here + } +}) +``` + +### unregister + +Unregisters a client event, removing the associated callback + +```ts +fw.event.unregister('playerDeath') +``` + +## Player + +Handles event manipulations that require player to be present in context + +### browser + +Setter. Also shares browser with `rage-fw-rpc` + +```ts +fw.player.browser = mp.browsers.new('package://index.html') +``` + +### trigger + +Triggers registered client event with passed arguments. Formerly known as `call` or `emit` + +```ts +// without args +fw.player.trigger('clientEventName') +// with args +fw.player.trigger('clientEventName', ['message to me']) +``` + +### triggerServer + +Triggers a server event from the client with arguments from shared types. Formerly known as `callServer` or `emitServer` + +```ts +// without args +fw.player.triggerServer('serverEventName') +// with args +fw.player.triggerServer('serverEventName', ['message to server']) +``` + +### triggerBrowser + +Triggers a browser event from the client with arguments from shared types. Formerly known as `callBrowser` or `emitBrowser` + +```ts +// without args +fw.player.triggerBrowser('browserEventName') +// with args +fw.player.triggerBrowser('browserEventName', ['message to browser']) +``` + +## System + +Handles functions used to interact with the client environment + +### Logger + +Used to log to a client in-game console + +### info + +Informational logs. Colored in white + +```ts +fw.system.log.info('some information to be logged') +``` + +### warn + +Warning logs. Colored in yellow + +```ts +fw.system.log.warn('warning message') +``` + +### error + +Error logs. Colored in red + +```ts +fw.system.log.info('some error information') +``` + +## Rpc + +`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 diff --git a/docs/rage-fw/0.3.0/features.md b/docs/rage-fw/0.3.0/features.md new file mode 100644 index 0000000..c3412f3 --- /dev/null +++ b/docs/rage-fw/0.3.0/features.md @@ -0,0 +1,92 @@ +--- +outline: [2, 4] +--- +# Features + +## Event Middlewares + +Rage FW offers you to add middlewares when registering events in Client and Server environments, to check if callback should be executed or should not. Core class functionality is not exposed to user, but some types are for easier code managing. Here are some key points (in context of Server environment, but also applicable to Client) + +### Declaration + +```ts +register( + eventName, + callback, + options?: { + middlewares?: RageFW_MiddlewareOptions + }, +) + +type RageFW_MiddlewareOptions = + | RageFW_MiddlewareFunction[] + | { + executables: RageFW_MiddlewareFunction[] + onError: (error: string) => unknown + } + +type RageFW_MiddlewareFunction = (...args: T.RageFW_ServerArgs) => + Promise + +type RageFW_MiddlewareResponse = + | { + success: boolean + message?: string + } + | boolean +``` + +### Usage + +`RageFW_MiddlewareFunction` can be imported to type your middlewares, eg.: + +```ts +import { type RageFW_MiddlewareFunction } from '@entityseven/rage-fw-server' + +const isPlayerAdmin: RageFW_MiddlewareFunction<'yourEventName'> = async ( + player, + ...args +) => { + if (player.adminLvl >= 2) { + return true + } else { + return { + success: false, + message: 'You must have administrator rights for this action' + } + } +} + +// or function-like if you want to omit some argument types or hoisting is required + +async function isPlayerAdmin( + player: PlayerMp, + ...args +): ReturnType> { + if (player.adminLvl >= 2) { + return true + } else { + return { + success: false, + message: 'You must have administrator rights for this action' + } + } +} + +fw.event.register( + 'yourEventName', + async (player, ...args) => { + // do an action which requires administrator rights + }, + { + middlewares: { + executables: [isPlayerAdmin], + onError: (e) => {} // notify player about missing permissions + } + } +) +``` + +### Implementation TL;DR (why no next()?) + +Unfortunately in Client-side of Rage:MP every thrown `Error` or `Promise.reject` is not `catch`able, meaning a naughty error window will pop-up on player's screen. Due to this implementation variant we were forced to not throw `Errors` from middlewares and just return middleware result. Server-side can handle errors, but to keep everything consistent we stick up to this variant diff --git a/docs/rage-fw/0.3.0/index.md b/docs/rage-fw/0.3.0/index.md new file mode 100644 index 0000000..40f3647 --- /dev/null +++ b/docs/rage-fw/0.3.0/index.md @@ -0,0 +1,59 @@ +--- +description: RageFW is a ty1pe-safe framework for developing Rage:MP servers +prev: false +next: false +outline: [2, 4] +--- + +# Rage Framework (RageFW) + +RageFW is a type-safe framework for developing Rage:MP servers. Designed with developers in mind, RageFW brings structure and efficiency to your Rage:MP servers, ensuring robust and maintainable code + +## Features + +- **Type-Safe Development:** Eliminate runtime errors and enhance code reliability with RageFW comprehensive type safety, making your RP server's development smoother than a Sunday drive through Los Santos + +- **Wrapped RPC client:** Communicate effortlessly between server, client and cef with RPC system, wrapped in a cozy custom-typed blanket for your peace of mind + +- **Logging System:** Keep track of server activities and debug like a pro with our built-in, feature-rich logging system. After all, even virtual cops need evidence + +## Getting Started + +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/) + +You can find out more about our CLI [here](/rage-fw-cli/) + +```shell +pnpm create @entityseven/rage-fw@latest +``` + +```shell +bun create @entityseven/rage-fw@latest +``` + +This will give you a few options, among them, you can find `Initialize new project`. Use that option to scaffold a new project for yourself using the preferred frontend framework + +## Documentation + +RageFW distibutes under a list of packages: + +- [`@entityseven/rage-fw-server`](./server) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-server) +- [`@entityseven/rage-fw-client`](./client) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-client) +- [`@entityseven/rage-fw-browser`](./browser) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-browser) +- [`@entityseven/rage-fw-shared-types`](./shared-types) [npmjs.com](https://www.npmjs.com/package/@entityseven/rage-fw-shared-types) + +These are setup as-should if you are using our CLI, if you still want to setup yourself, please refer to [examples](https://git.entityseven.com/entityseven/rage-framework-example) + +## Contributing + +Join our community of developers and contribute to the ongoing development of RageFW. At the moment the only way to contribute is opening issues + +## Support + +Need help? Reach out via our community forums or contact us directly through our support channels. We're committed to help you as we can + +## License + +Licensed under **Custom Attribution-NoDerives Software License** + +> _RageFW - because in the world of GTA:RP, nobody has time for type errors_ diff --git a/docs/rage-fw/0.3.0/server.md b/docs/rage-fw/0.3.0/server.md new file mode 100644 index 0000000..bcd7c46 --- /dev/null +++ b/docs/rage-fw/0.3.0/server.md @@ -0,0 +1,139 @@ +--- +outline: [2, 4] +--- +# Server + +Package used on a server-side of your Rage:MP Server + +## Usage + +```ts +import { fw } from '@entityseven/rage-fw-server' +``` + +Only usable in server environment. For usage in [Client](./client) and [Browser](./browser) refer to their sections + +## Declaration + +```ts +fw = { + event: Server, + player: Player, + system: { + log: Logger + }, + rpc: Rpc +} +``` + +Further documentation will describe the fields included in `fw` + +## Server + +Server-side interactions + +### register + +Registers a server event with an associated callback + +```ts +fw.event.register("playerJoin", (player) => { + // do something +}) + +// Registering an event with middlewares +fw.event.register("playerJoin", (player) => { + // do something +}, { + middlewares: [...] // <- your middlewares here +}) + +// or + +fw.event.register("playerJoin", (player) => { + // do something +}, { + middlewares: { + executables: [...], // <- your middlewares here + onError: (msg) => void msg // <- error handling here + } +}) +``` + +### unregister + +Unregisters a server event, removing the associated callback + +```ts +fw.event.unregister('playerJoin') +``` + +### trigger + +Triggers registered server event. Formerly known as `call` or `emit` + +```ts +fw.event.trigger('serverEventName', ['arg1', 2]) +``` + +## Player + +Handles event manipulations that require player to be present in context + +### triggerClient + +Triggers a client event from the server with arguments from shared types. Formerly known as `callClient` or `emitClient` + +```ts +// without args +fw.player.triggerClient('clientEventName') +// with args +fw.player.triggerClient('clientEventName', ['message to client']) +``` + +### triggerBrowser + +Triggers a browser event from the server with arguments from shared types. Formerly known as `callBrowser` or `emitBrowser` + +```ts +// without args +fw.player.triggerBrowser('browserEventName') +// with args +fw.player.triggerBrowser('browserEventName', ['message to browser']) +``` + +## System + +Handles functions used to interact with system environment + +### Logger + +Used to log to a client in-game console + +#### info + +Informational logs. Colored in white + +```ts +fw.system.log.info('some information to be logged') +``` + +#### warn + +Warning logs. Colored in yellow + +```ts +fw.system.log.warn('warning message') +``` + +#### error + +Error logs. Colored in red + +```ts +fw.system.log.info('some error information') +``` + +### Rpc + +`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 diff --git a/docs/rage-fw/0.3.0/shared-types.md b/docs/rage-fw/0.3.0/shared-types.md new file mode 100644 index 0000000..2ae7a25 --- /dev/null +++ b/docs/rage-fw/0.3.0/shared-types.md @@ -0,0 +1,24 @@ +--- +outline: [2, 4] +--- +# Shared types + +Core element of type-safety in our framework system. It allows you to manually type any **custom** events you have in your server system. +There are three interfaces, which share between the system to apply types: + +- `RageFW_ICustomClientEvent` +- `RageFW_ICustomServerEvent` +- `RageFW_ICustomBrowserEvent` + +To apply types for events we use the following system and below is a sample how it applies them your code + +```ts +customEventName(arg1: string, arg2: number): boolean +``` + +```ts +register('customEventName', async (arg1 /*string*/, arg2 /*number*/) => { + // your logic + return true /*Promise*/ +}) +``` \ No newline at end of file diff --git a/docs/rage-fw/index.md b/docs/rage-fw/index.md new file mode 100644 index 0000000..87bfbe1 --- /dev/null +++ b/docs/rage-fw/index.md @@ -0,0 +1,8 @@ +--- +description: RageFW is a type-safe framework for developing Rage:MP servers +prev: false +next: false +outline: [2, 4] +--- + + \ No newline at end of file diff --git a/docs/snippets/rage-fw-rpc/0.3.0/snippets.md b/docs/snippets/rage-fw-rpc/0.3.0/snippets.md index 0048666..18a902b 100644 --- a/docs/snippets/rage-fw-rpc/0.3.0/snippets.md +++ b/docs/snippets/rage-fw-rpc/0.3.0/snippets.md @@ -1,5 +1,7 @@ + ::: code-group + ```ts twoslash [lib/rpc.ts] // @filename: node_modules/@types/@entityseven/rage-fw-rpc/index.d.ts @@ -7,11 +9,15 @@ import { Rpc } from '@entityseven/rage-fw-rpc' export const rpc = new Rpc(/* options */) ``` + ::: + + ::: code-group + ```ts twoslash [client/index.ts] // @filename: node_modules/@types/@entityseven/rage-fw-rpc/index.d.ts @@ -20,10 +26,13 @@ import { Rpc } from '@entityseven/rage-fw-rpc' export const rpc = new Rpc(/* options */) rpc.browser = mp.browsers.new('package://path-to-your-cef-assets/index.html') ``` + ::: + + ```ts twoslash // ---cut--- @@ -33,7 +42,7 @@ rpc.browser = mp.browsers.new('package://path-to-your-cef-assets/index.html') rpc.register('event', (player: PlayerMp, argument1: number[]) => {}) // client-side -// @log: Client sends two arguments +// @log: Client sends two arguments rpc.callServer('event', [2, 3]) @@ -46,9 +55,11 @@ rpc.register('event', (player: PlayerMp, argument1: number, argument2: number) = rpc.callServer('event', [2, 3]) // [!code --] rpc.callServer<[number[]]>('event', [[2, 3]]) // [!code ++] ``` + + ```ts twoslash // @filename: node_modules/@types/@entityseven/rage-fw-rpc/index.d.ts @@ -59,15 +70,19 @@ export const rpc = new Rpc({ debugLogs: true }) ``` + + ::: code-group + ```ts [Simple] rpc.register('playerJoin', (player: PlayerMp) => { - console.log(`Connected: ${player.socialClub}`) + console.log(`Connected: ${player.socialClub}`) }) ``` + ```ts twoslash [With types] // ---cut--- @@ -77,31 +92,42 @@ rpc.register< console.log(`Connected: ${player.socialClub}`) }) ``` + ::: + + ::: code-group + ```ts [Simple] rpc.unregister('playerJoin') ``` + ```ts twoslash [With types] // ---cut--- rpc.unregister('playerJoin') ``` + ::: + + ::: code-group + ```ts [Simple (browser)] rpc.callClient('updatePlayerData', ['argument']) ``` + ```ts [Simple (server)] const player = {} satisfies PlayerMp rpc.callClient(player, 'updatePlayerData', ['argument']) ``` + ```ts twoslash [With types (browser)] // ---cut--- @@ -109,6 +135,7 @@ rpc.callClient< [string], ClientEventNames >('updatePlayerData', ['argument']) ``` + ```ts twoslash [With types (server)] // ---cut--- @@ -117,24 +144,30 @@ rpc.callClient< [string], ClientEventNames >(player, 'updatePlayerData', ['argument']) ``` + ::: + + ::: code-group + ```ts [Simple (browser)] -rpc.callClientAsync('updatePlayerData', ['argument']) - .then(response => { - console.log(`Received: ${response}`) - }) +rpc.callClientAsync('updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) +}) ``` + ```ts [Simple (server)] const player = {} satisfies PlayerMp -rpc.callClientAsync(player, 'updatePlayerData', ['argument']) - .then(response => { - console.log(`Received: ${response}`) - }) +rpc + .callClientAsync(player, 'updatePlayerData', ['argument']) + .then((response) => { + console.log(`Received: ${response}`) + }) ``` + ```ts twoslash [With types (browser)] // ---cut--- @@ -145,6 +178,7 @@ rpc.callClientAsync< console.log(`Received: ${response}`) }) ``` + ```ts twoslash [With types (server)] // ---cut--- @@ -156,14 +190,19 @@ rpc.callClientAsync< console.log(`Received: ${response}`) }) ``` + ::: + + ::: code-group + ```ts [Simple] rpc.callServer('updatePlayerData', ['argument']) ``` + ```ts twoslash [With types] // ---cut--- @@ -171,17 +210,21 @@ rpc.callServer< [string], ServerEventNames >('updatePlayerData', ['argument']) ``` + ::: + + ::: code-group + ```ts [Simple] -rpc.callServerAsync('updatePlayerData', ['argument']) - .then(response => { - console.log(`Received: ${response}`) - }) +rpc.callServerAsync('updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) +}) ``` + ```ts twoslash [With types] // ---cut--- @@ -192,18 +235,24 @@ rpc.callServerAsync< console.log(`Received: ${response}`) }) ``` + ::: + + ::: code-group + ```ts [Simple (client)] rpc.callBrowser('updatePlayerData', ['argument']) ``` + ```ts [Simple (server)] const player = {} satisfies PlayerMp rpc.callBrowser('updatePlayerData', ['argument']) ``` + ```ts twoslash [With types (client)] // ---cut--- @@ -211,6 +260,7 @@ rpc.callBrowser< [string], BrowserEventNames >('updatePlayerData', ['argument']) ``` + ```ts twoslash [With types (server)] // ---cut--- @@ -219,22 +269,28 @@ rpc.callBrowser< [string], BrowserEventNames >(player, 'updatePlayerData', ['argument']) ``` + ::: + + ::: code-group + ```ts [Simple (client)] -rpc.callBrowserAsync('updatePlayerData', ['argument']).then(response => { - console.log(`Received: ${response}`) +rpc.callBrowserAsync('updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) }) ``` + ```ts [Simple (server)] const player = {} satisfies PlayerMp -rpc.callBrowserAsync('updatePlayerData', ['argument']).then(response => { - console.log(`Received: ${response}`) +rpc.callBrowserAsync('updatePlayerData', ['argument']).then((response) => { + console.log(`Received: ${response}`) }) ``` + ```ts twoslash [With types (client)] // ---cut--- @@ -245,6 +301,7 @@ rpc.callBrowserAsync< console.log(`Received: ${response}`) }) ``` + ```ts twoslash [With types (server)] // ---cut--- @@ -256,29 +313,39 @@ rpc.callBrowserAsync< console.log(`Received: ${response}`) }) ``` + ::: + + ::: code-group + ```ts [Simple] rpc.call('triggerSomething') ``` + ```ts twoslash [With types] // ---cut--- rpc.call('triggerSomething') ``` + ::: + + ::: code-group + ```ts [Simple] -rpc.callAsync('triggerSomething').then(response => { - console.log(`Received: ${response}`) +rpc.callAsync('triggerSomething').then((response) => { + console.log(`Received: ${response}`) }) ``` + ```ts twoslash [With types] // ---cut--- @@ -289,5 +356,7 @@ rpc.callAsync< console.log(`Received: ${response}`) }) ``` + ::: - \ No newline at end of file + + diff --git a/index.ts b/index.ts deleted file mode 100644 index f67b2c6..0000000 --- a/index.ts +++ /dev/null @@ -1 +0,0 @@ -console.log("Hello via Bun!"); \ No newline at end of file diff --git a/package.json b/package.json index af30779..7dfdcf5 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "devDependencies": { "@shikijs/vitepress-twoslash": "^2.3.0", "@types/bun": "latest", + "prettier": "^3.4.2", "vitepress": "^1.6.3" }, "peerDependencies": { @@ -13,9 +14,10 @@ "scripts": { "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", - "docs:preview": "vitepress preview docs" + "docs:preview": "vitepress preview docs", + "format": "prettier docs/snippets \"!**/declaration.*\" --write" }, "dependencies": { "vitepress-versioning-plugin": "^1.3.0" } -} \ No newline at end of file +} diff --git a/readme.md b/readme.md index 307b7e3..7ee3e9b 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,5 @@ # Entity Seven Docs + Documentation vault for projects under Entity Seven -Built with [VitePress](https://vitepress.dev/) \ No newline at end of file +Built with [VitePress](https://vitepress.dev/)