RivalityFX Docsrivalityfx.com ↗

RivalityFX

RivalityFX is a modular PvP arena engine for FiveM (GTA V, Lua 5.4). It's a platform: it runs several PvP game modes at once — round-based duels, deathmatch, battle royale — each in its own isolated match, and you customize or extend it without touching the core.

This documentation has two paths:

  • Run a server — deploy on Linux, configure and operate it, and (optionally) sell VIP + cosmetic packs. Start here if you're hosting RivalityFX.
  • Create for it — build and distribute game modes, maps, ped skins, outfits, weapon wraps, and load screens against the stable, public SDK. Start here if you're a creator extending it.

The rest of this page explains what the engine is — useful background for both paths, and the foundation the SDK builds on.

What the engine is

The engine owns the generic machinery, and nothing else:

  • Scene instances — one scene is one running match, bound to one mode.
  • Routing buckets — each scene gets its own bucket, so matches never collide.
  • Arenas — the one match container: players open or join an arena of a mode, which fills and auto-launches into a scene (no ready-check). A persistent arena recycles after each match; a player-created one closes when it empties.
  • Tick loop — drives every scene (a built-in driver, or your custom tick).
  • Kill / death dispatch — killfeed, spectate, and your mode's hooks.
  • Default scoreboard + match HUD — rendered for two-team modes for free.
  • Lobby snapshot — the live list of modes and scenes the menu reads.
  • Built-in rules runtime — declarative rules blocks: elimination rounds, deathmatch, and zone control, with no gameplay code.

The engine owns no game rules. Round length, win conditions, teams, and loadouts come from your mode; maps come from the shared battleground pool (the battlegrounds a mode draws from — distinct from the Arena match container above), which a mode can also ship its own map into (see the battleground SDK).

The mod model

A mode is a separate resource — a different Lua VM from the engine — so it can be shipped, versioned, and sold on its own. A mode depends on both sdk (the contract/registry) and engine (the runtime), and registers a plain-data descriptor with the SDK (exports.sdk:registerMode), which the engine mirrors and runs:

-- [modes]/mode_xxx/server/main.lua
CreateThread(function()
    while GetResourceState('sdk') ~= 'started' do Wait(100) end
    exports.sdk:registerMode(MODE)   -- MODE is a plain Lua table
end)

Because a descriptor is plain data, the simplest mode you can ship — the 1v1 Duel — contains no functions at all: just its structure (teamCount × perTeam), win condition, loadouts, and a declarative rules block. That is the floor, not the ceiling. A mode grows along a value ladder, and you only pay for the rung you need:

  1. A rules presetzero code. The Duel is structure = 'rounds' with a best-of win condition; the built-in rules runtime composes it into a full match. Its value is design — loadouts, pacing, the arenas it draws from — not lines of Lua.
  2. A rules compositionstill zero code. rules is a device system, not a fixed menu. Add the zones device and the same machine becomes King of the Hill, Domination, or Hardpoint: a genuinely different mode, still pure data. New devices (triggers, sequencers, spawners) widen what data alone can express.
  3. An imperative escape hatchyour own code. When a mode needs logic the devices don't cover (Gun Game, Escort, Infection…), it ships a tick loop, a registered native driver, or per-event hooks (onKill, …). These are real function references that run inside the mode's VM; the SDK registry drops a mode automatically if its owning resource stops, so a dead hook is never called. See Hooks and Choosing how to drive a mode.

Plain data is a deliberate strength, not a limitation: a descriptor is validated against a versioned contract (apiVersion), serializable, and carries no arbitrary code into the engine — so a mode is safe to distribute and to load. You drop to a tick/driver only when the declarative surface genuinely runs out.

The golden rule

One scene = one mode = one routing bucket.

Two matches can therefore never share a bucket, which gives you natural isolation: two duels (or two different modes) cannot interfere with each other. This requires routingBuckets = true in the engine config — it is on by default and must stay on. See the lobby contract and the internal buckets notes.

Where to go next

Running a server — the operator / server-owner path

Creating & extending — the creator path