Client-side C# Events
Client-side scripting can be written in C#, against the same RAGE.* API RAGE:MP ships. Put .cs
files in your server's client_packages/cs_packages/ folder and the client compiles them on
connect — you do not build a DLL, and you cannot reference one. That is RAGE:MP's model, and it is
ours.
using RAGE;
using RAGE.Elements;
public class ClientEvents : Events.Script
{
public ClientEvents()
{
Events.OnPlayerReady += () => Chat.Output("Player ready in world.");
Events.OnPlayerDeath += (player, reason, killer, cancel) =>
Chat.Output($"died, reason {reason}");
// Returning a cancel stops the action, exactly as in RAGE:MP.
Events.OnPlayerChat += (text, cancel) =>
{
if (text.StartsWith("!")) cancel.Cancel = true;
};
}
}
Every class deriving from Events.Script is instantiated once at load — that is what makes a
script "run", so registration goes in the constructor.
JavaScript and C# can be used together. Both hosts receive every event, and if either cancels, the event is cancelled.
The events
All 43 delegates from RAGE:MP's client assembly are present with matching signatures. Two do not
fire yet and five fire with incomplete arguments — both called out inline below, and listed with a
planned fix in the client repo's docs/API_GAPS.md.
Browser
OnBrowserCreated · OnBrowserDomReady · OnBrowserLoadingFailed ·
OnScriptWindowCreatedC# · OnScriptWindowDestroyedC#
The three browser events fire, but
HtmlWindow.Idis always 0 — the client runs a single CEF page and does not yet route by id. The two script-window events do not fire at all: there is no script-window concept distinct from a browser.
Checkpoints and colshapes
OnPlayerEnterCheckpoint · OnPlayerExitCheckpoint · OnPlayerEnterColshape · OnPlayerExitColshape
Checkpoints and colshapes also carry their own OnEnter / OnExit delegates, fired alongside the
global ones.
Common
OnClick · OnClickWithRaycastC# · OnPlayerChat · OnPlayerCommand · OnPlayerJoin ·
OnPlayerReady · OnPlayerQuit · OnPlayerSpawn · OnPlayerDeath · OnPlayerResurrect ·
OnPlayerWeaponShot · OnEventTriggeredByKeyC# · OnConsoleCommand ·
OnIncomingDamage · OnOutgoingDamage
Entity
OnEntityStreamIn · OnEntityStreamOut · OnEntityCreated · OnEntityDestroyed ·
OnEntityDataChangeByKeyC# · OnEntityControllerChange · OnDummyEntityCreated ·
OnDummyEntityDestroyed
OnEntityCreated/OnEntityDestroyedfire from the streamer's create and destroy transitions. On a streaming client "the entity exists" and "the entity is in range" are the same moment, so they arrive paired withOnEntityStreamIn/OnEntityStreamOut(created first, destroyed last). The dummy-entity pair covers locally created dummies only.
OnEntityDataChangeByKey and per-key handlers registered with Events.AddDataHandler both fire on
a synced-data change. oldArg is always null: the client's data packet carries only the new value.
Graphics
TickC# — every frame, with the nametag list · OnGuiReady
OnGuiReadyfires when the CEF page's DOM is ready — the client owns a single page, so that is the moment the GUI becomes usable.
Vehicles
OnPlayerStartEnterVehicle · OnPlayerEnterVehicle · OnPlayerLeaveVehicle
Voice chat
OnPlayerStartTalking · OnPlayerStopTalking
Waypoint
OnPlayerCreateWaypoint · OnPlayerRemoveWaypoint
OnPlayerCreateWaypointcarries the real waypoint position, sampled from the waypoint blip.
Two the RAGE:MP wiki does not list
OnExplosion · OnProjectile
Both are real — they exist in RAGE:MP's shipping client assembly, and the wiki's events page (last edited in 2018) simply never listed them. We took the signatures from the assembly.
Events.OnExplosion += (source, explosionType, position, cancel) => { };
Events.OnProjectile += (source, wepaonHash, ammoType, position, velocity, cancel) => { };
The
wepaonHashspelling is RAGE:MP's own typo. It is kept so code written against RAGE:MP compiles here unchanged — and for the same reasonOnDummyEntityDestroyedusesOnDummyEntityCreatedDelegate, as it does there.
Custom and remote events
Events.Add("myEvent", args => Chat.Output($"got {args[0]}"));
Events.CallRemote("saveMe", 1, "two");
Events.CallLocal("myEvent", "hello"); // reaches JS handlers too
CallLocal goes through the client's one event dispatcher, so a C# script and a JS script can
talk to each other through it.
Differences from the JavaScript events
The C# API is not the JS API with capital letters — RAGE:MP itself differs between the two, and we match its C# shapes:
| JavaScript | C# | |
|---|---|---|
| click direction | "up" / "down" strings | bool up, bool right |
| click raycast | one click event | OnClick and OnClickWithRaycast |
| incomingDamage | (sourceEntity, sourcePlayer, …) | (sourcePlayer, sourceEntity, …) — first two swapped |
| waypoint cleared | playerReachWaypoint | OnPlayerRemoveWaypoint |
| vehicle events | the local GTA handle | a Vehicle element |
Requirements
The client needs a .NET 8+ runtime and RAGE.dll beside altv-client.dll. Without either, client
C# stays off and everything else runs unchanged — a JavaScript gamemode is unaffected.
Current coverage, including what is not implemented yet, is tracked in
docs/CLIENT_CSHARP_STATUS.md in the client repo.