Skip to main content
Complete queues, matchmaking, maps, loadouts, hooks, theme, and API reference.
fnx-pvp-matches provides ranked queues, custom team matches, rounds, Elo, inventories, stats, and leaderboard integration.

First installation

ensure oxmysql
ensure community_bridge

ensure fnx-pvp-panel
ensure fnx-pvp-matches
The resource creates its required database tables automatically. You do not need to import an SQL file.

First setup

Configure client behavior, server rules, and shared/public/_main.lua. Every enabled map needs unique identification, valid team spawns, and correct inventory item names.

Client configuration

Runtime

KeyDefaultExplanation
Runtime.closePanelOnJointrueCloses the panel after joining.
Runtime.deathEventstrueEnables built-in death events. Disable if one custom detector replaces them.
Runtime.countdown.enabledtrueEnables the visual countdown.
Runtime.countdown.soundtrueEnables countdown sound.
Runtime.countdown.volume0.5Sound volume from 0 to 1.
Runtime.countdown.holdMs2200Milliseconds the final countdown state remains visible.
Runtime.invincible.enabledtrueMaster transition-invincibility switch.
Runtime.invincible.beforeRoundtrueProtects during prestart.
Runtime.invincible.afterRoundtrueProtects after a round.
Runtime.antiTeamKill.enabledtrueBlocks teammate damage using relationship groups.
Runtime.antiTeamKill.disableMeleetrueAlso blocks teammate melee damage.
Runtime.antiTeamKill.groupA"PVP_MATCH_A"Relationship-group name for team A.
Runtime.antiTeamKill.groupB"PVP_MATCH_B"Relationship-group name for team B.
Runtime.returnCoordsnilOptional client return vec4.
ClientCfg.Death{}Overrides panel death defaults; empty inherits them.

Client hooks

KeyArgumentsCalled when
Events.OnMatchStartpayloadMatch begins.
Events.OnRoundPrestartpayloadPrestart begins; default code equips ox_inventory weapon.
Events.OnRoundStartpayloadCombat begins.
Events.OnRoundEndpayloadRound ends.
Events.OnMatchEndpayloadMatch completes.
Events.OnMatchLeavepayloadLocal player leaves.
Events.OnPlayerDeadOnMatchpayload, activeMatchLocal death is accepted.
Events.OnPlayerKillOnMatchpayload, activeMatchLocal kill is accepted.
Events.OnGameEventTriggeredcallback or nilOptional raw game-event callback.

Client commands

These commands are registered client-side by the match runtime.
CommandPurpose
/leavematchLeaves the active match.
/leavepartyLeaves the current party.
+fnx_pvp_match_tabInternal key-mapped command that opens the match scoreboard.
-fnx_pvp_match_tabInternal key-mapped command that closes the match scoreboard.
Default key mapping:
Key mappingDefault keyLabel
+fnx_pvp_match_tabTABShow match scoreboard

Server configuration

Queue and warning

KeyDefaultExplanation
Queue.modes1v1 to 6v6Allowed ranked queue sizes. Format must be NvN.
Queue.defaultElo0Starting match Elo.
Queue.matchFoundNotifyTime6500Match-found notification duration in milliseconds.
Queue.warning.enabledbridge-dependentEnables inventory-change confirmation.
Queue.warning.holdSeconds1Confirmation hold time.
Queue.warning.timeoutSeconds30Confirmation expiry.

Matchmaking

KeyDefaultExplanation
Matchmaking.rankedtrueUses Elo restrictions/updates for ranked queue.
Matchmaking.requireSameMaptrueRequires compatible selected map.
Matchmaking.eloGap.initial250Initial maximum Elo difference.
Matchmaking.eloGap.expandEverySeconds30Search time between gap expansions.
Matchmaking.eloGap.expandBy100Elo added at every expansion.
Matchmaking.eloGap.max1000Final maximum Elo difference.

Custom matches

KeyDefaultExplanation
Custom.firstTo{6,9,13}Allowed target scores.
Custom.maxTeamSize6Maximum members per custom team.
Custom.updateStatsfalseSaves normal stats from custom matches.
Custom.updateElofalseChanges Elo from custom matches.
Custom.requireBalancedTeamsfalseRequires equal team sizes when true.

Match runtime

KeyDefaultExplanation
Match.firstTo6Default rounds/points required to win.
Match.roundTime300Round limit in seconds.
Match.countdown5Pre-round countdown in seconds.
Match.prestartDelay2500Server prestart delay in milliseconds.
Match.roundEndDelay5500Delay after a round in milliseconds.
Match.loadTimeout15000Player/map load timeout in milliseconds.
Match.bucketBase40000First match routing bucket.
Match.returnBucket0Bucket restored after leaving.
Match.returnCoordsnilOptional server return vec4.

Elo and inventory

KeyDefaultExplanation
Elo.kFactor32Elo sensitivity. Higher values produce larger changes.
Elo.minDelta6Minimum applied Elo change.
Elo.maxDelta40Maximum applied Elo change.
Inventory.enabledbridge-dependentEnables snapshot/loadout management.
Inventory.keepInventoryfalseDisables match inventory/loadout management and lets players keep items.
Inventory.mode"auto"auto, bridge, or native.
Inventory.weaponMetadata.enabledtrueGives weapon ammo/components inside weapon metadata instead of as separate inventory items.
Inventory.weaponMetadata.durability10000Durability value written to weapon metadata when metadata mode is enabled.
Inventory.weaponMetadata.ammotrueStores configured ammo in weapon metadata and skips the separate ammo item grant.
Inventory.weaponMetadata.componentstrueStores configured components in weapon metadata and skips separate component item grants.
Inventory.ammoRefill.enabledtrueEnables automatic ammo refill checks.
Inventory.ammoRefill.threshold0.5Refill only when current ammo is below this fraction of configured ammo.
Inventory.ammoRefill.roundEndtrueChecks/refills ammo at round end.
Inventory.snapshotConfiguredItemsOnlyfalseWhen true, snapshots only items known to match configuration.
Inventory.restoreOnNextJointrueRestores a pending snapshot when the player next joins after interruption.

Rewards

ServerCfg.Rewards controls item/account rewards. Rewards are given independently from match loadouts, so they still work when Inventory.keepInventory is true.
KeyExplanation
Rewards.kill.enabledEnables rewards for every kill.
Rewards.kill.itemsItems/accounts given on kill.
Rewards.killStreaks.enabledEnables killstreak rewards.
Rewards.killStreaks.publicWhen false, players need command-granted access before receiving streak rewards.
Rewards.killStreaks.rewards[streak]Rewards given when the player reaches that streak value.
reward.itemInventory item name given through the configured inventory bridge.
reward.accountFramework account name such as bank; requires a framework/account bridge.
reward.amountBase reward amount. It is multiplied by the player’s reward multiplier.
reward.metadataOptional item metadata passed to the inventory.
Example:
ServerCfg.Rewards = {
    kill = {
        enabled = true,
        items = {
            { item = "bread", amount = 1 },
            { item = "money", amount = 1 },
            { account = "bank", amount = 5000 },
        }
    },
    killStreaks = {
        enabled = true,
        public = true,
        rewards = {
            [3] = {
                { item = "bread", amount = 3 },
            }
        }
    }
}

Logs

Each log category supports enabled, username, avatar, webhooks, and embed. webhooks is an array, so one category can send to multiple Discord webhooks. embed is a Lua function and can be fully customized per server.
CategorySent when
Logs.adminAdmin/internal actions such as reward permission or multiplier changes.
Logs.matchCreatedA match is created.
Logs.matchEndA match ends; includes map, mode, reason, winner, score, duration, team totals, and player stats.
Logs.matchEndByTypeOptional per-mode override for matchEnd webhooks/settings, e.g. 1v1, 2v2, or custom.
Logs.inventoryStoredA player’s inventory snapshot is stored before match loadout is applied.
Logs.inventoryRestoreA player’s original inventory and pending rewards are restored.
Example per-mode match-end routing:
ServerCfg.Logs.matchEndByType = {
    ["1v1"] = { webhooks = { "https://..." } },
    ["2v2"] = { webhooks = { "https://..." } },
    custom = { webhooks = { "https://..." } }
}

Reward commands

KeyExplanation
Commands.giveStreakRewards.activeRegisters the streak reward access command.
Commands.giveStreakRewards.nameDefault command name: givematchstreakrewards.
Commands.giveStreakRewards.permissionPermission required to use the command.
Commands.setRewardMultiplier.activeRegisters the reward multiplier command.
Commands.setRewardMultiplier.nameDefault command name: setmatchrewardmultiplier.
Commands.setRewardMultiplier.permissionPermission required to use the command.
Use giveStreakRewards only when Rewards.killStreaks.public is false. setRewardMultiplier changes the multiplier used by kill and streak rewards.

Server hooks

KeyArguments
Events.OnMatchStartmatch
Events.OnRoundPrestartmatch
Events.OnRoundStartmatch
Events.OnRoundEndmatch, winnerTeam, reason
Events.OnMatchEndmatch, winnerTeam, reason
Events.OnPlayerDeadOnMatchmatch, deadPlayer, killerPlayer, payload
Events.OnPlayerKillOnMatchmatch, killerPlayer, deadPlayer, payload
Events.OnPlayerLeaveMatchmatch, player, dropped

Shared files

shared/public/_main.lua obtains REVIVE_EVENT from the panel and defines Maps.

Map schema

KeyExplanation
nameUnique map ID. Duplicate names make selection ambiguous.
resourceOne or more map resource names used as availability requirements.
labelHuman-readable map name.
imageLocal filename or supported URL shown in the UI.
coordsGeneral vec3 map position.
swapEnables team-side swapping between rounds.
firstToMap-specific target score.
roundTimeMap-specific round limit in seconds.
weaponsLoadouts offered on this map.
extraItemsExtra inventory items given with the match loadout. Ignored when Inventory.keepInventory is true or inventory management is disabled.
winningTeamRewardsItems/accounts given to winners at match end. These are real rewards and are given even when Inventory.keepInventory is true.
teamsAvailable team spawn definitions.
Example:
extraItems = {
    { item = "bread", amount = 2 },
},

winningTeamRewards = {
    { item = "money", amount = 100 },
    { account = "bank", amount = 5000 },
}

Shared weapon and team keys

KeyExplanation
weapons[].idUnique weapon selection ID.
weapons[].hashGTA weapon hash.
weapons[].labelDisplay label.
weapons[].ammoAmmo issued.
weapons[].componentsGTA component hashes.
weapons[].itemsData.weaponInventory weapon item.
weapons[].itemsData.ammoInventory ammo item.
weapons[].itemsData.componentsInventory component items.
teams[].nameUnique team display/name.
teams[].spawnTeam vec4 spawn and heading.

Theme system

KeyExplanation
colors.panelBgMain panel background.
colors.panelBgStrongDarker background.
colors.accentPrimary highlight.
colors.accentStrongStrong active highlight.
colors.textPrimary text.
colors.mutedTextSecondary text.
colors.surfaceNormal card surface.
colors.surfaceStrongStrong card surface.
colors.borderBorders and separators.
colors.overlayModal overlay.
colors.successSuccess foreground.
colors.successBgSuccess background.
colors.dangerError foreground.
colors.dangerBgError background.
colors.warningWarning foreground.
colors.warningBgWarning background.
colors.infoInformation foreground.
colors.infoBgInformation background.
colors.ratingEliteElite rating tier.
colors.ratingMasterMaster rating tier.
colors.ratingDiamondDiamond rating tier.
colors.ratingPlatinumPlatinum rating tier.
colors.ratingGoldGold rating tier.
colors.ratingSilverSilver rating tier.
colors.ratingBaseBase/unranked tier.
radii.panelMain panel corner radius in pixels.
radii.cardCard corner radius in pixels.
radii.controlInput/control corner radius in pixels.
radii.pillBadge radius; 999 creates a pill.
effectsReserved effects table, currently empty.
fonts.bodyNormal-text CSS font stack.
fonts.displayHeading CSS font stack.

Developer info

Client exports

ExportParametersReturns / purpose
IsInMatchnoneReturns whether the local player is in an active match.
ReportDeathpayloadReports a local match death.
CanReportDeathnoneReturns whether a new death report is currently accepted.

Server exports

ExportParametersReturns / purpose
GetLeaderboardPagerequestReturns the requested match leaderboard page.
GetStatsidentifierReturns stored match statistics for an identifier.
GetPanelStatssource, identifierReturns match statistics formatted for the PVP panel.
GetPanelRanksource, identifierReturns panel rank data for the identifier.
GetLeaderboardEntrieslimit, sortMetricReturns leaderboard entries with optional limit and sorting.
SyncPlayersourceReloads/synchronizes one player’s match data.
GetActiveMatchsourceReturns the active match associated with the player.
IsInMatchsourceReturns whether the server player is in a match.

Common issues

No opponent is found

Check mode, selected map, same-map requirement, and Elo gap expansion.

Wrong spawn or map

Ensure every map name is unique and every team spawn is valid.

Inventory is not restored

Check inventory mode, item names, snapshot scope, and restoreOnNextJoin.

Some configured weapons are not given

Each weapon is applied with its ammunition and components as one package. If a complete package does not fit, only that weapon is skipped. Other weapons are still given and the player receives one notification showing how many weapons were skipped.

Deaths count twice

Use one detector and call CanReportDeath() before a custom report.

Common folders

PathWhat it is for
fxmanifest.luaFiveM manifest. It loads the resource files in the correct order.
settings/public/client/cfg.luaClient settings that server owners are expected to edit.
settings/public/server/cfg.luaServer settings that server owners are expected to edit.
settings/private/**/_cfg.luaInternal/default settings. Do not edit unless you are modifying the resource itself.
client/public/*.luaPublic client API files and safe integration files.
client/private/*.luaInternal client runtime code.
server/public/*.luaPublic server API files and exports.
server/private/*.luaInternal server runtime code.
shared/public/*.luaShared public data or integration constants.
shared/private/*.luaShared internal helpers loaded by the resource.
locales/*.jsonLocales for ui and game.
web/dist/Built NUI files used in-game. Do not edit these by hand.

Weapon is holstered/folded instead of equipped during PvP flow

Open:
fnx-pvp-matches/settings/public/server/cfg.lua

Find:
ServerCfg.Events.OnRoundPrestart
Write inside:
if GetResourceState("ox_inventory") == "started" then
    if exports.ox_inventory:getCurrentWeapon() then return end
    local inventory = exports.ox_inventory:GetPlayerItems()
    if not inventory then return end
    Citizen.CreateThreadNow(function()
        for i=1, #inventory, 1 do
            local item = inventory[i]
            if (item.name or ""):match("^WEAPON_") then
                exports.ox_inventory:useSlot(item.slot)
                break
            end
        end
        Wait(1500)
        MakePedReload(PlayerPedId())
    end)
end

File structure

fnx-pvp-matches/
|-- client/
|   |-- private/
|   |   \-- _main.lua
|   \-- public/
|       \-- main.lua
|-- locales/
|   \-- *.json
|-- server/
|   |-- private/
|   |   |-- _database.lua
|   |   |-- _events.lua
|   |   |-- _inventory.lua
|   |   |-- _main.lua
|   |   |-- _maps.lua
|   |   |-- _party.lua
|   |   |-- _profiles.lua
|   |   |-- _runtime.lua
|   |   |-- _state.lua
|   |   \-- _stats.lua
|   \-- public/
|       \-- main.lua
|-- settings/
|   |-- private/
|   |   |-- client/
|   |   |   \-- _cfg.lua
|   |   \-- server/
|   |       \-- _cfg.lua
|   \-- public/
|       |-- client/
|       |   \-- cfg.lua
|       \-- server/
|           \-- cfg.lua
|-- shared/
|   |-- private/
|   |   \-- _main.lua
|   \-- public/
|       \-- _main.lua
|-- web/
|   \-- dist/
\-- fxmanifest.lua