• Home
  • Experiences
  • Projects
  • About

Evo

> Description
Evo is a type-safe, schema-based RPC system tailored for FiveM environments. It enables seamless, validated communication across client (rc), server (rs), and UI (ui) contexts in FiveM applications. Contracts are defined using Zod schemas, enabling strong typing, runtime validation, retry logic, and environment enforcement. Evo leverages FiveM-native interfaces such as emitNet, onNet, and RegisterNuiCallbackType to provide a robust and predictable messaging layer across distributed game systems.
> Goals
  • Provide a reliable, validated RPC layer across FiveM's client, server, and UI runtimes.
  • Eliminate runtime bugs using strict schema validation with Zod.
  • Support FiveM-native communication methods with full TypeScript safety.
  • Enable retry logic and timeout handling in unstable or delayed game network conditions.
> Features
  • Built for FiveM runtime environments: Supports communication across rc, rs, and ui layers using emitNet, onNet, RegisterNuiCallbackType.
  • Contract-driven design: Define RPC methods with Zod schemas and auto-generate fully-typed APIs with runtime validation.
  • Cross-environment communication: Handles bi-directional calls between Client ⇄ Server, Client ⇄ UI, and UI ⇄ Client.
  • Retry and timeout handling: Built-in support for configurable retry strategies, delay enforcement, and timeout safeguards.
  • Runtime environment safety: Prevents incorrect usage by validating the execution context before sending or handling RPC calls.
  • Response routing and cleanup: Uses UUIDs to track and clean up response listeners automatically for better memory and safety control.
> Usage
  • // 1. Define a contract using Zod for type-safe args and return values
    import { z } from 'zod';
    import { Contract } from '@smaiill/evo';
    
    const userContract = new Contract({
      getUser: {
        args: z.object({ id: z.number() }),
        returns: z.object({ name: z.string(), email: z.string() }),
      },
    }).build();
  • // 2. Define a server-side RPC listener (RS = server, RC = client)
    userContract.createListener('rs', 'rc', {
      getUser: async ({ id }) => {
        // Simulate user data fetching
        return { name: 'Alice', email: 'alice@example.com' };
      },
    });
  • // 3. Define a client-side API (RC = client, RS = server)
    const api = userContract.createApi('rc', 'rs');
    
    // Make a request to the server from the client
    const user = await api.getUser({ id: 1 });
    // Output: { name: string; email: string }
  • // 4. Add retry options to make the method more resilient
    const contractWithRetry = new Contract({
      getUser: {
        args: z.object({ id: z.number() }),
        returns: z.object({ name: z.string() }),
        retryOptions: {
          max: 3,          // Max 3 attempts
          delay: 1000,     // Wait 1 second between retries
          forceDelay: false,
        },
      },
    }).build();
  • // 5. Global timeout for all API calls in the contract (in ms)
    const contractWithTimeout = new Contract(
      {
        getUser: {
          args: z.object({ id: z.number() }),
          returns: z.object({ name: z.string() }),
        },
      },
      {
        timeout: 5000, // 5 seconds max timeout for RPC calls
      }
    ).build();
> Tech Stack
  • TypeScript
  • Zod
> Learning Outcomes
  • Built a runtime-safe, contract-based RPC system for a multi-environment game framework
  • Applied deep type inference and validation using TypeScript and Zod