From 3e5e5299249b2102c5ad2e17fef9634b5fa2088b Mon Sep 17 00:00:00 2001 From: Oleksandr Honcharov <0976053529@ukr.net> Date: Sat, 29 Mar 2025 15:17:40 +0200 Subject: [PATCH] example usage of WASM in RAGE:MP environment --- apps/client/src/index.ts | 2 +- apps/server/package.json | 2 +- apps/server/src/benchmark/index.ts | 58 +++++++++++++++++++++++ apps/server/src/commands.ts | 10 ---- apps/server/src/index.ts | 72 +++++++++++++++++++++++++++-- apps/server/src/wasm/index.ts | 21 +++++++++ apps/server/src/wasm/release.d.ts | 24 ++++++++++ apps/server/src/wasm/release.js | 4 ++ apps/server/tsconfig.json | 1 + server/release.wasm | Bin 0 -> 4628 bytes tsconfig.json | 2 +- 11 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 apps/server/src/benchmark/index.ts delete mode 100644 apps/server/src/commands.ts create mode 100644 apps/server/src/wasm/index.ts create mode 100644 apps/server/src/wasm/release.d.ts create mode 100644 apps/server/src/wasm/release.js create mode 100644 server/release.wasm diff --git a/apps/client/src/index.ts b/apps/client/src/index.ts index 21e5ac9..32bef0f 100644 --- a/apps/client/src/index.ts +++ b/apps/client/src/index.ts @@ -1,3 +1,3 @@ import { fw } from '@entityseven/rage-fw-client' -fw.player.browser = mp.browsers.new('https://localhost:8080') +fw.player.browser = mp.browsers.new('https://localhost:5173') diff --git a/apps/server/package.json b/apps/server/package.json index 5e5c773..70edd6d 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -5,7 +5,7 @@ "license": "CC BY-ND", "description": "Server side of rage-fw example", "scripts": { - "build": "esbuild src/index.ts --bundle --platform=node --target=node10.4 --outfile=../../server/packages/server/index.js" + "build": "esbuild src/index.ts --bundle --platform=node --target=node14.10 --format=cjs --outfile=../../server/packages/server/index.js" }, "dependencies": { "@entityseven/rage-fw-server": "latest" diff --git a/apps/server/src/benchmark/index.ts b/apps/server/src/benchmark/index.ts new file mode 100644 index 0000000..621fb5e --- /dev/null +++ b/apps/server/src/benchmark/index.ts @@ -0,0 +1,58 @@ +/** + * Calculates Pi using the Leibniz formula for a given number of iterations. + * (Identical logic to the AssemblyScript version) + * @param iterations Number of terms in the series. + * @returns Approximate value of Pi. + */ +export function calculatePiLeibnizTs(iterations: number): number { + let pi_div_4: number = 0.0 + let sign: number = 1.0 + + for (let i: number = 0; i < iterations; i++) { + let term = sign / (2.0 * i + 1.0) + pi_div_4 += term + sign = -sign // Flip the sign + } + return pi_div_4 * 4.0 +} + +/** + * Helper function to check if a number is prime (Trial Division). + * (Identical logic to the AssemblyScript version) + * @param num The number to check. + * @returns True if prime, false otherwise. + */ +function isPrimeTs(num: number): boolean { + if (num <= 1) return false + if (num <= 3) return true + if (num % 2 == 0 || num % 3 == 0) return false + + // Only need to check up to sqrt(num) + // Optimized loop: check 6k ± 1 + for (let i: number = 5; i * i <= num; i = i + 6) { + if (num % i == 0 || num % (i + 2) == 0) { + return false + } + } + return true +} + +/** + * Counts prime numbers up to a given limit using trial division. + * (Identical logic to the AssemblyScript version) + * @param limit The upper bound (exclusive) to search for primes. + * @returns The count of prime numbers found. + */ +export function countPrimesTs(limit: number): number { + let count: number = 0 + for (let i: number = 2; i < limit; i++) { + if (isPrimeTs(i)) { + count++ + } + } + return count +} + +export function addTs(a: number, b: number): number { + return a + b +} diff --git a/apps/server/src/commands.ts b/apps/server/src/commands.ts deleted file mode 100644 index 16929cc..0000000 --- a/apps/server/src/commands.ts +++ /dev/null @@ -1,10 +0,0 @@ -mp.events.addCommand('veh', async (player, fullText) => { - const vehicleID = mp.joaat(fullText) - const vehicle = mp.vehicles.new(vehicleID, player.position, { - numberPlate: 'admin', - }) - - setTimeout(() => { - player.putIntoVehicle(vehicle, 0) - }, 250) -}) diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index e167789..0d21bd5 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -1,9 +1,71 @@ -import { fw } from '@entityseven/rage-fw-server' +import { InitWasm } from './wasm' +import { addTs, calculatePiLeibnizTs, countPrimesTs } from './benchmark' -import './commands' +import { performance } from 'perf_hooks' +/** + * Runs a function, measures its execution time, and prints the result. + * @param name Name of the benchmark. + * @param func The function to execute. + * @param args Arguments to pass to the function. + */ +function runBenchmark( + name: string, + func: (...args: any[]) => any, + ...args: any[] +) { + console.log(`\nRunning benchmark: ${name}`) + console.log(`Arguments: ${args.join(', ')}`) -fw.event.register('playerJoin', async player => { - fw.system.log.info(`Connected: ${player.socialClub}`) + const startTime = performance.now() + const result = func(...args) + const endTime = performance.now() + const duration = endTime - startTime - fw.system.log.info(`Response from client: here respionse}`) + console.log(`Result: ${result}`) + console.log(`Execution Time: ${duration.toFixed(3)} ms`) + return duration +} + +// --- Benchmark Parameters --- +const PI_ITERATIONS = 100_000_000_000 // High number for Pi calculation +const PRIME_LIMIT = 100_000_000 // Limit for prime counting +// --- End Benchmark Parameters --- + +InitWasm().then(wasm => { + // Verify AS module loaded + const sumAs = wasm.addAs(5, 7) + console.log(`AS Module Loaded Verification (addAs(5, 7)): ${sumAs}`) + if (sumAs !== 12) { + console.warn('AS addAs function did not return expected result!') + } + const sumTs = addTs(5, 7) + console.log(`TS Implementation Verification (addTs(5, 7)): ${sumTs}`) + + // --- Run Benchmarks --- + + // Benchmark: Calculate Pi + runBenchmark( + 'TypeScript: Calculate Pi (Leibniz)', + calculatePiLeibnizTs, + PI_ITERATIONS, + ) + runBenchmark( + 'AssemblyScript: Calculate Pi (Leibniz)', + wasm.calculatePiLeibnizAs, + PI_ITERATIONS, + ) + + // Benchmark: Count Primes + runBenchmark( + 'TypeScript: Count Primes (Trial Division)', + countPrimesTs, + PRIME_LIMIT, + ) + runBenchmark( + 'AssemblyScript: Count Primes (Trial Division)', + wasm.countPrimesAs, + PRIME_LIMIT, + ) + + console.log('\nBenchmark finished.') }) diff --git a/apps/server/src/wasm/index.ts b/apps/server/src/wasm/index.ts new file mode 100644 index 0000000..3107433 --- /dev/null +++ b/apps/server/src/wasm/index.ts @@ -0,0 +1,21 @@ +import { __AdaptedExports, instantiate } from './release' + +import * as path from 'node:path' +import * as fs from 'node:fs/promises' + +export async function InitWasm(): Promise { + return new Promise(async resolve => { + const wasmFilePath = path.resolve(__dirname, 'release.wasm') + + const wasm: typeof __AdaptedExports = await instantiate( + await (async () => { + return globalThis.WebAssembly.compile( + await fs.readFile(wasmFilePath), + ) + })(), + { env: '' }, + ) + + resolve(wasm) + }) +} diff --git a/apps/server/src/wasm/release.d.ts b/apps/server/src/wasm/release.d.ts new file mode 100644 index 0000000..5b0c339 --- /dev/null +++ b/apps/server/src/wasm/release.d.ts @@ -0,0 +1,24 @@ +declare namespace __AdaptedExports { + /** + * assembly/index/calculatePiLeibnizAs + * @param iterations `i32` + * @returns `f64` + */ + export function calculatePiLeibnizAs(iterations: number): number; + /** + * assembly/index/countPrimesAs + * @param limit `i32` + * @returns `i32` + */ + export function countPrimesAs(limit: number): number; + /** + * assembly/index/addAs + * @param a `i32` + * @param b `i32` + * @returns `i32` + */ + export function addAs(a: number, b: number): number; +} +/** Instantiates the compiled WebAssembly module with the given imports. */ +export declare function instantiate(module: WebAssembly.Module, imports: { +}): Promise; diff --git a/apps/server/src/wasm/release.js b/apps/server/src/wasm/release.js new file mode 100644 index 0000000..7e98f71 --- /dev/null +++ b/apps/server/src/wasm/release.js @@ -0,0 +1,4 @@ +export async function instantiate(module, imports = {}) { + const { exports } = await WebAssembly.instantiate(module, imports); + return exports; +} diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index 24a4ac0..0d64422 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -4,6 +4,7 @@ "resolveJsonModule": true, "baseUrl": "./src", "types": [ + "node", "../../node_modules/@ragempcommunity/types-server/", "../shared/declarations/rage-fw-shared-types/" ] diff --git a/server/release.wasm b/server/release.wasm new file mode 100644 index 0000000000000000000000000000000000000000..91b0034eb7db9c76a77407e5f0462390b285793a GIT binary patch literal 4628 zcmb7IU5Ff66+ZXgs-Eh;HAy9zMR43rbydlZKS}f@vLIwmWM+2a><0W<@Ihuelip;i z=dXJ@lMTf5JnV`{lpqVb5OE$B;)4sr;)CLgB7)+&h)-cv$iptU;)5VU#AV}mZdG-9 zG7xZ@?z(-?J%8W%yVKO#T2n-%J`pUb0WA?N>A^q^@C6@O(vmm8A1#@|;0EYwpvRSI zuW$RU3md&Y`DWsn773nLQ)5~iQlyOW4IZ`WaG;p%|Mx+BFBF#hH?Sft*%^LSXpkbZ?$JPdTXsd9X+z#T3x=f3i)5S++OZ%U0I`JfjSbZ zBaQz&dF*SX2YR}A#dHV`YGYZkjwx3B!@=w%9&~OpG&WY=V7Nag+s}3VRGbhd$hWqo)S%i*)J!2En89HnJKx1dA!SGLQIT>Oa;9{U(2(+SC11sK zS_fiCWkv6__Uw<8GEkBFv!!sSrjYuo;-{Q#D)S*YGHI>pxWh!VehxS(Kh_BKl%_Aj zl-+>uHTVhsm}A0`KEE60AadNbO%nr9?aF?9AuUTRq7nX&fU8i5lPlwhlbX3z3X&4-*l2C)DsoOhss^4*7&9X$_b z!``;f$N7T?4<48puVQt6GBKAriN|9dSZ`vFz0NjATNcuyc>wT{DiRQSyAtZ0^~E4B z@qn!t`|#4nKGuHh3HA~SuzUU33PefaH8lr)cT#O7*Okc^EWXNm(r zgh0|)yQei$(%N4e)5l9&IJ5^$E5T4%;N$lkgKERYGp5Ul~qqD$^YNAq3Xh(*2 zDTGHknkW&&T9`oWhzzH(HX4w>Rv8HiPHs))NObZ<AU{@u&IBq3jYT2xbr&E7vZ;E`_{wtuASgm&5JpBt!ymz8H^fs#WA^(Bt?~>q zf@wio2ZT=(x~ue7Z<;Lcd`F8I;@#JZh4f9tKvGb83Fx^4nyINkgHHtw(NGeN;fW>C zRNhq5L&1xsYo|`b>#ylE^pmsEH={tlXLb9YK?|&=L#l_=Mk|; zV&#>7RA-B0g$<93Y4 zd5J8;1ba{Mgv+vGD7k34azJpA6n;w112DsZPKIxIChqWgI9o@Tit0A4H{pULH0A&w z?lQhjmJCm{12I2 zKoyq}3C$~-j|);Mr3$-xns=h6MsDe-0iaV+kkT7xf2(eseV2Cb@J^6tN-jD`{d41v z5v(2T&vT;>nnj?70mJC#CXH302Foi62?#9J+x< z_uJ9u0gzusLY5KO}dO6axI`n=O z`=8{v^==yC?n8DR zGoZZBA$dlX$Q<7bMaex~451W-;ci2bhdPBq+-oa^xI;cpA)JA~);>cJLg&0`Go5Pm znZ`%mwHnWT8wYE^evYb%AWzsD$aUtBPfGi~hl{qn2rpN~i32V2t$l&cpeXusKku;G z&J@4e;9CtDYd@$Kw;GwpKW*@y+x}U#*lT1pei?Jn%IR-aiyfN<_7@tVNl^lw%xX*s zz8^k2PARnGtU!v%^axdBL#X8;2lteYU=$yk8z%sOE(W?p3y~;DsGD-T6j!S99tsm4 zATr7&eH_~J%<{mET8Tfaett?(69zbvG?n@+KD^nU^g})|CqwO5^gQk^^8FoJKxM!s zer5q5pLsq(VovsE?6}<6FvPV4uTmt#y;>6{X1R{>k0am0P2!h$qoYf<-dB$SPxt^T z9wnp>G>1-s-Y8i>-iN8Hze+Xgpuq1SxK3R}6!^(F&QB0E zO(h;B4Ga*h>S|LGhV+A;+drBk#5V)0K9Bqh?58vlWq8liI$fkTy#$Ci=nD1S^F@62 zs0G?3(DLSkf9M?5EiQO&es~7s}_LS(G!kfXj3Qfy6 z-G_t?=!mh}z$*qtWE46J-n(`CwD87wKh)U+$0>04A!iMmN_}rNtok(Z-G-eMo{0lI z<+ literal 0 HcmV?d00001 diff --git a/tsconfig.json b/tsconfig.json index 66ce038..454fe54 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ESNext", - "lib": ["ESNext", "ES2019", "DOM"], + "lib": ["ESNext", "DOM"], "moduleResolution": "node", "module": "ESNext", "resolveJsonModule": true,