example usage of NAPI module in RAGE:MP environment
This commit is contained in:
		
							parent
							
								
									3e5e529924
								
							
						
					
					
						commit
						52d611cf4e
					
				| @ -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=node14.10 --format=cjs --outfile=../../server/packages/server/index.js" | ||||
|     "build": "esbuild src/index.ts --bundle --platform=node --target=node14.10 --external:*.node --format=cjs --outfile=../../server/packages/server/index.js" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@entityseven/rage-fw-server": "latest" | ||||
|  | ||||
| @ -1,58 +0,0 @@ | ||||
| /** | ||||
|  * 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 | ||||
| } | ||||
| @ -1,71 +1,3 @@ | ||||
| import { InitWasm } from './wasm' | ||||
| import { addTs, calculatePiLeibnizTs, countPrimesTs } from './benchmark' | ||||
| import { checkLicense, doSensitiveOperation } from './x86_64-pc-windows-msvc' | ||||
| 
 | ||||
| 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(', ')}`) | ||||
| 
 | ||||
|     const startTime = performance.now() | ||||
|     const result = func(...args) | ||||
|     const endTime = performance.now() | ||||
|     const duration = endTime - startTime | ||||
| 
 | ||||
|     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.') | ||||
| }) | ||||
| console.log(doSensitiveOperation('test')) | ||||
|  | ||||
| @ -1,21 +0,0 @@ | ||||
| import { __AdaptedExports, instantiate } from './release' | ||||
| 
 | ||||
| import * as path from 'node:path' | ||||
| import * as fs from 'node:fs/promises' | ||||
| 
 | ||||
| export async function InitWasm(): Promise<typeof __AdaptedExports> { | ||||
|     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) | ||||
|     }) | ||||
| } | ||||
							
								
								
									
										24
									
								
								apps/server/src/wasm/release.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								apps/server/src/wasm/release.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1,24 +0,0 @@ | ||||
| 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<typeof __AdaptedExports>; | ||||
| @ -1,4 +0,0 @@ | ||||
| export async function instantiate(module, imports = {}) { | ||||
|   const { exports } = await WebAssembly.instantiate(module, imports); | ||||
|   return exports; | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								apps/server/src/x86_64-pc-windows-msvc/gta5.win32-x64-msvc.node
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								apps/server/src/x86_64-pc-windows-msvc/gta5.win32-x64-msvc.node
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										31
									
								
								apps/server/src/x86_64-pc-windows-msvc/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								apps/server/src/x86_64-pc-windows-msvc/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| /* tslint:disable */ | ||||
| /* eslint-disable */ | ||||
| 
 | ||||
| /* auto-generated by NAPI-RS */ | ||||
| 
 | ||||
| /** | ||||
|  * # checkLicense | ||||
|  * Accepts a 16-character license key string. | ||||
|  * Gets the server's local IPv4 address, prints it to the console, | ||||
|  * and validates the key. If valid, unlocks other functions. | ||||
|  * | ||||
|  * Returns `true` if the license key is valid, `false` otherwise. | ||||
|  */ | ||||
| export declare function checkLicense(licenseKey: string): boolean | ||||
| /** | ||||
|  * # doSensitiveOperation | ||||
|  * An example function that should only run if the license is validated. | ||||
|  * Takes a name as input and returns a greeting string. | ||||
|  */ | ||||
| export declare function doSensitiveOperation(name: string): string | ||||
| /** | ||||
|  * # getSecretData | ||||
|  * Another example function that should only run if the license is validated. | ||||
|  * Returns a dummy secret number. | ||||
|  */ | ||||
| export declare function getSecretData(): number | ||||
| /** | ||||
|  * # isLicensed (Optional utility) | ||||
|  * Allows JavaScript to check the current license status without triggering exit. | ||||
|  */ | ||||
| export declare function isLicensed(): boolean | ||||
							
								
								
									
										318
									
								
								apps/server/src/x86_64-pc-windows-msvc/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								apps/server/src/x86_64-pc-windows-msvc/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,318 @@ | ||||
| /* tslint:disable */ | ||||
| /* eslint-disable */ | ||||
| /* prettier-ignore */ | ||||
| 
 | ||||
| /* auto-generated by NAPI-RS */ | ||||
| 
 | ||||
| const { existsSync, readFileSync } = require('fs') | ||||
| const { join } = require('path') | ||||
| 
 | ||||
| const { platform, arch } = process | ||||
| 
 | ||||
| let nativeBinding = null | ||||
| let localFileExisted = false | ||||
| let loadError = null | ||||
| 
 | ||||
| function isMusl() { | ||||
|   // For Node 10
 | ||||
|   if (!process.report || typeof process.report.getReport !== 'function') { | ||||
|     try { | ||||
|       const lddPath = require('child_process').execSync('which ldd').toString().trim() | ||||
|       return readFileSync(lddPath, 'utf8').includes('musl') | ||||
|     } catch (e) { | ||||
|       return true | ||||
|     } | ||||
|   } else { | ||||
|     const { glibcVersionRuntime } = process.report.getReport().header | ||||
|     return !glibcVersionRuntime | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| switch (platform) { | ||||
|   case 'android': | ||||
|     switch (arch) { | ||||
|       case 'arm64': | ||||
|         localFileExisted = existsSync(join(__dirname, 'gta5.android-arm64.node')) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.android-arm64.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-android-arm64') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       case 'arm': | ||||
|         localFileExisted = existsSync(join(__dirname, 'gta5.android-arm-eabi.node')) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.android-arm-eabi.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-android-arm-eabi') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       default: | ||||
|         throw new Error(`Unsupported architecture on Android ${arch}`) | ||||
|     } | ||||
|     break | ||||
|   case 'win32': | ||||
|     switch (arch) { | ||||
|       case 'x64': | ||||
|         localFileExisted = existsSync( | ||||
|           join(__dirname, 'gta5.win32-x64-msvc.node') | ||||
|         ) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.win32-x64-msvc.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-win32-x64-msvc') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       case 'ia32': | ||||
|         localFileExisted = existsSync( | ||||
|           join(__dirname, 'gta5.win32-ia32-msvc.node') | ||||
|         ) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.win32-ia32-msvc.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-win32-ia32-msvc') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       case 'arm64': | ||||
|         localFileExisted = existsSync( | ||||
|           join(__dirname, 'gta5.win32-arm64-msvc.node') | ||||
|         ) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.win32-arm64-msvc.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-win32-arm64-msvc') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       default: | ||||
|         throw new Error(`Unsupported architecture on Windows: ${arch}`) | ||||
|     } | ||||
|     break | ||||
|   case 'darwin': | ||||
|     localFileExisted = existsSync(join(__dirname, 'gta5.darwin-universal.node')) | ||||
|     try { | ||||
|       if (localFileExisted) { | ||||
|         nativeBinding = require('./gta5.darwin-universal.node') | ||||
|       } else { | ||||
|         nativeBinding = require('gta5-darwin-universal') | ||||
|       } | ||||
|       break | ||||
|     } catch {} | ||||
|     switch (arch) { | ||||
|       case 'x64': | ||||
|         localFileExisted = existsSync(join(__dirname, 'gta5.darwin-x64.node')) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.darwin-x64.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-darwin-x64') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       case 'arm64': | ||||
|         localFileExisted = existsSync( | ||||
|           join(__dirname, 'gta5.darwin-arm64.node') | ||||
|         ) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.darwin-arm64.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-darwin-arm64') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       default: | ||||
|         throw new Error(`Unsupported architecture on macOS: ${arch}`) | ||||
|     } | ||||
|     break | ||||
|   case 'freebsd': | ||||
|     if (arch !== 'x64') { | ||||
|       throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) | ||||
|     } | ||||
|     localFileExisted = existsSync(join(__dirname, 'gta5.freebsd-x64.node')) | ||||
|     try { | ||||
|       if (localFileExisted) { | ||||
|         nativeBinding = require('./gta5.freebsd-x64.node') | ||||
|       } else { | ||||
|         nativeBinding = require('gta5-freebsd-x64') | ||||
|       } | ||||
|     } catch (e) { | ||||
|       loadError = e | ||||
|     } | ||||
|     break | ||||
|   case 'linux': | ||||
|     switch (arch) { | ||||
|       case 'x64': | ||||
|         if (isMusl()) { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-x64-musl.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-x64-musl.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-x64-musl') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } else { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-x64-gnu.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-x64-gnu.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-x64-gnu') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } | ||||
|         break | ||||
|       case 'arm64': | ||||
|         if (isMusl()) { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-arm64-musl.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-arm64-musl.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-arm64-musl') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } else { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-arm64-gnu.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-arm64-gnu.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-arm64-gnu') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } | ||||
|         break | ||||
|       case 'arm': | ||||
|         if (isMusl()) { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-arm-musleabihf.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-arm-musleabihf.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-arm-musleabihf') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } else { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-arm-gnueabihf.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-arm-gnueabihf.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-arm-gnueabihf') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } | ||||
|         break | ||||
|       case 'riscv64': | ||||
|         if (isMusl()) { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-riscv64-musl.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-riscv64-musl.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-riscv64-musl') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } else { | ||||
|           localFileExisted = existsSync( | ||||
|             join(__dirname, 'gta5.linux-riscv64-gnu.node') | ||||
|           ) | ||||
|           try { | ||||
|             if (localFileExisted) { | ||||
|               nativeBinding = require('./gta5.linux-riscv64-gnu.node') | ||||
|             } else { | ||||
|               nativeBinding = require('gta5-linux-riscv64-gnu') | ||||
|             } | ||||
|           } catch (e) { | ||||
|             loadError = e | ||||
|           } | ||||
|         } | ||||
|         break | ||||
|       case 's390x': | ||||
|         localFileExisted = existsSync( | ||||
|           join(__dirname, 'gta5.linux-s390x-gnu.node') | ||||
|         ) | ||||
|         try { | ||||
|           if (localFileExisted) { | ||||
|             nativeBinding = require('./gta5.linux-s390x-gnu.node') | ||||
|           } else { | ||||
|             nativeBinding = require('gta5-linux-s390x-gnu') | ||||
|           } | ||||
|         } catch (e) { | ||||
|           loadError = e | ||||
|         } | ||||
|         break | ||||
|       default: | ||||
|         throw new Error(`Unsupported architecture on Linux: ${arch}`) | ||||
|     } | ||||
|     break | ||||
|   default: | ||||
|     throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) | ||||
| } | ||||
| 
 | ||||
| if (!nativeBinding) { | ||||
|   if (loadError) { | ||||
|     throw loadError | ||||
|   } | ||||
|   throw new Error(`Failed to load native binding`) | ||||
| } | ||||
| 
 | ||||
| const { checkLicense, doSensitiveOperation, getSecretData, isLicensed } = nativeBinding | ||||
| 
 | ||||
| module.exports.checkLicense = checkLicense | ||||
| module.exports.doSensitiveOperation = doSensitiveOperation | ||||
| module.exports.getSecretData = getSecretData | ||||
| module.exports.isLicensed = isLicensed | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user