Skip to content

Events, commands and timers ​

A resource reacts to the server in three ways: event handlers for what happens in the game, command handlers for what players and the operator type, and timers for what is due later. All three run on the server's main loop, one callback at a time (Threading and timing), and are revoked when the resource stops.

Events ​

Subscribing ​

lua
dlmp.add_event_handler(event, element, fn, opts) -- true | nil, err
dlmp.remove_event_handler(event, element, fn)    -- true | nil, err
ArgumentContract
eventA built-in event name. The names and the arguments of each event are listed at dlmp.add_event_handler in dlmp.lua. An unknown name returns nil, err. Resources cannot declare events of their own; they talk to each other through exports.
elementAn Element: dlmp.root, dlmp.resource, dlmp.resource_root, or a dlmp.source kept from an earlier event. Any other value, a Player or Vehicle handle included, raises an error.
fnThe handler. The same event, element and fn registered twice by one resource returns nil, err, and so does removing a handler that is not registered.
opts.propagatetrue by default: the handler also fires for events whose source lies below element in the element tree. false: only for events sourced at element itself.
opts.priority"high", "normal" (the default) or "low". Any other string raises an error.

Handlers can be registered at load time (the top level of a script or module) and from any callback.

Event sources ​

Every event has a source element; the source decides which handlers hear it.

SourceEvents
the starting or stopping resource's resource_rootresource_start, resource_stop
the rootplayer_connecting (no player exists yet), player_auth (only the auth resource hears it)
the playerevery other player_* event
the pickuppickup_interact
the vehiclevehicle_* events; trailer_attach, trailer_detach at the trailer; winch_attach, winch_detach at the lifted vehicle
the checkpointcheckpoint_enter, checkpoint_leave
the colshapecolshape_enter, colshape_leave
the objectobject_move_end
the edited element, or the editing resource's resource_rootentity_select, entity_edit, edit_end of an edit session

Players hang under the root, created elements under their resource's dlmp.resource_root. So dlmp.root hears everything, dlmp.resource_root hears the events of the resource's own elements and its own resource_start and resource_stop, and player events are heard only on dlmp.root.

Matching and order ​

  • A handler matches when its element is the source, or, with propagate, an ancestor of the source. Handlers of every resource match alike: a dlmp.root handler hears the resource_start of every resource.
  • Matched handlers run by priority, then in the order they were registered, across all resources. Within one resource the order of registration is the order of the script and module code that registers them.
  • The set of handlers is fixed before the first one runs: a handler added or removed in a callback takes effect from the next event.
  • Every matched handler runs. A handler cannot stop the others, and the return value matters only for refusable events.

The callback context ​

While a handler runs, dlmp.event_name is the event's name, dlmp.source the source Element and dlmp.this_resource the resource whose handler it is. They are nil outside callbacks. Command, timer and other callbacks set dlmp.this_resource only.

Arguments ​

  • Each event passes its own arguments; the list is at dlmp.add_event_handler in dlmp.lua. Lua drops the arguments a function does not declare, so a handler takes only the ones it needs.
  • Elements arrive as fresh handles for every handler, and tables (such as the info of pickup_interact) are fresh for every handler too.
  • A handle reads the server when it is called, not when the event was raised: a handler sees the state the events and handlers before it left. An optional player, such as a killer who already left, is nil.
  • resource_start and resource_stop pass the source element only.

Delivery order ​

Resources get the server's events as one stream, in the order the server committed them, whatever their kind (ADR 0048):

  • Events reach handlers one at a time; each handler sees the state the events before it left.
  • What a handler causes (a kick, a respawn, a seat change) raises its events after the current one, in the same server step.
  • One delivery pass takes at most 8192 events. The rest follow in the next pass, in the same order, and the server logs a warning; a chain of handlers that keep raising each other's events therefore cannot hang the server.
  • An event about an element that is gone by the time of delivery follows the rules of Events with a removed source.

Lifecycle events ​

resource_start and resource_stop are raised on the resource's own dlmp.resource_root (Lifecycle). resource_start is the first place the resource may act on the game; an error in one of its handlers fails the start. An error in a resource_stop handler is logged and the stop goes on.

lua
dlmp.add_event_handler("resource_start", dlmp.resource_root, function()
    dlmp.set_spawn_point({ x = 2458.6, y = -1668.4, z = 13.3, heading = 0.0 })
    assert(dlmp.create_vehicle({ model = 411, x = 2470.0, y = -1660.0, z = 13.3 }))
end)

Refusable events ​

Some events ask before the server acts: a handler can refuse them by returning false (ADR 0038). They are vehicle_start_enter, vehicle_jack, vehicle_control, trailer_attach, winch_attach, player_remote_enter, player_detonate_satchels, player_stealth_kill, player_chat and player_connecting.

  • One handler returning false refuses the event; nil, true and any other value let it through. All handlers run in any case.
  • They fail closed: a handler that raises an error or runs past the time limit refuses the event, even if it caught the time-limit error with pcall.
  • player_connecting takes a reason as the second return value (return false, "Members only"), shown to the player; the first reason given wins.
  • A refused player_chat is not broadcast. A resource that wants other text or other recipients refuses it and sends its own line.
lua
local muted = {} -- player id -> true

dlmp.add_event_handler("player_chat", dlmp.root, function(player, text)
    if muted[player:id()] then
        player:send_message("You are muted.")
        return false
    end
end)

The return value of every other event is ignored.

Commands ​

Player commands ​

A line a player types starting with / reaches the server as a command (ClientCommand, ADR 0016).

lua
dlmp.add_command_handler(name, fn, opts)  -- true | nil, err
dlmp.remove_command_handler(name, fn)     -- true | nil, err
  • name is the command without the /: not empty, at most 64 bytes, no whitespace; otherwise nil, err. The same name and fn registered twice by one resource returns nil, err.
  • opts.case_sensitive is false by default (ASCII letters match either case); opts.priority is "high", "normal" (the default) or "low".
  • The handler gets the player followed by the rest of the line split on whitespace, each word a string. It gets no command name and no unsplit text.
  • Several resources may handle the same command. Handlers run by priority, then in registration order; the first that returns true consumes the command and the rest do not run. A handler that returns anything else, or raises an error (logged), lets the next one run.
  • A command no handler consumes gets the answer Unknown command: /<name>. A bare / is ignored.
lua
dlmp.add_command_handler("heal", function(player, amount)
    local ok, err = player:set_health(tonumber(amount) or 100)
    if not ok then
        player:send_message("heal failed: " .. err)
    end
    return true
end)

Console commands ​

lua
dlmp.add_console_command(name, fn) -- true | nil, err

A console command is a line of the server console, for what only the operator may do.

  • name follows the rules of player commands and matches exactly. One resource holds a name: a name that any resource already holds returns nil, err.
  • The handler gets the words after the name, each a string, and no player. print writes its answer to the server log. An error is logged.
  • The server's own console commands (resource, help, status, uptime, version, sver, shutdown and password) are matched first; no resource sees a password line. A line no resource holds goes on to the server's remaining console commands.
  • There is no remove function: a console command goes with its resource's stop.

Timers ​

lua
local timer = dlmp.set_timer(delay_ms, repeats, fn, ...)
timer:cancel()
timer:is_active()
  • delay_ms is raised to at least 50. repeats is the number of runs; 0 repeats until cancelled.
  • The extra arguments are passed to fn on every run.
  • A timer counts from the server step of the callback that set it; a timer set at load time counts from the step the resource started in. It runs from the server's main loop. A timer that fell behind runs once and is next due one delay_ms after that run: missed runs are not made up.
  • An error in fn is logged; a repeating timer keeps running.
  • After its last run, after cancel() and after its resource's stop, is_active() is false and the timer never runs again. cancel() on such a timer does nothing.
  • A timer is not an element: it raises no events, and while it runs dlmp.event_name and dlmp.source are nil.
lua
local countdown = 10
local timer
timer = dlmp.set_timer(1000, 0, function()
    countdown = countdown - 1
    dlmp.show_announcement(dlmp.root, { title = tostring(countdown), duration_ms = 900 })
    if countdown == 0 then
        timer:cancel()
    end
end)

Documentation licensed under CC BY 4.0. DarqlineMP is not affiliated with Rockstar Games or Take-Two Interactive. Grand Theft Auto is a trademark of Take-Two Interactive Software, Inc.