Repository navigation
Architecture
This page explains how the project is structured, how the different layers communicate, and how data flows through the framework.
The framework is built around a clear separation of concerns:
-
source/serverhandles server-side game logic -
source/clienthandles client-side RAGE:MP logic -
source/sharedcontains code shared between layers -
frontendcontains the CEF user interface built separately from the game logic
The server layer is responsible for authoritative game logic.
Typical responsibilities include:
- authentication and character handling
- database access
- game rules
- command handling
- persistence
- deciding what data should be sent to players
The server should be treated as the source of truth for persistent and gameplay-critical data.
The client layer is responsible for local in-game behavior inside RAGE:MP.
Typical responsibilities include:
- reacting to server events
- playing animations
- handling local GTA functions
- showing or hiding UI
- forwarding UI actions back toward the game logic
The client is the bridge between the game engine and the frontend UI.
The shared layer exists to prevent duplication between server and client.
Typical responsibilities include:
- shared types
- shared enums
- shared constants
- shared event names
- data contracts used by multiple layers
This layer is important because it keeps communication predictable and typed.
The frontend is the CEF UI layer.
Typical responsibilities include:
- HUD rendering
- menus
- overlays
- notifications
- interaction pages
- user-facing interface state
The frontend should focus on presentation and user interaction, not core game authority.
Project Root
├── source/
│ ├── server/ -> Server-side logic
│ ├── client/ -> Client-side logic
│ └── shared/ -> Shared contracts, types, utilities
├── frontend/ -> React-based CEF UI
├── client_packages/
├── config/
└── docs/
The framework is not meant to be a single monolithic gamemode file.
Instead, it is designed like a base framework:
- each layer has a clear responsibility
- systems can be extended without mixing everything together
- shared code reduces duplication
- the frontend is isolated from low-level game logic
The project can be understood as four connected parts:
- Server
- Client
- CEF Frontend
- Database
The most important thing to understand is that these parts do not all talk to each other directly.
- Server ⇄ Client
- Client ⇄ Frontend
- Server ⇄ Database
In most cases:
- the server does not talk directly to the frontend
- the frontend does not talk directly to the database
- the client acts as the bridge between game logic and UI
flowchart LR
DB[(PostgreSQL)]
S[Server]
C[Client]
F[Frontend / CEF]
DB <--> S
S <--> C
C <--> F
- persistence
- validation
- permissions
- gameplay decisions
- authoritative state
- local GTA execution
- local rendering behavior
- in-game event reception
- CEF visibility and bridging
- user interface
- visual state
- page rendering
- user actions inside CEF
- contracts and reusable definitions between layers
These boundaries are what keep the framework scalable.
A common flow in this framework looks like this:
- A player performs an action
- The action reaches the server or client
- The server validates and processes it if needed
- The client receives the result
- The client opens or updates the CEF UI
- The frontend renders the new state
- If the player clicks something in the UI, that action goes back through the client and then to the server if required
sequenceDiagram
participant P as Player
participant S as Server
participant C as Client
participant F as Frontend
P->>S: Triggers gameplay action
S->>C: Sends event / data
C->>F: Opens page and passes data
F->>P: Renders interface
This is the general pattern for server-driven UI.
sequenceDiagram
participant P as Player
participant F as Frontend
participant C as Client
participant S as Server
participant DB as Database
P->>F: Clicks button in UI
F->>C: Sends UI event
C->>S: Forwards action to server
S->>DB: Reads or updates data
DB-->>S: Returns result
S-->>C: Sends updated state
C-->>F: Pushes new data
F-->>P: Updates UI
This is the general pattern for UI actions that require validation or persistence.
The client exists between the server and the frontend because the frontend is not the game engine.
The frontend can render pages and collect user input, but it cannot directly perform GTA-native gameplay behavior the same way the client can.
That is why the client layer is so important:
- it receives gameplay events from the server
- it opens and updates CEF
- it translates UI actions back into game events
This bridge role is one of the core architectural ideas of the framework.
The shared layer is one of the most important parts of the architecture.
Without it, projects usually end up with:
- duplicated event names
- mismatched payloads
- inconsistent enums
- hardcoded strings spread everywhere
By keeping contracts inside source/shared, both client and server can rely on the same definitions.
That makes the framework:
- easier to extend
- easier to refactor
- less error-prone
Many RAGE:MP projects become hard to maintain because:
- logic is mixed together
- UI and gameplay responsibilities overlap
- events are scattered
- feature code leaks into unrelated systems
This project avoids that by separating:
- game authority
- local game execution
- frontend rendering
- shared contracts
That is one of the strongest parts of the framework’s design.
When working on the project, think like this:
It probably belongs to the server.
It probably belongs to the client.
It probably belongs to the frontend.
It probably belongs to shared.
This mental model helps keep features clean.
When adding a new system, try to keep its logic split by responsibility:
- validation
- persistence
- authority
- business logic
- event handling
- visual local actions
- CEF bridge calls
- page components
- UI state
- buttons and interface logic
- event names
- DTOs
- enums
- interfaces
This is how features stay modular instead of becoming tightly coupled.
A system like inventory usually touches all layers:
- loads items
- validates usage
- updates persistent inventory data
- receives inventory updates
- opens inventory UI
- forwards interaction requests
- displays slots, icons, quantities, actions
- item data shape
- inventory-related types
- event contracts
This is a good example of why the architecture is split the way it is.
This architecture gives several practical advantages:
- easier onboarding
- less duplication
- clearer feature ownership
- safer refactors
- better long-term maintainability
- easier UI expansion
- easier addition of new systems
The framework is built around four main parts:
- Server for authority and persistence
- Client for game-side execution and bridging
- Frontend for CEF UI
- Shared for common contracts and reusable definitions
The key rule is simple:
The server owns the truth, the client connects the game to the UI, the frontend renders the interface, and shared keeps communication consistent.
Once this is understood, the rest of the framework becomes much easier to work with.