Skip to main content

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.Id is 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 / OnEntityDestroyed fire 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 with OnEntityStreamIn / 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

OnGuiReady fires 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

OnPlayerCreateWaypoint carries 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 wepaonHash spelling is RAGE:MP's own typo. It is kept so code written against RAGE:MP compiles here unchanged — and for the same reason OnDummyEntityDestroyed uses OnDummyEntityCreatedDelegate, 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:

JavaScriptC#
click direction"up" / "down" stringsbool up, bool right
click raycastone click eventOnClick and OnClickWithRaycast
incomingDamage(sourceEntity, sourcePlayer, …)(sourcePlayer, sourceEntity, …) — first two swapped
waypoint clearedplayerReachWaypointOnPlayerRemoveWaypoint
vehicle eventsthe local GTA handlea 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.