Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cc-scripts.com/llms.txt

Use this file to discover all available pages before exploring further.

A DUI session lets you host a cc_minigames minigame inside a DUI render target you create yourself — typically so the game UI can be projected onto a wall-mounted CRT, monitor, or other prop via AddReplaceTexture. The caller still owns the DUI lifecycle. cc_minigames only hands you a correctly-shaped start payload, a cancel payload, and a result callback channel.

When to use this

Use caseUse this
Player presses a key, a minigame opens fullscreen, blocks until done.The plain export. Not this.
Minigame UI must appear on an in-world prop’s texture.This.
The minigame is just one panel inside another resource’s NUI dashboard (iframe-like).The plain export with embed = true, not this. The embed option does the same focus-ownership handover without DUI plumbing.

API

local controller = exports.cc_minigames:CreateDuiSession({
    minigame = 'frequency',     -- any minigame name
    variant  = 'screen-4x3',    -- whichever variant your prop expects
    opts     = {
        difficulty = 'medium',
        role         = 'viewer',
        sessionToken = token,    -- required for networked Frequency
    },
    onResult = function(success)
        -- called exactly once, with a boolean
    end,
    sendMessage = function(payload)
        -- optional. If provided, server-pushed Frequency state/finish events
        -- matching opts.sessionToken are forwarded here.
        -- Bind your DUI's :sendMessage in.
        myDui:sendMessage(payload)
    end,
})

-- controller is:
-- {
--   id            = 'dui_<gametimer>_<n>',
--   startMessage  = { action='start',  data={...} },
--   cancelMessage = { action='cancel', data={ id = ... } },
--   dispose       = function()  -- removes the session from the registry
-- }

Lifecycle

1

Create your DUI surface

Standard CitizenFX DUI flow: create the DUI pointing at https://cc_minigames/web/index.html, attach its render target to the in-world prop with AddReplaceTexture.
2

Open a session

Call CreateDuiSession with the minigame, variant, opts, and onResult callback.The controller you get back exposes the start message that needs to be pushed into the DUI once it’s loaded.
3

Send the start message into the DUI

myDui:sendMessage(controller.startMessage)
The NUI page running inside your DUI sees the same start action it sees on a normal session and renders the requested minigame inline (because the controller forces opts.embed = true so the page doesn’t try to grab focus).
4

Receive the result

When the player finishes (or cancels), onResult(success) fires once. You’re responsible for tearing down the DUI on your side.
5

Cancel from your side

If you need to abort externally — door closed, contract failed, prop destroyed — push the cancel message:
myDui:sendMessage(controller.cancelMessage)
The page inside the DUI will resolve the session as false, which routes back through onResult(false). Then call controller.dispose() to drop the registration.

How result routing works

The DUI’s NUI page reaches the resource via the same RegisterNUICallback channel as the fullscreen NUI does — but the data.id it sends back is the DUI session id you got from CreateDuiSession, not the player’s active-session id. cc_minigames checks the id against its registry of DUI sessions first. If it matches, your onResult fires and the player’s regular activeSession is left alone. If it doesn’t match, the call falls through to the normal path. This is why two DUI minigames and one fullscreen minigame can theoretically coexist on a single client — they each have their own id namespace.

Frequency forwarding

For the multiplayer Frequency game, the server pushes state and finish events to specific player sources, keyed on the sessionToken. If your DUI is showing the viewer half (the most common DUI scenario — a CRT prop on the heist wall), pass sendMessage to CreateDuiSession and cc_minigames will forward those server events into your DUI as well, so the viewer’s waveform stays live without any extra wiring.
exports.cc_minigames:CreateDuiSession({
    minigame    = 'frequency',
    variant     = 'screen-4x3',
    opts        = { role = 'viewer', sessionToken = token, difficulty = 'medium' },
    sendMessage = function(payload) myDui:sendMessage(payload) end,
    onResult    = function(success)
        -- success is the same bool the server promise resolves with
    end,
})

Notes

  • The controller’s dispose() is idempotent — calling it twice is fine.
  • You can have multiple DUI sessions live at once. Each gets its own id.
  • A normal fullscreen session opened via :Pattern{}, :Crack{}, etc. is separate from DUI sessions and is unaffected by them.