This document serves as the definitive deep-dive reference for RedM development. It goes beyond the standard documentation to explore the internal architecture of the CFX.re engine, RAGE engine mechanics, raw data structures, and advanced scripting patterns. It is intended for senior developers, server architects, and reverse engineers.
1. Engine Internals & Sync Architecture
1.1 OneSync and Entity Sync Trees
The CFX.re engine (which powers both FiveM and RedM) uses a custom synchronization architecture called OneSync. Instead of the base game’s peer-to-peer model, OneSync is a server-authoritative model where the server acts as the central hub for all entity data.
All entity synchronization is handled through Sync Trees. The server source code (ServerGameState_SyncTrees.cpp) defines 14 primary NetObjEntityType sync trees [1]:
| Entity Type | Sync Tree Class | Notes |
|---|---|---|
Automobile | CAutomobileSyncTree | Base for cars, wagons, coaches |
Ped | CPedSyncTree | Used for human peds, animals, and horses |
Object | CObjectSyncTree | Props, dynamic world objects |
Player | CPlayerSyncTree | Player characters |
Door | CDoorSyncTree | Networked doors and gates |
Train | CTrainSyncTree | Trains and trams |
In RedM, GetEntityType() returns 1 for Peds, Players, Animals, and Horses; 2 for Vehicles (Wagons, Boats, Trains); and 3 for Objects [2].
How Server Natives Work:
When you call a server-side native like GetEntityCoords(), the server does not ask the client for the position. Instead, it reads directly from the sync tree node (e.g., CSectorPositionDataNode), which maintains the last known state with 12-bit precision [1].
1.2 Entity Ownership and Routing Buckets
Every networked entity has an “owner” (a specific client responsible for running physics and AI logic). When the owner moves out of the culling radius (hardcoded to 424 units), the server automatically migrates ownership to another nearby client [1].
Routing Buckets (Virtual Worlds):
Routing buckets allow you to isolate players and entities into separate dimensions.
- Each bucket maintains its own population grid and entity pool.
- Control requests cannot cross bucket boundaries.
- Networked entities persist across bucket switches, while purely client-side entities do not [1].
1.3 Entity Lockdown and Security
To prevent malicious entity creation (a common exploit vector), RedM servers should utilize Entity Lockdown.
| Mode | Behavior |
|---|---|
strict | Prevents ALL client entity creation. Only server-created entities are allowed. |
relaxed | Blocks script-owned entities from clients, but allows ambient population. |
inactive | Clients can create any entity (Highly insecure). |
Combine strict mode with the sv_filterRequestControl convar (set to 2 or higher) to block unauthorized control requests over settled entities, effectively neutralizing most mod menu griefing [3].
2. MetaPed and Asset Internals
RedM’s character and horse customization relies on the RAGE engine’s MetaPed system, driven by raw .ymt metadata files [4].
2.1 The Asset Pipeline
A complete MetaPed asset consists of multiple layers linked by matchTags:
- Drawable (
assets_drawable.ymt): The 3D geometry (.ydr). - Albedo (
assets_albedo.ymt): The base color texture. - Normal (
assets_normal.ymt): The bump map. - Material (
assets_material.ymt): Roughness and metalness properties.
2.2 The Tint System
Many assets (especially horse coats and clothing) utilize the useTintPalette flag. Instead of unique textures for every color, the engine uses palettes (e.g., metaped_tint_horse from graphics.ytd).
- tint0 (Green Channel): Primary color (uses the bulk of the palette width).
- tint1 (Red Channel): Secondary color.
- tint2 (Blue Channel): Tertiary color.
Values range from0-254, with255used to disable the channel [4].
2.3 Undocumented Texture Overrides
There is a powerful, undocumented native API for overriding ped textures dynamically without streaming new .ytd files [5].
// Creates a texture override data for ped and returns its index
int _REQUEST_TEXTURE_OVERRIDE(Hash albedoHash, Hash normalHash, Hash unkHash); // 0xC5E7204F322E49EB
// Applies the texture on a ped's component
void _APPLY_TEXTURE_OVERRIDE_ON_PED(Ped ped, Hash componentHash, int textureId); // 0x0B46E25761519058Note: The engine has a hard limit of 32 concurrent texture overrides across all peds.
3. Advanced Networking and State Bags
3.1 Network Event Throttling
While TriggerServerEvent is standard, it blocks the entire network channel. For payloads larger than a few kilobytes, you must use Latent Events [6].
-- TriggerLatentServerEvent(eventName, bps, ...)
TriggerLatentServerEvent("myEvent", 25000, largePayload)bpsdefines bytes per second (default is 25,000).- Warning: Setting
bpstoo high (>10,000,000) will cause severe connectivity issues. When targeting-1(all players) with a latent client event, the effective bandwidth isbps * player_count[6].
3.2 State Bag Internals
State bags are the modern replacement for most network events, but they have hidden performance characteristics [7]:
- Shallow Limitations: State getters and setters are naive. Every
getdeserializes the full state from the game, and everysetserializes the entire state back. - Do not do this:
Entity(x).state.x.y = 'b'(It will not replicate). - Do this:
Entity(x).state['x:y'] = 'b'(Use flat keys). - Rate Limiting: Pushing 100+ state bags with ~1MB payloads to an entity will crash the server due to network saturation (Issue #2361) [8].
4. Streaming Budgets and RAGE Limits
4.1 The grcResourceCache Saturation Issue
The RAGE engine streaming pool (grcore/ResourceCache) behaves differently than most developers expect.
- No Proactive Eviction: Once assets are loaded into VRAM, the streamer does not release them based on distance or time. Eviction only occurs under demand pressure when the budget is 100% full and a new asset needs to load [9].
- Base game assets alone (especially Casino/DLC interiors) can consume the entire budget, leading to synchronous evict-load stalls (micro-stutters) during area transitions.
4.2 Entity Pool Sizes
The engine has hard limits on concurrent entities. If you see <<unknown pool>> Pool Full errors, you must increase the pool sizes in your server.cfg [10]:
set sv_poolSizesIncrease '{"VehicleStruct": 100, "Peds": 200, "FragmentStore": 1000}'Note: The FragmentStore pool (used for physics objects and glass) has a hardcoded maximum increase limit of ~4,836 [11].
5. Decompiled Scripts and Game Logic
Thanks to reverse engineering efforts (e.g., Halen84/RDR3-Decompiled-Scripts), the raw .ysc game scripts have been decompiled into readable C code [12].
5.1 Script Globals
RDR3 relies heavily on Script Globals to control game state. These are tunable parameters that can be manipulated without calling natives.
- Example: Trinket stats are hardcoded floats in the scripts (e.g., Owl Feather =
0.15fcore drain reduction, Eagle Talon =0.5fEagle Eye duration) [13]. - Globals control economy, difficulty, spawn rates, and weather transitions.
5.2 Native Hashes and Strings
The RAGE engine uses joaat() (Jenkins One-at-a-Time) hashing for almost all string comparisons. When looking for specific components, weapons, or peds, you must use the hashed value of the internal string. Repositories like t3chman/rdr3_strings and Halen84/RDR3-Native-Flags-And-Enums are essential for mapping these hashes back to readable names [14] [15].
6. Advanced Resource Manifest (fxmanifest.lua)
Modern RedM resources should utilize the cerulean (or newer bodacious) fx_version.
6.1 Dependency Constraints
You can enforce strict server environments directly in the manifest [16]:
dependencies {
'/server:4500', -- Requires at least server build 4500
'/policy:subdir_file_mapping', -- Requires specific server key policy
'/onesync', -- Requires OneSync to be enabled
'/gameBuild:h4', -- Requires specific game build
'/native:0xE27C97A0', -- Requires specific native support
}6.2 Experimental OAL
use_experimental_fxv2_oal 'yes'Enables One Argument List (OAL) for Lua, correcting return types and improving native call performance. However, it breaks vector unpacking, meaning SetEntityCoords(ped, coords) will fail; you must explicitly pass coords.x, coords.y, coords.z [16].
7. The Native Prompt System
RedM’s interaction system relies on native UI prompts. Creating a prompt requires specific hash invocations, as the wrapper functions are often incomplete [17].
-- RDR3 requires VarString for prompt text
local str = Citizen.InvokeNative(0xFA925AC00EB830B9, 10, 'LITERAL_STRING', "Press Me")
-- Create the prompt (0x04F97DE45A519419 = PromptCreate)
local prompt = Citizen.InvokeNative(0x04F97DE45A519419)
-- Configure prompt
Citizen.InvokeNative(0xB5352B7494A08258, prompt, controlHash) -- SetControlAction
Citizen.InvokeNative(0x5DD02A8318420DD7, prompt, str) -- SetText
Citizen.InvokeNative(0x94073D5CA3F16B7B, prompt, 1) -- SetHoldMode
Citizen.InvokeNative(0xF7AA2696A22AD8B9, prompt) -- RegisterEnd