Skip to content
shr0x edited this page Mar 25, 2026 · 1 revision

CEF Communication

This page explains how communication works between the server, client, and frontend (CEF UI).


Overview

In a default RAGE:MP setup, communication with CEF follows this pattern:

Server → Client → Frontend

The client is required as a middle layer to forward data to the browser.


Framework Approach

In this framework, communication is abstracted and structured.

Instead of manually managing raw events, the framework introduces a page-based event system:

RAGERP.cef.emit(player, page, pointer, data);
RAGERP.cef.register(page, pointer, handler);

This creates a predictable and organized communication layer between server and frontend.


Communication Flow

flowchart LR
    S[Server]
    C[Client Bridge]
    F[Frontend CEF]

    S --> C --> F
    F --> C --> S
Loading

Event Structure

Events follow this internal format:

cef::page:pointer

Example

cef::inventory:update
cef::hud:setData

This is automatically handled by the framework.


Server → Frontend (Emit)

Method

RAGERP.cef.emit(player, page, pointer, data);

Example

RAGERP.cef.emit(player, "hud", "setData", { level: 1 });

What happens internally

  1. Event name is generated:

    cef::hud:setData
    
  2. Server sends event to client:

    player.call("client::eventManager", [eventName, data]);
  3. Client forwards it to CEF using Browser.processEvent

  4. Frontend receives and handles it


Frontend → Server (Register)

Method

RAGERP.cef.register(page, pointer, handler);

Example

RAGERP.cef.register("inventory", "moveItem", (player, data) => {
  // handle logic
});

What happens internally

  1. Event is registered as:

    server::inventory:moveItem
    
  2. Client forwards frontend events using:

    client::eventManager::emitServer
    
  3. Server receives structured event with:

    • player
    • predefined data

Why RAGERP.cef.register Exists

This system is not meant to:

  • improve performance
  • reduce memory usage
  • add security

Its purpose is to improve:

  • developer experience
  • structure
  • predictability of data

Problem with Default Events

Using default RAGE:MP:

mp.events.add("inventory:update", (player, data) => {
  // what is inside data?
});

Problems:

  • unknown data structure
  • requires debugging or logging
  • requires checking frontend manually
  • no clear contract between layers

Solution: Structured Event Contracts

With this system:

  • Events are split into page + pointer
  • Data is defined in:
    CefData.ts
    
  • Each event has a known structure

This allows:

  • predictable data flow
  • no guessing
  • no back-and-forth between frontend and backend

Type Safety (Core Benefit)

The emit method is strongly typed:

emit<T, K>(player, page: T, pointer: K, data: CefData.Interfaces.CefEventMap[T][K])

This means:

  • wrong data will be caught early
  • event structure is enforced
  • frontend and backend stay synchronized

Client Bridge (Browser)

The client acts as a transport layer.

It does not contain business logic.

Core method

Browser.processEvent(eventName, ...args);

This:

  • receives cef::page:pointer
  • converts it into:
window.callHandler(pointer, data);

Client Responsibilities

  • receive server events
  • forward them to CEF
  • receive frontend events
  • forward them to server

The client is purely a bridge.


Full Flow Example

Using an Item

sequenceDiagram
    participant P as Player
    participant F as Frontend
    participant C as Client
    participant S as Server

    P->>F: Click "Use Item"
    F->>C: emit("inventory:moveItem")
    C->>S: server::inventory:moveItem
    S->>S: Validate & process
    S-->>C: cef::inventory:update
    C-->>F: processEvent("inventory:update")
    F-->>P: Update UI
Loading

Where Code Lives

Server

  • RAGERP.cef.emit
  • RAGERP.cef.register
  • validation
  • business logic
  • database interaction

Client

  • Browser class
  • event forwarding
  • CEF execution

Frontend

  • UI rendering
  • EventManager
  • user interaction

Best Practices

1. Keep logic on server

Server is always authoritative.


2. Treat events as contracts

Each event should:

  • have defined structure
  • match CefData

3. Do not use raw events for UI

Avoid:

mp.events.add(...)

Use:

RAGERP.cef.register(...)

4. Keep frontend lightweight

Frontend should:

  • render
  • emit actions

Not:

  • validate
  • store important state

5. One event = one responsibility

Avoid large, overloaded events.


Common Misunderstandings

"This uses more memory"

Technically yes, but:

  • overhead is negligible
  • benefit in structure is massive

"This replaces validation"

No.

All validation must still happen on the server.


Mental Model

  • Server = logic + authority
  • Client = transport layer
  • Frontend = UI
  • CEF system = structured communication layer

Frontend Event System (EventManager)

The frontend uses a custom event system called EventManager.

This system acts as a local event bus for UI logic.


Event Format

Events follow a consistent structure:

target:name

Examples

chat:setActive
chat:sendMessage
inventory:update

Registering Events

EventManager.addHandler("chat", "setActive", (data) => {
  // handle event
});

This registers a handler for:

chat:setActive

Calling Events

Internally, events are triggered using:

EventManager.callHandler("chat:setActive", data);

This is what gets called from CEF bridge (window.callHandler).


Emitting Events to Server

EventManager.emitServer("chat", "sendMessage", text);

This becomes:

server::chat:sendMessage

Which is forwarded through: Frontend → Client → Server


Emitting Events to Client

EventManager.emitClient("chat", "close");

This becomes:

client::chat:close

How It Connects to CEF

From client side:

window.callHandler("chat:setActive", data);

Which results in:

EventManager.callHandler("chat:setActive", data);

Why This Exists

This system provides:

  • structured event naming
  • modular UI logic
  • separation between features
  • easier debugging
  • no direct dependency on RAGE:MP inside UI

Example (MobX Store)

EventManager.addHandler("chat", "setActive", (data) => {
  this.setActive(data);
});

This connects UI events directly to state updates.


Key Idea

Frontend should not directly depend on:

  • RAGE:MP events
  • server logic

Instead, it uses EventManager as an abstraction layer.


Summary

EventManager is:

  • a frontend event bus
  • used for UI communication
  • connected to the client via CEF bridge
  • responsible for routing target:name events

It keeps frontend clean, modular, and independent from backend logic.


Summary

The framework introduces a structured CEF communication system:

  • Events are split into page:pointer
  • Data is defined in shared contracts
  • Server communicates using emit
  • Server receives using register

This creates:

  • predictable data flow
  • cleaner architecture
  • easier debugging
  • better developer experience

Even though the client is still part of the flow, it is abstracted away to keep development simple and consistent.

🚀 Getting Started

🧠 Architecture

🎮 Features

🛠 Development

❗ Help

Clone this wiki locally