Repository navigation
Events
This page explains how the event system works in the framework and how different layers communicate with each other.
The framework is built around an event-driven architecture.
Events are used for communication between:
- Server ↔ Client
- Client ↔ Server
The system relies on RAGE:MP’s event system, extended with a consistent naming convention and structure.
There are two main types of events:
These are triggered from the server and received on the client.
Used for:
- sending data to players
- triggering UI updates
- running client-side logic
These are triggered from the client and received on the server.
Used for:
- player actions
- UI interactions
- gameplay requests
Events follow a structured naming format:
layer::category:action
client::task:taskAchieveHeading
client::ui:openInventory
server::player:spawn
-
client::→ event is handled on the client -
server::→ event is handled on the server -
category→ logical grouping (task, ui, player, etc.) -
action→ what the event does
player.call("client::ui:openInventory", [data]);mp.events.add("client::ui:openInventory", (data) => {
// handle UI open
});mp.events.callRemote("server::inventory:useItem", itemId);mp.events.add("server::inventory:useItem", (player, itemId) => {
// handle item usage
});sequenceDiagram
participant P as Player
participant F as Frontend (CEF)
participant C as Client
participant S as Server
P->>F: Clicks "Use Item"
F->>C: Send UI event
C->>S: callRemote("server::inventory:useItem")
S->>S: Validate & process item
S-->>C: Send result / updated data
C-->>F: Emit UI update
F-->>P: Update interface
Never trust client input.
// BAD
// trusting client blindly
// GOOD
// validate item exists, player owns it, etc.Avoid large multi-purpose events.
// BAD
"server::game:doEverything";
// GOOD
"server::inventory:useItem";
"server::inventory:dropItem";Stick to the same pattern across the project. However you are free of will to change it how you would like to, the reason why I went this way was because it made it easier for me to debug or track an error.
Client should not:
- decide game outcomes
- modify persistent data
That belongs to the server.
Whenever possible, define event payloads in the shared layer.
This prevents:
- mismatched data
- runtime errors
- confusion
-
callis for local events -
callRemoteis for server events
Always check player exists and is valid before processing.
One event should do one thing.
Organize events by feature:
server/events/inventory/
client/events/inventory/
shared/events/
This keeps the system modular and scalable, easy to understand and track files, as you may have noticed the whole structure is based on Name.whatitis.ts, so if a system you may developer might have a class, a module and events its easy to track what you want to add or edit in that specific system.
Plain English: Let's say you have a vehicle system or on-top layer of what RAGEMP provides related to vehicles and you have your vehicle class and events. My expectations or what I expect you to do is, create a class file in source/server/classes named Vehicle.class.ts and a event file in source/server/events named Vehicle.events.ts, now instead of navigating to those files by clicking and expanding folders you can use your IDE to go directly at that file by their name, and depending what you want to do you can search for, System.whatsProvided (in this case Vehicle.class or Vehicle.events). But also let's say there's a bug coming from a feature related to vehicles, so the first thing you would search for is vehicle related events, which for you if you created those files would be easy as you know where vehicle events are stored, but if you work with a group of developers then they would struggle to find them, therefore file naming becomes important here and easy for other fellow devs to find stuff.
The event system is the backbone of the framework.
- Server handles authority
- Client acts as bridge
- Events connect everything together
Understanding events is essential for building features in this framework.