Skip to main content

Running a full RAGE:MP C# gamemode

SYNC MP runs unmodified RAGE:MP C# gamemodes — the same GTANetworkAPI (NAPI.*, : Script, [Command], [ServerEvent], [RemoteEvent], [RemoteProc]) surface, loaded by the same loader flow. A large production gamemode (hundreds of scripts, MySQL + Redis, thousands of events) boots on SYNC MP with no source changes.

How it works​

Your server's C# backend (Bootstrapper.dll, next to the server exe under dotnet/runtime/) is a faithful re-implementation of RAGE:MP's GTANetworkAPI bridge. The public API is identical; only the native layer (Rage.*) is re-pointed from RAGE:MP's native server onto the SYNC MP engine.

When a C# resource loads, the loader:

  1. Loads <resource>/<meta.xml → script src> (e.g. bin/Debug/netcoreapp3.1/YourGamemode.dll). Any Bootstrapper reference in the resource resolves to the server's own Bootstrapper.dll.
  2. Detects your custom entity classes — if you declare class ExtPlayer : Player, class ExtColShape : ColShape, etc., every entity SYNC MP creates is instantiated as your subclass, so (ExtColShape) NAPI.ColShape.CreateCylinderColShape(...) casts succeed (RAGE:MP 1:1).
  3. Instantiates every : Script class and registers its [Command] / [ServerEvent] / [RemoteEvent] / [RemoteProc] members — same counts and order as RAGE:MP.
  4. Fires Event.ResourceStart exactly once per resource.

A main-thread task pump drives NAPI.Task.Run / NAPI.Task.WaitForMainThread every server tick, exactly like RAGE:MP's game loop — so scheduled/delayed work runs on the main thread.

Running your server​

SYNC MP is a drop-in for ragemp-server.exe. Point the server's working directory at your RAGE:MP server root so the gamemode's relative file reads (settings/, json/, dotnet/resources/) and AppDomain.BaseDirectory resolve the same way:

:: from your RAGE:MP server root
D:\path\to\sync-mp\altv-server.exe

conf.json is read in RAGE:MP format (bind, port, maxplayers, gamemode, "csharp": "enabled"). With C# enabled, every folder under dotnet/resources/ is auto-discovered and loaded — no resource list needed.

A successful boot mirrors RAGE:MP line-for-line, e.g.:

loaded 350 commands(s)
loaded 123 server events(s)
loaded 620 remote event(s)
Accounts loaded. Characters loaded. Fractions loaded. Organizations loaded.
Loaded 1627 houses. Loaded 1627 garages. Vehicles are loaded (19)
Server is ready to accept connections

Native bridge coverage & the safety net​

The Rage.* native surface is wired to the SYNC MP engine incrementally. Any native not yet wired is auto-defaulted to a no-op that returns default, instead of throwing — so an unimplemented function degrades to "does nothing" rather than aborting the handler that called it. On boot the server logs how many delegates were defaulted, e.g.:

[csharp] Rage native bridge: 144 unbound delegate(s) defaulted to no-op (safety net)

Fully wired today: chat, events/RPC (server↔client), player lifecycle/health/armour/weapons, entity position/velocity/model/dimension/heading, vehicles (engine/lock/health/color/mod/neon/…), world time/weather, colshapes, blips, markers, text-label creation, IPLs, run-native-on-client, entity shared data / server data (SetSharedData/GetSharedData/Has/Reset — server-side, per-entity, all types), and world data. Client propagation of entity shared data and some per-entity property setters are still being wired; until then those calls are safe no-ops.

The resource-load console output is RAGE:MP-exact — -> Starting <res> resource.., loading scripts.. found N script(s), instantiating <Type>.. per class, loaded N commands/server events/remote events, loading exported functions.. found N, -> Resource <res> started!, Started! Waiting for connections.. — so a boot looks the same as it did under ragemp-server.exe.

Known limitations (in progress)​

  • Some entity property setters (e.g. TextLabel.Text, entity shared-data broadcast) are safe no-ops until wired end-to-end to client streaming.
  • A gamemode that relies on an unwired native won't crash, but that specific effect won't appear yet. Check the boot log for the residual exceptions — anything gamemode-side (bad DB row, data cast) reproduces identically on RAGE:MP.