Skip to content
GAMPEONCreate
New project
Preview: this works today, but details can still change.

Web interfaces (NUI)

Resources can draw menus, HUDs and phones as web pages over the game. GAMPEON uses the same API as FiveM NUI, so most NUI resources work without changes.

fxmanifest.lua
fx_version 'cerulean'
ui_page 'html/index.html'
client_script 'client.lua'
files { 'html/app.js', 'html/style.css', 'html/img/*.png' }
client.lua
RegisterCommand('menu', function()
SendNUIMessage({ type = 'open', items = { 'bread', 'water' } })
SetNuiFocus(true, true) -- keyboard and mouse go to the page
end)
RegisterNUICallback('close', function(data, cb)
SetNuiFocus(false, false)
cb('ok')
end)
html/app.js
window.addEventListener('message', (event) => {
if (event.data.type === 'open') showMenu(event.data.items);
});
function close() {
fetch(`https://${GetParentResourceName()}/close`, {
method: 'POST',
body: JSON.stringify({ reason: 'button' }),
});
}
  • Every resource with ui_page gets its own frame, transparent over the game. Pages of different resources do not see each other.
  • ui_page is sent to players with the other client files. List everything the page loads (scripts, styles, images, fonts) under files.
  • Pages load from https://cfx-nui-<resource>/<path>, the same address as in FiveM. Only ui_page and files are served; client scripts and other files are not.
  • SendNUIMessage(data) arrives as event.data of a message event. The data travels as JSON, at most 1 MB per message.
  • fetch('https://<resource>/<name>') from the page calls the handler from RegisterNUICallback(name, ...). The body is decoded from JSON and the value passed to cb comes back as JSON. XMLHttpRequest works the same way, so jQuery $.post and axios work too.
  • Messages sent before the page has loaded are delivered once it has loaded (up to 256).
  • A page may also call callbacks and load files of other resources, as in FiveM, for example item images from https://cfx-nui-<inventory>/….
  • Resource names in these addresses are not case-sensitive, because browsers write host names in lower case. Callback addresses only work for resource names without a dot.
  • console.log and errors of pages appear in the game’s log as [client/nui].

Graphics from store packs, such as UI sprites you bought for your own game, do not belong in a resource: every file under files is sent to every player as a plain file. Put them into your game package instead, in a folder Nui next to Content:

GamCity/
GamCity.uplugin
Content/
Nui/
menus/panel_wide.png
icons/coin.png

Pages load them from https://gampeon-pkg-<package>/<path>, with the package name in lower case:

html/index.html
<img src="https://gampeon-pkg-gamcity/icons/coin.png" alt="">
<div style="background-image: url('https://gampeon-pkg-gamcity/menus/panel_wide.png')"></div>
  • Only images (png, jpg, webp, gif, avif), fonts (woff, woff2, ttf, otf), sounds (mp3, ogg, wav) and videos (mp4, webm) are served, at most 32 MB per file. Pages, scripts and styles stay in the resource.
  • Only packages the game has mounted are served. In the first stage that is every package given with -gppackage=; later only the packages of the project the player joined.
  • The game log shows Web interfaces (NUI): files of game package GamCity at https://gampeon-pkg-gamcity/ when it works.
  • Free fonts under an open license (for example from Google Fonts) can stay in the resource.
Situation The page gets
The handler calls cb(value) Status 200 with value as JSON
The body is not JSON 400
No handler with that name 404
The body is larger than 64 KB 413
More than 256 requests are open 429
The handler fails with an error 500 with { "error": "…" }
The resource stops before the answer 503
No answer within 30 seconds 504

Handlers may call Wait before they answer.

  • SetNuiFocus(hasFocus, hasCursor) gives the keyboard and the mouse to the pages. The last call of any resource wins.

  • SetNuiFocusKeepInput(true) lets the player keep moving while a page has focus.

  • When the resource that set the focus stops, the focus goes back to the game.

  • Players can always take the focus back by holding Escape for 2 seconds, so a page cannot trap the mouse. A short press of Escape still reaches the page, so menus can close on Escape as usual.

  • The page stays visible after that. Your client script gets the event gampeon:nuiFocusReleased and can close its menu:

    AddEventHandler('gampeon:nuiFocusReleased', function(resourceName)
    if resourceName == GetCurrentResourceName() then
    SendNUIMessage({ type = 'hide' })
    end
    end)

With gampeon-server, join a fake player with gp_fakeplayer client <name> and then:

Command Effect
gp_fakeplayer nui <id> <resource> <callback> [json] Calls the callback like fetch from the page and prints the answer
gp_fakeplayer nuifile <id> <resource> <path> Loads a file like the browser does, or prints 404
gp_fakeplayer nuirelease <id> Takes the focus back like holding Escape in the game

Messages, focus changes and answers appear as [client <id>/nui] in the output.

  • Same manifest entries, same functions, same https://<resource>/<name> callbacks and GetParentResourceName().
  • The low-level form RegisterNuiCallbackType with AddEventHandler('__cfx_nui:name', ...) is not supported. Use RegisterNUICallback.
  • ui_page must be a file of the resource. Remote pages (ui_page 'https://…') are not shown.
  • Pages run in a sandbox: no pop-ups, no alert() or confirm(), and they cannot navigate the game’s browser away. Images, fonts and scripts from https:// addresses still load, as in FiveM.
  • Pages on surfaces in the world (DUI) and loading screens (loadscreen) come later.

Report a problem with this pageOther ways to give feedback