{
  "$comment": "Machine-readable description of the GAMPEON Lua API (docs ADR 0013). Source for the developer docs (create.gampeon.com/docs), the MCP server and the LuaLS stubs in api/library/. runtime/tests checks that every global in the sandbox is listed here.",
  "version": "0.10.0",
  "status": "preview",
  "lua": "5.4",
  "categories": [
    {
      "id": "events",
      "title": "Events",
      "description": "Resources talk to each other and between server and client through named events."
    },
    {
      "id": "threads",
      "title": "Threads and timers",
      "description": "Threads are coroutines that the host resumes every tick. Wait pauses only the current thread."
    },
    {
      "id": "exports",
      "title": "Exports",
      "description": "Functions one resource offers to other resources on the same side."
    },
    {
      "id": "resource",
      "title": "Resource and runtime",
      "description": "Information about the running resource and the side it runs on."
    },
    {
      "id": "data",
      "title": "Data",
      "description": "Encoding and decoding values."
    },
    {
      "id": "sandbox",
      "title": "Sandbox",
      "description": "Standard Lua functions that behave differently in GAMPEON."
    },
    {
      "id": "commands",
      "title": "Commands",
      "description": "Console and chat commands that resources register, and permissions (ACE) for them."
    },
    {
      "id": "convars",
      "title": "Convars",
      "description": "Server settings from server.cfg (set, setr, sets) that scripts can read."
    },
    {
      "id": "http",
      "title": "HTTP",
      "description": "Web requests from server scripts, for example webhooks or external APIs."
    },
    {
      "id": "vectors",
      "title": "Vectors",
      "description": "vector2, vector3 and vector4 values for positions, directions and rotations."
    },
    {
      "id": "promises",
      "title": "Promises",
      "description": "Wait for a result that arrives later, for example from a callback."
    },
    {
      "id": "database",
      "title": "Database",
      "description": "MySQL/MariaDB (or SQLite for tests) through the MySQL table, compatible with oxmysql. Calls never block the server."
    },
    {
      "id": "players",
      "title": "Players",
      "description": "Players on the server: IDs, names, identifiers and kicking. Provided by the GAMPEON server (game plugin or gampeon-server), not by the bare runtime."
    },
    {
      "id": "world",
      "title": "World and entities",
      "description": "Player characters (peds), objects and vehicles in the game world: create, move and delete them. Positions are in meters, headings in degrees. Provided by the game (or gampeon-server with its placeholder world)."
    },
    {
      "id": "chat",
      "title": "Chat",
      "description": "The chat window of the game. Use the events of the built-in chat resource (chatMessage, chat:addMessage) in your scripts; these functions are what the chat resource itself uses."
    },
    {
      "id": "nui",
      "title": "Web interfaces (NUI)",
      "description": "HTML pages of a resource (ui_page) drawn over the game, with messages and callbacks like FiveM NUI."
    },
    {
      "id": "blocks",
      "title": "Block worlds",
      "description": "A world made of 1 m blocks that players dig and build in, generated from a seed. A platform feature: switch it on with EnableBlockWorld. Coordinates are whole blocks, which are the same as meters. Provided by the game (or gampeon-server)."
    }
  ],
  "functions": [
    {
      "name": "AddEventHandler",
      "category": "events",
      "side": "shared",
      "summary": "Run a function whenever an event fires.",
      "description": "Handlers run for local events from TriggerEvent and, once the event is registered with RegisterNetEvent, for events from the other side. Each handler runs in its own thread, so it may call Wait.",
      "params": [
        {
          "name": "eventName",
          "type": "string",
          "description": "Name of the event, for example \"myshop:buy\". Prefix it with your resource name to avoid clashes."
        },
        {
          "name": "handler",
          "type": "fun(...: any)",
          "description": "Called with the arguments of the event."
        }
      ],
      "returns": [
        {
          "type": "EventHandle",
          "description": "Pass it to RemoveEventHandler to remove the handler again."
        }
      ],
      "example": "AddEventHandler('myshop:opened', function(shopId)\n  print('Shop opened: ' .. shopId)\nend)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "RemoveEventHandler",
      "category": "events",
      "side": "shared",
      "summary": "Remove a handler added with AddEventHandler.",
      "params": [
        {
          "name": "handle",
          "type": "EventHandle",
          "description": "Value returned by AddEventHandler or RegisterNetEvent."
        }
      ],
      "returns": [],
      "example": "local h = AddEventHandler('tick', function() end)\nRemoveEventHandler(h)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "RegisterNetEvent",
      "category": "events",
      "side": "shared",
      "summary": "Allow an event to arrive from the other side (client to server or server to client).",
      "description": "Without this call, events from the network never reach your handlers. On the server, treat every network event as untrusted input: check `source` and every argument, because players can send any values.",
      "params": [
        {
          "name": "eventName",
          "type": "string",
          "description": "Name of the event."
        },
        {
          "name": "handler",
          "type": "fun(...: any)",
          "optional": true,
          "description": "Optional handler, same as calling AddEventHandler afterwards."
        }
      ],
      "returns": [
        {
          "type": "EventHandle?",
          "description": "The handle when a handler was given, otherwise nothing."
        }
      ],
      "example": "RegisterNetEvent('myshop:buy', function(itemId)\n  local player = source\n  if type(itemId) ~= 'string' then return end\n  -- check that the player may buy the item, then give it\nend)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "RegisterServerEvent",
      "category": "events",
      "side": "shared",
      "deprecated": "Use RegisterNetEvent.",
      "summary": "Old name of RegisterNetEvent, kept for ported FiveM scripts.",
      "params": [
        {
          "name": "eventName",
          "type": "string",
          "description": "Name of the event."
        },
        {
          "name": "handler",
          "type": "fun(...: any)",
          "optional": true,
          "description": "Optional handler."
        }
      ],
      "returns": [
        {
          "type": "EventHandle?",
          "description": "The handle when a handler was given."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "TriggerEvent",
      "category": "events",
      "side": "shared",
      "summary": "Fire an event for all resources on the same side.",
      "description": "Handlers of every running resource on this side receive the event. It never leaves the machine.",
      "params": [
        {
          "name": "eventName",
          "type": "string",
          "description": "Name of the event."
        },
        {
          "name": "...",
          "type": "any",
          "description": "Arguments. Values are copied: tables, strings, numbers, booleans and nil. Functions arrive as callable functions that run in the resource that passed them."
        }
      ],
      "returns": [],
      "example": "TriggerEvent('myshop:opened', 'downtown')\n\n-- callback pattern from ESX\nTriggerEvent('myframework:getSharedObject', function(obj) Framework = obj end)",
      "fivem": "Same as in FiveM, including functions as arguments."
    },
    {
      "name": "CancelEvent",
      "category": "events",
      "side": "shared",
      "summary": "Cancel the event whose handler is running.",
      "description": "Other handlers still run. The code that fired the event checks WasEventCanceled right after TriggerEvent and can skip its default action. Only works before the handler calls Wait.",
      "params": [],
      "returns": [],
      "example": "AddEventHandler('chatMessage', function(source, name, message)\n  if message:find('badword') then\n    CancelEvent()\n  end\nend)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "WasEventCanceled",
      "category": "events",
      "side": "shared",
      "summary": "Check if a handler canceled the last event fired with TriggerEvent.",
      "params": [],
      "returns": [
        {
          "type": "boolean",
          "description": "true if a handler called CancelEvent."
        }
      ],
      "example": "TriggerEvent('myshop:beforeBuy', source, item)\nif WasEventCanceled() then\n  return\nend",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "TriggerServerEvent",
      "category": "events",
      "side": "client",
      "summary": "Send an event from the client to the server.",
      "description": "The server only receives it if a resource registered it with RegisterNetEvent. The server sees the sending player in `source`.",
      "params": [
        {
          "name": "eventName",
          "type": "string",
          "description": "Name of the event."
        },
        {
          "name": "...",
          "type": "any",
          "description": "Arguments, copied like in TriggerEvent. Functions do not travel over the network and arrive as nil."
        }
      ],
      "returns": [],
      "example": "TriggerServerEvent('myshop:buy', 'water')",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "TriggerClientEvent",
      "category": "events",
      "side": "server",
      "summary": "Send an event from the server to one player or to everyone.",
      "params": [
        {
          "name": "eventName",
          "type": "string",
          "description": "Name of the event."
        },
        {
          "name": "target",
          "type": "integer",
          "description": "Player ID, or -1 for all players."
        },
        {
          "name": "...",
          "type": "any",
          "description": "Arguments, copied like in TriggerEvent. Functions do not travel over the network and arrive as nil."
        }
      ],
      "returns": [],
      "example": "TriggerClientEvent('myshop:bought', source, 'water')\nTriggerClientEvent('chat:announce', -1, 'Server restart in 5 minutes')",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "CreateThread",
      "category": "threads",
      "side": "shared",
      "summary": "Run a function as its own thread, starting with the next tick.",
      "description": "Use threads for loops that run over time. Always call Wait inside a loop, otherwise the script exceeds its instruction budget and is stopped.",
      "params": [
        {
          "name": "fn",
          "type": "fun()",
          "description": "Body of the thread."
        }
      ],
      "returns": [],
      "example": "CreateThread(function()\n  while true do\n    Wait(1000)\n    print('one second passed')\n  end\nend)",
      "fivem": "Same as Citizen.CreateThread in FiveM."
    },
    {
      "name": "Wait",
      "category": "threads",
      "side": "shared",
      "summary": "Pause the current thread.",
      "description": "Only works inside a thread, event handler or timeout. The rest of the game keeps running.",
      "params": [
        {
          "name": "ms",
          "type": "integer",
          "optional": true,
          "description": "Milliseconds to wait. 0 or nothing waits until the next tick."
        }
      ],
      "returns": [],
      "example": "Wait(500)",
      "fivem": "Same as in FiveM. Not allowed inside an export called from another resource."
    },
    {
      "name": "SetTimeout",
      "category": "threads",
      "side": "shared",
      "summary": "Run a function once after a delay.",
      "params": [
        {
          "name": "ms",
          "type": "integer",
          "description": "Delay in milliseconds."
        },
        {
          "name": "fn",
          "type": "fun()",
          "description": "Function to run. It runs as a thread and may call Wait."
        }
      ],
      "returns": [],
      "example": "SetTimeout(5000, function()\n  print('five seconds later')\nend)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "Citizen.CreateThread",
      "category": "threads",
      "side": "shared",
      "summary": "Same as CreateThread. Kept for ported FiveM scripts.",
      "params": [
        {
          "name": "fn",
          "type": "fun()",
          "description": "Body of the thread."
        }
      ],
      "returns": [],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "Citizen.Wait",
      "category": "threads",
      "side": "shared",
      "summary": "Same as Wait. Kept for ported FiveM scripts.",
      "params": [
        {
          "name": "ms",
          "type": "integer",
          "optional": true,
          "description": "Milliseconds to wait."
        }
      ],
      "returns": [],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "Citizen.SetTimeout",
      "category": "threads",
      "side": "shared",
      "summary": "Same as SetTimeout. Kept for ported FiveM scripts.",
      "params": [
        {
          "name": "ms",
          "type": "integer",
          "description": "Delay in milliseconds."
        },
        {
          "name": "fn",
          "type": "fun()",
          "description": "Function to run."
        }
      ],
      "returns": [],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "exports",
      "category": "exports",
      "side": "shared",
      "summary": "Offer a function to other resources, or call one.",
      "description": "Call `exports('name', fn)` to offer a function. Other resources call it with `exports.yourresource:name(...)` or `exports['your-resource']:name(...)`. Arguments and return values are copied. Functions in them, also inside tables, stay callable and run in the resource they come from, so an export can return an object with methods. Such a function stops working when its resource stops or restarts. Exports only work between resources on the same side.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "description": "Name of the export."
        },
        {
          "name": "fn",
          "type": "function",
          "description": "The function to offer."
        }
      ],
      "returns": [],
      "example": "-- in resource 'bank'\nexports('GetBalance', function(playerId)\n  return balances[playerId] or 0\nend)\n\n-- in another resource\nlocal money = exports.bank:GetBalance(source)",
      "fivem": "Same as in FiveM, including function references. If the export calls Wait, a caller in a thread or event handler waits for the result; elsewhere the result is lost and the log says so."
    },
    {
      "name": "GetCurrentResourceName",
      "category": "resource",
      "side": "shared",
      "summary": "Name of the resource this script belongs to.",
      "params": [],
      "returns": [
        {
          "type": "string",
          "description": "Folder name of the resource."
        }
      ],
      "example": "print('Started ' .. GetCurrentResourceName())",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetGameTimer",
      "category": "resource",
      "side": "shared",
      "summary": "Milliseconds since the runtime started. Use it to measure time.",
      "params": [],
      "returns": [
        {
          "type": "integer",
          "description": "Milliseconds."
        }
      ],
      "example": "local started = GetGameTimer()\nWait(250)\nprint(GetGameTimer() - started)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetHashKey",
      "category": "resource",
      "side": "shared",
      "summary": "Hash of a name, for example of a model name. Same numbers as in FiveM.",
      "description": "Jenkins one-at-a-time hash of the lower-case text as a signed 32-bit number. Functions that take a model accept the hash or the name itself.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "description": "Text to hash, for example \"prop_cube\". Upper and lower case give the same hash."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Hash between -2147483648 and 2147483647."
        }
      ],
      "example": "local model = GetHashKey('prop_cube')\nprint(IsModelValid(model))",
      "fivem": "Same as in FiveM. Backtick hashes (`prop_cube`) are not supported, use GetHashKey."
    },
    {
      "name": "IsDuplicityVersion",
      "category": "resource",
      "side": "shared",
      "summary": "True on the server, false on the client.",
      "description": "Useful in shared scripts that run on both sides.",
      "params": [],
      "returns": [
        {
          "type": "boolean",
          "description": "true on the server."
        }
      ],
      "example": "if IsDuplicityVersion() then\n  print('running on the server')\nend",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "json.encode",
      "category": "data",
      "side": "shared",
      "summary": "Turn a Lua value into a JSON string.",
      "params": [
        {
          "name": "value",
          "type": "any",
          "description": "Table, string, number, boolean or nil."
        }
      ],
      "returns": [
        {
          "type": "string",
          "description": "JSON text."
        }
      ],
      "example": "local text = json.encode({ name = 'Max', cash = 100 })",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "json.decode",
      "category": "data",
      "side": "shared",
      "summary": "Turn a JSON string into a Lua value.",
      "params": [
        {
          "name": "text",
          "type": "string",
          "description": "JSON text."
        }
      ],
      "returns": [
        {
          "type": "any",
          "description": "The value, or nil if the text is not valid JSON."
        },
        {
          "type": "string?",
          "description": "Error message when decoding failed."
        }
      ],
      "example": "local data, err = json.decode('{\"cash\": 100}')\nif not data then print(err) end",
      "fivem": "FiveM throws on invalid JSON; GAMPEON returns nil and an error message."
    },
    {
      "name": "print",
      "category": "sandbox",
      "side": "shared",
      "summary": "Write a line to the server or client log, tagged with the resource name.",
      "params": [
        {
          "name": "...",
          "type": "any",
          "description": "Values, separated by tabs."
        }
      ],
      "returns": [],
      "example": "print('player joined', source)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "load",
      "category": "sandbox",
      "side": "shared",
      "summary": "Compile Lua source text. Precompiled bytecode is refused.",
      "params": [
        {
          "name": "chunk",
          "type": "string",
          "description": "Lua source text."
        },
        {
          "name": "chunkname",
          "type": "string",
          "optional": true,
          "description": "Name used in error messages."
        },
        {
          "name": "mode",
          "type": "string",
          "optional": true,
          "description": "Ignored; only text is accepted."
        },
        {
          "name": "env",
          "type": "table",
          "optional": true,
          "description": "Environment of the chunk."
        }
      ],
      "returns": [
        {
          "type": "function?",
          "description": "The compiled function, or nil on error."
        },
        {
          "type": "string?",
          "description": "Error message."
        }
      ],
      "fivem": "FiveM allows bytecode; GAMPEON does not, for security."
    },
    {
      "name": "RegisterCommand",
      "category": "commands",
      "side": "shared",
      "summary": "Add a command for the console, the chat (/name) or key bindings.",
      "description": "Commands are shared by all resources on the same side. If two resources register the same name, the later one wins and a warning is logged. Commands of a stopped resource are removed.",
      "params": [
        {
          "name": "commandName",
          "type": "string",
          "description": "Name without slash, for example \"car\". Not case-sensitive."
        },
        {
          "name": "handler",
          "type": "fun(source: integer, args: string[], rawCommand: string)",
          "description": "Called with the player ID (0 for the server console), the words after the command and the whole line."
        },
        {
          "name": "restricted",
          "type": "boolean",
          "optional": true,
          "description": "If true, only players with the permission command.<name> may use it (see add_ace in server.cfg)."
        }
      ],
      "returns": [],
      "example": "RegisterCommand('heal', function(source, args, raw)\n  local target = tonumber(args[1]) or source\n  TriggerClientEvent('myres:heal', target)\nend, true)",
      "fivem": "Same as in FiveM. The handler runs in its own thread, so it may call Wait."
    },
    {
      "name": "ExecuteCommand",
      "category": "commands",
      "side": "shared",
      "summary": "Run a console command, as if typed into the server console.",
      "params": [
        {
          "name": "commandString",
          "type": "string",
          "description": "The whole command line, for example \"restart myresource\"."
        }
      ],
      "returns": [],
      "example": "ExecuteCommand('add_principal identifier.gampeon:42 group.admin')",
      "fivem": "Same as in FiveM. On the server it runs with console permissions; built-in commands like start, stop or add_ace work too."
    },
    {
      "name": "GetRegisteredCommands",
      "category": "commands",
      "side": "shared",
      "summary": "List all commands that resources registered on this side.",
      "params": [],
      "returns": [
        {
          "type": "{name: string, resource: string, arity: integer, restricted: boolean}[]",
          "description": "One entry per command. arity is always -1. restricted is true for commands that need an ACE permission."
        }
      ],
      "example": "for _, cmd in ipairs(GetRegisteredCommands()) do\n  print(cmd.name, cmd.resource)\nend",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "IsPlayerAceAllowed",
      "category": "commands",
      "side": "server",
      "summary": "Check if a player has a permission (ACE).",
      "params": [
        {
          "name": "playerSrc",
          "type": "integer|string",
          "description": "Player ID."
        },
        {
          "name": "object",
          "type": "string",
          "description": "Permission, for example \"command.heal\" or \"myres.admin\"."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true if allowed."
        }
      ],
      "example": "if IsPlayerAceAllowed(source, 'myres.admin') then\n  -- ...\nend",
      "fivem": "Same as in FiveM. Server operators grant permissions with add_ace and add_principal in server.cfg."
    },
    {
      "name": "GetConvar",
      "category": "convars",
      "side": "shared",
      "summary": "Read a convar as text.",
      "params": [
        {
          "name": "varName",
          "type": "string",
          "description": "Name, for example \"sv_hostname\"."
        },
        {
          "name": "default",
          "type": "string",
          "description": "Returned if the convar is not set."
        }
      ],
      "returns": [
        {
          "type": "string",
          "description": "The value or the default."
        }
      ],
      "example": "local locale = GetConvar('myres_locale', 'en')",
      "fivem": "Same as in FiveM. On the client only replicated convars (setr) are visible. Protected convars (for example the server key) always return the default."
    },
    {
      "name": "GetConvarInt",
      "category": "convars",
      "side": "shared",
      "summary": "Read a convar as a whole number.",
      "params": [
        {
          "name": "varName",
          "type": "string",
          "description": "Name."
        },
        {
          "name": "default",
          "type": "integer",
          "description": "Returned if the convar is not set or not a number."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "The value or the default."
        }
      ],
      "example": "local slots = GetConvarInt('sv_maxclients', 64)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetConvarFloat",
      "category": "convars",
      "side": "shared",
      "summary": "Read a convar as a number with decimals.",
      "params": [
        {
          "name": "varName",
          "type": "string",
          "description": "Name."
        },
        {
          "name": "default",
          "type": "number",
          "description": "Returned if the convar is not set or not a number."
        }
      ],
      "returns": [
        {
          "type": "number",
          "description": "The value or the default."
        }
      ],
      "example": "local rate = GetConvarFloat('myres_tax', 0.19)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetConvarBool",
      "category": "convars",
      "side": "shared",
      "summary": "Read a convar as true or false.",
      "params": [
        {
          "name": "varName",
          "type": "string",
          "description": "Name."
        },
        {
          "name": "default",
          "type": "boolean",
          "description": "Returned if the convar is not set or not a yes/no value."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true for true, 1, yes, on; false for false, 0, no, off; otherwise the default."
        }
      ],
      "example": "if GetConvarBool('myres_debug', false) then print('debug on') end",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "SetConvar",
      "category": "convars",
      "side": "server",
      "summary": "Set a convar on the server.",
      "params": [
        {
          "name": "varName",
          "type": "string",
          "description": "Name."
        },
        {
          "name": "value",
          "type": "string",
          "description": "New value."
        }
      ],
      "returns": [],
      "example": "SetConvar('myres_mode', 'event')",
      "fivem": "Same as in FiveM. Protected convars cannot be changed."
    },
    {
      "name": "SetConvarReplicated",
      "category": "convars",
      "side": "server",
      "summary": "Set a convar that clients can read too.",
      "params": [
        {
          "name": "varName",
          "type": "string",
          "description": "Name."
        },
        {
          "name": "value",
          "type": "string",
          "description": "New value."
        }
      ],
      "returns": [],
      "example": "SetConvarReplicated('myres_theme', 'dark')",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "SetConvarServerInfo",
      "category": "convars",
      "side": "server",
      "summary": "Set a convar that is shown in the server list.",
      "params": [
        {
          "name": "varName",
          "type": "string",
          "description": "Name."
        },
        {
          "name": "value",
          "type": "string",
          "description": "New value."
        }
      ],
      "returns": [],
      "example": "SetConvarServerInfo('tags', 'roleplay,economy')",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "PerformHttpRequest",
      "category": "http",
      "side": "server",
      "summary": "Send a web request and get the answer in a callback.",
      "params": [
        {
          "name": "url",
          "type": "string",
          "description": "http:// or https:// address. Addresses in private networks and localhost are blocked unless the server operator allows them."
        },
        {
          "name": "callback",
          "type": "fun(statusCode: integer, body: string?, headers: table<string, string>, errorData: string?)",
          "optional": true,
          "description": "Called with the result. body is nil for errors; then errorData has the reason or the error body."
        },
        {
          "name": "method",
          "type": "string",
          "optional": true,
          "description": "GET (default), POST, PUT, PATCH, DELETE, HEAD or OPTIONS."
        },
        {
          "name": "data",
          "type": "string",
          "optional": true,
          "description": "Request body, at most 1 MB by default."
        },
        {
          "name": "headers",
          "type": "table<string, string>",
          "optional": true,
          "description": "Request headers."
        },
        {
          "name": "options",
          "type": "{timeout: integer?}",
          "optional": true,
          "description": "timeout in milliseconds (at most the server limit, 30 s by default)."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "ID of the request."
        }
      ],
      "example": "PerformHttpRequest('https://example.com/api/status', function(status, body, headers, err)\n  if status == 200 then\n    local data = json.decode(body)\n  else\n    print('request failed', status, err)\n  end\nend, 'GET', '', { ['Accept'] = 'application/json' })",
      "fivem": "Same as in FiveM. At most 32 requests per resource at the same time, responses up to 16 MB."
    },
    {
      "name": "GetInvokingResource",
      "category": "resource",
      "side": "shared",
      "summary": "Name of the resource that called the current export or triggered the current local event.",
      "params": [],
      "returns": [
        {
          "type": "string?",
          "description": "Resource name, or nil inside network events, threads and timers."
        }
      ],
      "example": "exports('Pay', function(amount)\n  print(GetInvokingResource() .. ' wants to pay ' .. amount)\nend)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "LoadResourceFile",
      "category": "resource",
      "side": "shared",
      "summary": "Read a file from a resource folder.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource, for example GetCurrentResourceName()."
        },
        {
          "name": "fileName",
          "type": "string",
          "description": "Path inside the resource, for example \"data/items.json\"."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "File content, or nil if the file does not exist."
        }
      ],
      "example": "local items = json.decode(LoadResourceFile(GetCurrentResourceName(), 'data/items.json'))",
      "fivem": "Same as in FiveM. Paths cannot leave the resource folder. Files up to 16 MB."
    },
    {
      "name": "SaveResourceFile",
      "category": "resource",
      "side": "server",
      "summary": "Write a file into the own resource folder.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Must be the own resource."
        },
        {
          "name": "fileName",
          "type": "string",
          "description": "Path inside the resource. The folder must exist."
        },
        {
          "name": "data",
          "type": "string",
          "description": "Content."
        },
        {
          "name": "dataLength",
          "type": "integer",
          "optional": true,
          "description": "Number of bytes to write, or -1 for all."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true if written."
        }
      ],
      "example": "SaveResourceFile(GetCurrentResourceName(), 'data/state.json', json.encode(state), -1)",
      "fivem": "FiveM allows writing into any resource; GAMPEON only into the own one."
    },
    {
      "name": "GetResourceState",
      "category": "resource",
      "side": "shared",
      "summary": "Check if a resource runs.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource name."
        }
      ],
      "returns": [
        {
          "type": "string",
          "description": "\"started\", \"stopped\" (known but not running) or \"missing\"."
        }
      ],
      "example": "if GetResourceState('gp_inventory') ~= 'started' then\n  print('inventory missing')\nend",
      "fivem": "Same as in FiveM, without the states starting and stopping."
    },
    {
      "name": "GetNumResources",
      "category": "resource",
      "side": "shared",
      "summary": "Number of running resources on this side.",
      "params": [],
      "returns": [
        {
          "type": "integer",
          "description": "Count."
        }
      ],
      "example": "print(GetNumResources())",
      "fivem": "FiveM also counts stopped resources."
    },
    {
      "name": "GetResourceByFindIndex",
      "category": "resource",
      "side": "shared",
      "summary": "Name of the running resource at an index, for loops over all resources.",
      "params": [
        {
          "name": "findIndex",
          "type": "integer",
          "description": "0 to GetNumResources() - 1."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "Resource name."
        }
      ],
      "example": "for i = 0, GetNumResources() - 1 do\n  print(GetResourceByFindIndex(i))\nend",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetResourcePath",
      "category": "resource",
      "side": "server",
      "summary": "Folder of a resource on the server.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource name."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "Path, or nil if unknown."
        }
      ],
      "example": "print(GetResourcePath(GetCurrentResourceName()))",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetResourceMetadata",
      "category": "resource",
      "side": "shared",
      "summary": "Read a value from the manifest of a running resource.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource name."
        },
        {
          "name": "metadataKey",
          "type": "string",
          "description": "Directive, for example \"version\", \"author\" or \"ui_page\"."
        },
        {
          "name": "index",
          "type": "integer",
          "optional": true,
          "description": "0 for the first value."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "The value, or nil."
        }
      ],
      "example": "local version = GetResourceMetadata(GetCurrentResourceName(), 'version', 0)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetNumResourceMetadata",
      "category": "resource",
      "side": "shared",
      "summary": "How many values a manifest directive has.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource name."
        },
        {
          "name": "metadataKey",
          "type": "string",
          "description": "Directive."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Count."
        }
      ],
      "example": "print(GetNumResourceMetadata('myres', 'dependency'))",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "vector2",
      "category": "vectors",
      "side": "shared",
      "summary": "Create a vector2 with 2 numbers (x, y).",
      "params": [
        {
          "name": "x",
          "type": "number",
          "optional": true,
          "description": "x component. With only one number, all components get it."
        },
        {
          "name": "y",
          "type": "number",
          "optional": true,
          "description": "y component. With only one number, all components get it."
        }
      ],
      "returns": [
        {
          "type": "vector2",
          "description": "Read-only vector."
        }
      ],
      "example": "local size = vector2(1920, 1080)\nprint(size.x, size.y)",
      "fivem": "Same as in FiveM: + - * / with vectors and numbers, #v is the length, v.xy and other swizzles, type(v) returns \"vector2\". GAMPEON uses double precision."
    },
    {
      "name": "vec2",
      "category": "vectors",
      "side": "shared",
      "summary": "Short name for vector2.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "optional": true,
          "description": "x component."
        },
        {
          "name": "y",
          "type": "number",
          "optional": true,
          "description": "y component."
        }
      ],
      "returns": [
        {
          "type": "vector2",
          "description": "Read-only vector."
        }
      ],
      "example": "local v = vec2(1, 2)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "vector3",
      "category": "vectors",
      "side": "shared",
      "summary": "Create a vector3 with 3 numbers (x, y, z).",
      "description": "Vectors are values: components cannot be changed, create a new vector instead. They keep their type in events between server and client and in exports.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "optional": true,
          "description": "x component. With only one number, all components get it."
        },
        {
          "name": "y",
          "type": "number",
          "optional": true,
          "description": "y component. With only one number, all components get it."
        },
        {
          "name": "z",
          "type": "number",
          "optional": true,
          "description": "z component. With only one number, all components get it."
        }
      ],
      "returns": [
        {
          "type": "vector3",
          "description": "Read-only vector."
        }
      ],
      "example": "local spawn = vector3(120.0, -45.5, 30.0)\nlocal dist = #(GetEntityCoords(ped) - spawn)",
      "fivem": "Same as in FiveM: + - * / with vectors and numbers, #v is the length, v.xy and other swizzles, type(v) returns \"vector3\". GAMPEON uses double precision."
    },
    {
      "name": "vec3",
      "category": "vectors",
      "side": "shared",
      "summary": "Short name for vector3.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "optional": true,
          "description": "x component."
        },
        {
          "name": "y",
          "type": "number",
          "optional": true,
          "description": "y component."
        },
        {
          "name": "z",
          "type": "number",
          "optional": true,
          "description": "z component."
        }
      ],
      "returns": [
        {
          "type": "vector3",
          "description": "Read-only vector."
        }
      ],
      "example": "local v = vec3(1, 2, 3)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "vector4",
      "category": "vectors",
      "side": "shared",
      "summary": "Create a vector4 with 4 numbers (x, y, z, w).",
      "params": [
        {
          "name": "x",
          "type": "number",
          "optional": true,
          "description": "x component. With only one number, all components get it."
        },
        {
          "name": "y",
          "type": "number",
          "optional": true,
          "description": "y component. With only one number, all components get it."
        },
        {
          "name": "z",
          "type": "number",
          "optional": true,
          "description": "z component. With only one number, all components get it."
        },
        {
          "name": "w",
          "type": "number",
          "optional": true,
          "description": "w component. With only one number, all components get it."
        }
      ],
      "returns": [
        {
          "type": "vector4",
          "description": "Read-only vector."
        }
      ],
      "example": "local spawnWithHeading = vector4(120.0, -45.5, 30.0, 90.0)\nprint(spawnWithHeading.w)",
      "fivem": "Same as in FiveM: + - * / with vectors and numbers, #v is the length, v.xy and other swizzles, type(v) returns \"vector4\". GAMPEON uses double precision."
    },
    {
      "name": "vec4",
      "category": "vectors",
      "side": "shared",
      "summary": "Short name for vector4.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "optional": true,
          "description": "x component."
        },
        {
          "name": "y",
          "type": "number",
          "optional": true,
          "description": "y component."
        },
        {
          "name": "z",
          "type": "number",
          "optional": true,
          "description": "z component."
        },
        {
          "name": "w",
          "type": "number",
          "optional": true,
          "description": "w component."
        }
      ],
      "returns": [
        {
          "type": "vector4",
          "description": "Read-only vector."
        }
      ],
      "example": "local v = vec4(1, 2, 3, 4)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "vec",
      "category": "vectors",
      "side": "shared",
      "summary": "Create a vector with 2, 3 or 4 numbers.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "description": "x"
        },
        {
          "name": "y",
          "type": "number",
          "description": "y"
        },
        {
          "name": "z",
          "type": "number",
          "optional": true,
          "description": "z"
        },
        {
          "name": "w",
          "type": "number",
          "optional": true,
          "description": "w"
        }
      ],
      "returns": [
        {
          "type": "vector2|vector3|vector4",
          "description": "Vector with as many components as arguments."
        }
      ],
      "example": "local v = vec(1, 2, 3) -- vector3",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "promise.new",
      "category": "promises",
      "side": "shared",
      "summary": "Create a promise that is resolved later.",
      "description": "Call p:resolve(value) or p:reject(err) once. p:next(onResolved, onRejected) chains a follow-up and returns a new promise.",
      "params": [
        {
          "name": "executor",
          "type": "fun(resolve: function, reject: function)",
          "optional": true,
          "description": "Optional: called right away with resolve and reject."
        }
      ],
      "returns": [
        {
          "type": "promise",
          "description": "The promise."
        }
      ],
      "example": "local p = promise.new()\nPerformHttpRequest(url, function(status, body)\n  p:resolve(body)\nend)\nlocal body = Citizen.Await(p)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "promise.resolve",
      "category": "promises",
      "side": "shared",
      "summary": "Create a promise that is already resolved.",
      "params": [
        {
          "name": "...",
          "type": "any",
          "description": "Values."
        }
      ],
      "returns": [
        {
          "type": "promise",
          "description": "The promise."
        }
      ],
      "example": "local p = promise.resolve(42)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "promise.reject",
      "category": "promises",
      "side": "shared",
      "summary": "Create a promise that is already rejected.",
      "params": [
        {
          "name": "err",
          "type": "any",
          "description": "Error."
        }
      ],
      "returns": [
        {
          "type": "promise",
          "description": "The promise."
        }
      ],
      "example": "local p = promise.reject('no money')",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "promise.all",
      "category": "promises",
      "side": "shared",
      "summary": "Wait for several promises; resolves with a list of their first values.",
      "params": [
        {
          "name": "promises",
          "type": "promise[]",
          "description": "Promises."
        }
      ],
      "returns": [
        {
          "type": "promise",
          "description": "Resolves when all are resolved, rejects on the first error."
        }
      ],
      "example": "local results = Citizen.Await(promise.all({ p1, p2 }))",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "promise.first",
      "category": "promises",
      "side": "shared",
      "summary": "Resolve with the first promise that finishes.",
      "params": [
        {
          "name": "promises",
          "type": "promise[]",
          "description": "Promises."
        }
      ],
      "returns": [
        {
          "type": "promise",
          "description": "The new promise."
        }
      ],
      "example": "local winner = Citizen.Await(promise.first({ p1, p2 }))",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "promise.race",
      "category": "promises",
      "side": "shared",
      "summary": "Same as promise.first.",
      "params": [
        {
          "name": "promises",
          "type": "promise[]",
          "description": "Promises."
        }
      ],
      "returns": [
        {
          "type": "promise",
          "description": "The new promise."
        }
      ],
      "example": "local winner = Citizen.Await(promise.race({ p1, p2 }))",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "Citizen.Await",
      "category": "promises",
      "side": "shared",
      "summary": "Pause the current thread until a promise is resolved, then return its values.",
      "params": [
        {
          "name": "p",
          "type": "promise",
          "description": "The promise."
        }
      ],
      "returns": [
        {
          "type": "any",
          "description": "Values from resolve. A rejected promise raises the error here."
        }
      ],
      "example": "local data = Citizen.Await(p)",
      "fivem": "Same as in FiveM. Only inside a thread, event handler or command."
    },
    {
      "name": "Citizen.Trace",
      "category": "sandbox",
      "side": "shared",
      "summary": "Write to the server or client log, like print.",
      "params": [
        {
          "name": "...",
          "type": "any",
          "description": "Values."
        }
      ],
      "returns": [],
      "example": "Citizen.Trace('loaded')",
      "fivem": "FiveM prints without a line break; GAMPEON writes one log line."
    },
    {
      "name": "MySQL.query",
      "category": "database",
      "side": "server",
      "summary": "Run a query. SELECT returns the rows; other statements return { affectedRows, insertId }.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local rows = MySQL.query.await(sql, params). On an error the callback is not called and the error is logged; .await raises the error in the calling thread.",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL with ? or @name placeholders."
        },
        {
          "name": "parameters",
          "type": "table|any[]",
          "optional": true,
          "description": "Values for ? placeholders (list) or for @name/:name placeholders (table with keys \"name\" or \"@name\"). A list as a value for ? expands for IN (?)."
        },
        {
          "name": "cb",
          "type": "fun(result: table)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "MySQL.query('SELECT name, money FROM users WHERE job = ?', { 'police' }, function(rows)\n  for _, row in ipairs(rows) do print(row.name, row.money) end\nend)\n\nlocal rows = MySQL.query.await('SELECT * FROM users WHERE identifier = @id', { id = identifier })",
      "fivem": "Same as oxmysql. NULL columns are missing from the row table, TINYINT(1) becomes true/false, DATE/DATETIME/TIMESTAMP become milliseconds since 1970 (UTC).",
      "await": true
    },
    {
      "name": "MySQL.single",
      "category": "database",
      "side": "server",
      "summary": "Run a query and return only the first row.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local row = MySQL.single.await(sql, params). nil if there is no row.",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL."
        },
        {
          "name": "parameters",
          "type": "table|any[]",
          "optional": true,
          "description": "Values for ? placeholders (list) or for @name/:name placeholders (table with keys \"name\" or \"@name\"). A list as a value for ? expands for IN (?)."
        },
        {
          "name": "cb",
          "type": "fun(result: table?)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "local user = MySQL.single.await('SELECT * FROM users WHERE identifier = ? LIMIT 1', { identifier })\nif user then print(user.money) end",
      "fivem": "Same as oxmysql.",
      "await": true
    },
    {
      "name": "MySQL.scalar",
      "category": "database",
      "side": "server",
      "summary": "Run a query and return the first column of the first row.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local value = MySQL.scalar.await(sql, params).",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL."
        },
        {
          "name": "parameters",
          "type": "table|any[]",
          "optional": true,
          "description": "Values for ? placeholders (list) or for @name/:name placeholders (table with keys \"name\" or \"@name\"). A list as a value for ? expands for IN (?)."
        },
        {
          "name": "cb",
          "type": "fun(result: any)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "local count = MySQL.scalar.await('SELECT COUNT(*) FROM vehicles WHERE owner = ?', { identifier })",
      "fivem": "Same as oxmysql.",
      "await": true
    },
    {
      "name": "MySQL.insert",
      "category": "database",
      "side": "server",
      "summary": "Run an INSERT and return the new ID.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local id = MySQL.insert.await(sql, params).",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL."
        },
        {
          "name": "parameters",
          "type": "table|any[]",
          "optional": true,
          "description": "Values for ? placeholders (list) or for @name/:name placeholders (table with keys \"name\" or \"@name\"). A list as a value for ? expands for IN (?)."
        },
        {
          "name": "cb",
          "type": "fun(result: integer)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "local id = MySQL.insert.await('INSERT INTO bills (owner, amount) VALUES (?, ?)', { identifier, 250 })",
      "fivem": "Same as oxmysql.",
      "await": true
    },
    {
      "name": "MySQL.update",
      "category": "database",
      "side": "server",
      "summary": "Run an UPDATE or DELETE and return the number of changed rows.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local count = MySQL.update.await(sql, params).",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL."
        },
        {
          "name": "parameters",
          "type": "table|any[]",
          "optional": true,
          "description": "Values for ? placeholders (list) or for @name/:name placeholders (table with keys \"name\" or \"@name\"). A list as a value for ? expands for IN (?)."
        },
        {
          "name": "cb",
          "type": "fun(result: integer)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "local changed = MySQL.update.await('UPDATE users SET money = money + ? WHERE identifier = ?', { 100, identifier })",
      "fivem": "Same as oxmysql.",
      "await": true
    },
    {
      "name": "MySQL.prepare",
      "category": "database",
      "side": "server",
      "summary": "Run a statement with ? placeholders, also many times with a list of parameter lists.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local result = MySQL.prepare.await(sql, params).",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL with ? placeholders."
        },
        {
          "name": "parameters",
          "type": "any[]|any[][]",
          "description": "One list of values, or a list of lists to run the statement once per entry."
        },
        {
          "name": "cb",
          "type": "fun(result: any)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "MySQL.prepare('INSERT INTO items (owner, name) VALUES (?, ?)', { { 1, 'bread' }, { 1, 'water' } })\nlocal money = MySQL.prepare.await('SELECT money FROM users WHERE id = ?', { 1 })",
      "fivem": "Same as oxmysql: one row with one column returns the value, one row returns the row, more rows return the list. INSERT returns the new ID, other statements the number of changed rows. Runs as a normal query on the server.",
      "await": true
    },
    {
      "name": "MySQL.rawExecute",
      "category": "database",
      "side": "server",
      "summary": "Run a query and return the raw result.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local result = MySQL.rawExecute.await(sql, params).",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL."
        },
        {
          "name": "parameters",
          "type": "table|any[]",
          "optional": true,
          "description": "Values for ? placeholders (list) or for @name/:name placeholders (table with keys \"name\" or \"@name\"). A list as a value for ? expands for IN (?)."
        },
        {
          "name": "cb",
          "type": "fun(result: table)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "local rows = MySQL.rawExecute.await('SHOW TABLES')",
      "fivem": "Same as MySQL.query in GAMPEON.",
      "await": true
    },
    {
      "name": "MySQL.execute",
      "category": "database",
      "side": "server",
      "summary": "Old name of MySQL.query.",
      "params": [
        {
          "name": "query",
          "type": "string",
          "description": "SQL."
        },
        {
          "name": "parameters",
          "type": "table|any[]",
          "optional": true,
          "description": "Values for ? placeholders (list) or for @name/:name placeholders (table with keys \"name\" or \"@name\"). A list as a value for ? expands for IN (?)."
        },
        {
          "name": "cb",
          "type": "fun(result: table)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "MySQL.execute('DELETE FROM sessions WHERE expires < NOW()')",
      "fivem": "Same as oxmysql."
    },
    {
      "name": "MySQL.transaction",
      "category": "database",
      "side": "server",
      "summary": "Run several statements in one transaction. If one fails, none of them is kept.",
      "description": "Add .await to wait for the result in the current thread instead of a callback: local ok = MySQL.transaction.await(queries, params).",
      "params": [
        {
          "name": "queries",
          "type": "(string|{query: string, values: table?})[]",
          "description": "Statements as strings (using the shared parameters) or tables with query and values."
        },
        {
          "name": "parameters",
          "type": "table",
          "optional": true,
          "description": "Shared parameters for statements given as strings."
        },
        {
          "name": "cb",
          "type": "fun(result: boolean)",
          "optional": true,
          "description": "Called with the result. Leave it out and use .await to get the result directly."
        }
      ],
      "returns": [],
      "example": "local ok = MySQL.transaction.await({\n  { query = 'UPDATE users SET money = money - ? WHERE id = ?', values = { 100, from } },\n  { query = 'UPDATE users SET money = money + ? WHERE id = ?', values = { 100, to } },\n})",
      "fivem": "Same as oxmysql. The callback gets true, or false after a rollback.",
      "await": true
    },
    {
      "name": "MySQL.ready",
      "category": "database",
      "side": "server",
      "summary": "Run a function once the database is ready.",
      "params": [
        {
          "name": "cb",
          "type": "function",
          "description": "Called in a new thread."
        }
      ],
      "returns": [],
      "example": "MySQL.ready(function()\n  print('database ready')\nend)",
      "fivem": "Same as oxmysql. The server connects before resources start, so it runs right away."
    },
    {
      "name": "MySQL.Async",
      "category": "database",
      "side": "server",
      "summary": "Old mysql-async names: fetchAll, fetchScalar, fetchSingle, insert, execute, transaction (callback form).",
      "params": [],
      "returns": [],
      "example": "MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @identifier', { ['@identifier'] = id }, function(rows) end)",
      "fivem": "Same as the mysql-async names that oxmysql still offers. Prefer MySQL.query and friends in new code.",
      "kind": "table"
    },
    {
      "name": "MySQL.Sync",
      "category": "database",
      "side": "server",
      "summary": "Old mysql-async names that wait for the result: fetchAll, fetchScalar, fetchSingle, insert, execute, transaction.",
      "params": [],
      "returns": [],
      "example": "local rows = MySQL.Sync.fetchAll('SELECT * FROM jobs')",
      "fivem": "Same as the mysql-async names that oxmysql still offers. Only inside a thread.",
      "kind": "table"
    },
    {
      "name": "GetPlayers",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "List the IDs of all players on the server.",
      "params": [],
      "returns": [
        {
          "type": "string[]",
          "description": "Player IDs as strings, like in FiveM."
        }
      ],
      "example": "for _, id in ipairs(GetPlayers()) do\n  print(id, GetPlayerName(id))\nend",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetNumPlayerIndices",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Number of players on the server.",
      "params": [],
      "returns": [
        {
          "type": "integer",
          "description": "Player count."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetPlayerFromIndex",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Player ID at a position in the player list.",
      "params": [
        {
          "name": "index",
          "type": "integer",
          "description": "0 to GetNumPlayerIndices() - 1."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "Player ID, or nil if the index is out of range."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "DoesPlayerExist",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Check whether a player ID is on the server.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true if the player is connected."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetPlayerName",
      "category": "players",
      "side": "shared",
      "provider": "host",
      "summary": "Display name of a player.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source) on the server. On the client only your own player (PlayerId()) for now."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "The name, or nil for unknown IDs on the server. On the client \"**Invalid**\" for other players."
        }
      ],
      "fivem": "Same as in FiveM. The name is the player's GAMPEON display name. Client scripts can only read their own name for now."
    },
    {
      "name": "GetPlayerIdentifiers",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "All identifiers of a player.",
      "description": "The first is always `gampeon:<account id>`. `license:<40 hex>` is derived from the account and the convar sv_licenseSalt, so databases from ESX and Qbox that key players by license keep working.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        }
      ],
      "returns": [
        {
          "type": "string[]",
          "description": "Identifiers like \"gampeon:42\" and \"license:…\"."
        }
      ],
      "example": "for _, id in ipairs(GetPlayerIdentifiers(source)) do print(id) end",
      "fivem": "Same as in FiveM. There are no steam:, discord:, xbl: or ip: identifiers yet."
    },
    {
      "name": "GetNumPlayerIdentifiers",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Number of identifiers of a player.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Count, 0 for unknown IDs."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetPlayerIdentifier",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "One identifier of a player by position.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        },
        {
          "name": "index",
          "type": "integer",
          "description": "0-based position."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "The identifier, or nil."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetPlayerIdentifierByType",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "The identifier of a player with a given prefix.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        },
        {
          "name": "identifierType",
          "type": "string",
          "description": "Prefix without colon, for example \"license\" or \"gampeon\"."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "For example \"license:3f2a…\", or nil."
        }
      ],
      "example": "local license = GetPlayerIdentifierByType(source, 'license')",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetPlayerEndpoint",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Network address of a player, as far as the server shares it.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        }
      ],
      "returns": [
        {
          "type": "string?",
          "description": "Address text, may be empty."
        }
      ],
      "fivem": "Like FiveM. Servers may hide addresses for privacy, then it is empty."
    },
    {
      "name": "GetPlayerPing",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Round-trip time to a player in milliseconds.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Ping in ms, 0 for unknown IDs."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetPlayerLastMsg",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Milliseconds since the last network event from a player.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Time in ms."
        }
      ],
      "fivem": "Counts script events, not every network packet."
    },
    {
      "name": "PlayerId",
      "category": "players",
      "side": "client",
      "provider": "host",
      "summary": "Handle of your own player on the client. Pass it to GetPlayerServerId or GetPlayerName.",
      "params": [],
      "returns": [
        {
          "type": "integer",
          "description": "Always 0 for now (the local player)."
        }
      ],
      "example": "local myId = GetPlayerServerId(PlayerId())\nprint('my server id is ' .. myId)",
      "fivem": "Same use as in FiveM. Other players' handles come with the game natives in a later step."
    },
    {
      "name": "GetPlayerServerId",
      "category": "players",
      "side": "client",
      "provider": "host",
      "summary": "Server ID (source) of a player, as server scripts see it.",
      "params": [
        {
          "name": "player",
          "type": "integer",
          "description": "Player handle, for now only PlayerId()."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "The server ID, 0 for unknown players."
        }
      ],
      "example": "TriggerServerEvent('myres:ready', GetPlayerServerId(PlayerId()))",
      "fivem": "Same as in FiveM for your own player."
    },
    {
      "name": "DropPlayer",
      "category": "players",
      "side": "server",
      "provider": "host",
      "summary": "Disconnect a player with a reason.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Player ID (source)."
        },
        {
          "name": "reason",
          "type": "string",
          "optional": true,
          "description": "Shown to the player."
        }
      ],
      "returns": [],
      "example": "DropPlayer(source, 'You were kicked for spamming.')",
      "fivem": "Same as in FiveM. playerDropped fires with the reason."
    },
    {
      "name": "GetPlayerPed",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "A player's character (ped).",
      "description": "On the server, pass the player ID (source). On the client, pass -1 or PlayerId() for your own character; other players follow later. Returns 0 while the player has no character, for example right after joining.",
      "params": [
        {
          "name": "playerSrc",
          "type": "string|integer",
          "description": "Server: player ID (source). Client: -1 or PlayerId()."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Entity handle of the ped, or 0."
        }
      ],
      "example": "RegisterCommand('pos', function(source)\n  local ped = GetPlayerPed(source)\n  print(GetPlayerName(source) .. ' is at ' .. GetEntityCoords(ped))\nend)",
      "fivem": "Same as in FiveM. Server and client have their own handles for the same entity, so do not send handles in events."
    },
    {
      "name": "PlayerPedId",
      "category": "world",
      "side": "client",
      "provider": "world",
      "summary": "Your own character (ped) on the client.",
      "params": [],
      "returns": [
        {
          "type": "integer",
          "description": "Entity handle of your ped, or 0 before it spawned."
        }
      ],
      "example": "local coords = GetEntityCoords(PlayerPedId())\nprint(('I am at %.1f, %.1f, %.1f'):format(coords.x, coords.y, coords.z))",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "DoesEntityExist",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Check if an entity handle still points to an entity.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true if the entity exists."
        }
      ],
      "example": "local ped = GetPlayerPed(source)\nif DoesEntityExist(ped) then\n  print('ped exists')\nend",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetEntityCoords",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Position of an entity in meters.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "vector3",
          "description": "Position in meters. vector3(0, 0, 0) for unknown entities."
        }
      ],
      "example": "local a = GetEntityCoords(GetPlayerPed(source))\nlocal b = vector3(100.0, 20.0, 1.0)\nif #(a - b) < 5.0 then\n  print('near the shop')\nend",
      "fivem": "Same as in FiveM: meters, z is up. The axes follow the GAMPEON map, so coordinates from GTA maps do not carry over. Extra arguments (alive) are ignored."
    },
    {
      "name": "GetEntityHeading",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Direction an entity faces, in degrees.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "number",
          "description": "0 up to 360. 0 faces +x, 90 faces +y. 0 for unknown entities."
        }
      ],
      "fivem": "Degrees from 0 to 360 like in FiveM, but measured like the Unreal yaw (Rotation Z in the editor): 0 faces +x and the angle grows towards +y. For directions use GetEntityForwardVector instead of GTA heading math."
    },
    {
      "name": "GetEntityForwardVector",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Unit vector in the direction an entity faces.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "vector3",
          "description": "Length 1, z is 0. Follows GetEntityHeading: heading 0 gives (1, 0, 0), 90 gives (0, 1, 0). (0, 0, 0) for unknown entities."
        }
      ],
      "example": "local ped = GetPlayerPed(source)\nlocal inFront = GetEntityCoords(ped) + GetEntityForwardVector(ped) * 2.0",
      "fivem": "Same as in FiveM. Use it instead of sin and cos of the heading: GAMPEON headings are measured like the Unreal yaw, so GTA heading math gives other directions."
    },
    {
      "name": "GetEntityForwardX",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "X part of GetEntityForwardVector.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "number",
          "description": "-1 up to 1. 0 for unknown entities."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetEntityForwardY",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Y part of GetEntityForwardVector.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "number",
          "description": "-1 up to 1. 0 for unknown entities."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetEntityVelocity",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Speed and direction of an entity in meters per second.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "vector3",
          "description": "Velocity in meters per second. #velocity is the speed."
        }
      ],
      "example": "local speed = #GetEntityVelocity(GetPlayerPed(source))\nprint(('%.1f m/s'):format(speed))",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "GetEntityType",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Kind of entity.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "0 = no entity, 1 = ped, 2 = vehicle, 3 = object."
        }
      ],
      "fivem": "Same numbers as in FiveM. Peds come from players; objects come from CreateObject. Vehicles need vehicle models, which the game does not have yet."
    },
    {
      "name": "SetEntityCoords",
      "category": "world",
      "side": "server",
      "provider": "world",
      "summary": "Teleport an entity to a position in meters.",
      "description": "The server owns the world, so only server scripts move entities. Players see the new position through the normal replication. Coordinates must be finite and within 1,000,000 meters. For a character the position is the middle of its body, about 0.9 meters above its feet, so add that to the ground height (GetEntityCoords returns the same point).",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        },
        {
          "name": "x",
          "type": "number|vector3",
          "description": "x in meters, or a vector3 with x, y and z."
        },
        {
          "name": "y",
          "type": "number",
          "optional": true,
          "description": "y in meters (not needed with a vector3)."
        },
        {
          "name": "z",
          "type": "number",
          "optional": true,
          "description": "z in meters (not needed with a vector3)."
        }
      ],
      "returns": [],
      "example": "RegisterCommand('tpspawn', function(source)\n  SetEntityCoords(GetPlayerPed(source), vector3(0.0, 0.0, 2.0))\nend, true)",
      "fivem": "Like in FiveM, but server only for now. The flags after z (alive, deadFlag, ragdollFlag, clearArea) are ignored. Unknown handles do nothing."
    },
    {
      "name": "SetEntityHeading",
      "category": "world",
      "side": "server",
      "provider": "world",
      "summary": "Turn an entity to face a direction in degrees.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle, for example from GetPlayerPed. 0 means no entity."
        },
        {
          "name": "heading",
          "type": "number",
          "description": "Degrees. Any value works, it is wrapped to 0 up to 360. 0 faces +x, 90 faces +y."
        }
      ],
      "returns": [],
      "example": "SetEntityHeading(GetPlayerPed(source), 90.0)",
      "fivem": "Like in FiveM, but server only for now and measured like the Unreal yaw (see GetEntityHeading)."
    },
    {
      "name": "GetEntityModel",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Model of an entity as a hash.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle. 0 means no entity."
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "GetHashKey of the model name, 0 for player characters and missing entities."
        }
      ],
      "example": "if GetEntityModel(obj) == GetHashKey('prop_cube') then\n  print('a cube')\nend",
      "fivem": "Same as in FiveM, but player characters have no model hash yet (0)."
    },
    {
      "name": "IsModelValid",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Check if the game knows a model that CreateObject or CreateVehicle can create.",
      "params": [
        {
          "name": "model",
          "type": "string|integer",
          "description": "Model name such as \"prop_cube\" or its hash from GetHashKey."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true if the model exists."
        }
      ],
      "example": "print(IsModelValid('prop_cube'))",
      "fivem": "Same idea as in FiveM, but GAMPEON has its own models instead of the GTA ones. Built in: prop_cube, prop_sphere, prop_cylinder, prop_cone and prop_plane."
    },
    {
      "name": "IsModelAVehicle",
      "category": "world",
      "side": "shared",
      "provider": "world",
      "summary": "Check if a model is a vehicle model.",
      "params": [
        {
          "name": "model",
          "type": "string|integer",
          "description": "Model name such as \"prop_cube\" or its hash from GetHashKey."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true for vehicle models, false for objects and unknown models."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "CreateObject",
      "category": "world",
      "side": "server",
      "provider": "world",
      "summary": "Create an object in the world for every player.",
      "description": "The object belongs to your resource and is deleted when the resource stops. It has physics but sleeps until something hits it, like props in FiveM; FreezeEntityPosition holds it in place. A resource may have 1000 entities at once. Unknown models return 0 and write a warning to the log.",
      "params": [
        {
          "name": "model",
          "type": "string|integer",
          "description": "Model name such as \"prop_cube\" or its hash from GetHashKey."
        },
        {
          "name": "x",
          "type": "number",
          "description": "Position in meters (or a vector3 here and leave out y and z)."
        },
        {
          "name": "y",
          "type": "number",
          "description": "Meters."
        },
        {
          "name": "z",
          "type": "number",
          "description": "Meters, up."
        },
        {
          "name": "isNetwork",
          "type": "boolean",
          "description": "Ignored: objects from the server always exist for every player.",
          "optional": true
        },
        {
          "name": "netMissionEntity",
          "type": "boolean",
          "description": "Ignored.",
          "optional": true
        },
        {
          "name": "doorFlag",
          "type": "boolean",
          "description": "Ignored.",
          "optional": true
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Entity handle, 0 if it could not be created."
        }
      ],
      "example": "local c = GetEntityCoords(GetPlayerPed(source))\nlocal box = CreateObject('prop_cube', c.x + 2.0, c.y, c.z, true, true, false)\nFreezeEntityPosition(box, true)",
      "fivem": "Same arguments as in FiveM, but server only for now and with GAMPEON models. The object is not pushed to the ground; it is placed at the position you give."
    },
    {
      "name": "CreateObjectNoOffset",
      "category": "world",
      "side": "server",
      "provider": "world",
      "summary": "Same as CreateObject.",
      "params": [
        {
          "name": "model",
          "type": "string|integer",
          "description": "Model name such as \"prop_cube\" or its hash from GetHashKey."
        },
        {
          "name": "x",
          "type": "number",
          "description": "Position in meters (or a vector3 here and leave out y and z)."
        },
        {
          "name": "y",
          "type": "number",
          "description": "Meters."
        },
        {
          "name": "z",
          "type": "number",
          "description": "Meters, up."
        },
        {
          "name": "isNetwork",
          "type": "boolean",
          "description": "Ignored.",
          "optional": true
        },
        {
          "name": "netMissionEntity",
          "type": "boolean",
          "description": "Ignored.",
          "optional": true
        },
        {
          "name": "doorFlag",
          "type": "boolean",
          "description": "Ignored.",
          "optional": true
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Entity handle, 0 if it could not be created."
        }
      ],
      "fivem": "In FiveM the two differ in how the position is offset; GAMPEON places both at the exact position."
    },
    {
      "name": "CreateVehicle",
      "category": "world",
      "side": "server",
      "provider": "world",
      "summary": "Create a vehicle in the world for every player.",
      "description": "Belongs to your resource and is deleted when it stops. The game has no vehicle models yet, so for now this returns 0 and writes a warning to the log.",
      "params": [
        {
          "name": "model",
          "type": "string|integer",
          "description": "Model name such as \"prop_cube\" or its hash from GetHashKey."
        },
        {
          "name": "x",
          "type": "number",
          "description": "Position in meters (or a vector3 here and leave out y and z)."
        },
        {
          "name": "y",
          "type": "number",
          "description": "Meters."
        },
        {
          "name": "z",
          "type": "number",
          "description": "Meters, up."
        },
        {
          "name": "heading",
          "type": "number",
          "description": "Degrees, 0 faces +x, 90 faces +y.",
          "optional": true
        },
        {
          "name": "isNetwork",
          "type": "boolean",
          "description": "Ignored: vehicles from the server always exist for every player.",
          "optional": true
        },
        {
          "name": "netMissionEntity",
          "type": "boolean",
          "description": "Ignored.",
          "optional": true
        }
      ],
      "returns": [
        {
          "type": "integer",
          "description": "Entity handle, 0 if it could not be created."
        }
      ],
      "fivem": "Same arguments as in FiveM, but server only for now and with GAMPEON models."
    },
    {
      "name": "DeleteEntity",
      "category": "world",
      "side": "server",
      "provider": "world",
      "summary": "Delete an entity that a script created.",
      "description": "Works for objects and vehicles from CreateObject and CreateVehicle of any resource. Player characters cannot be deleted.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle. 0 does nothing."
        }
      ],
      "returns": [],
      "example": "DeleteEntity(box)",
      "fivem": "Same as in FiveM, but server only for now."
    },
    {
      "name": "FreezeEntityPosition",
      "category": "world",
      "side": "server",
      "provider": "world",
      "summary": "Hold an entity in place without physics, or let it move again.",
      "params": [
        {
          "name": "entity",
          "type": "integer",
          "description": "Entity handle."
        },
        {
          "name": "toggle",
          "type": "boolean",
          "description": "true holds it in place, false turns physics back on."
        }
      ],
      "returns": [],
      "example": "FreezeEntityPosition(box, true)",
      "fivem": "Same as in FiveM, but server only for now and only for entities that scripts created."
    },
    {
      "name": "Gampeon.ChatAddMessage",
      "category": "chat",
      "side": "client",
      "provider": "host",
      "summary": "Show a line in the chat window of the game.",
      "description": "Most scripts use the chat:addMessage event instead, which the chat resource turns into this call. Line breaks become spaces, other control characters are removed, the author is cut at 64 bytes and the text at 1024 bytes.",
      "params": [
        {
          "name": "author",
          "type": "string",
          "description": "Shown before the text. Empty for a line without author."
        },
        {
          "name": "text",
          "type": "string",
          "description": "The message."
        },
        {
          "name": "color",
          "type": "{integer, integer, integer}",
          "optional": true,
          "description": "Color as {r, g, b} from 0 to 255. Default white."
        }
      ],
      "returns": [],
      "example": "Gampeon.ChatAddMessage('System', 'Welcome to the server', {0, 200, 255})",
      "fivem": "GAMPEON function. FiveM scripts use the chat:addMessage event, which works the same with the built-in chat resource."
    },
    {
      "name": "Gampeon.ChatClear",
      "category": "chat",
      "side": "client",
      "provider": "host",
      "summary": "Remove all lines from the chat window.",
      "params": [],
      "returns": [],
      "fivem": "GAMPEON function. The chat:clear event of the chat resource calls it."
    },
    {
      "name": "Gampeon.ChatAddSuggestion",
      "category": "chat",
      "side": "client",
      "provider": "host",
      "summary": "Suggest a command while the player types in the chat window.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "description": "The command with slash, for example \"/tp\"."
        },
        {
          "name": "help",
          "type": "string",
          "optional": true,
          "description": "Short help text."
        }
      ],
      "returns": [],
      "fivem": "GAMPEON function. The chat:addSuggestion event of the chat resource calls it. Parameter hints are not shown yet."
    },
    {
      "name": "Gampeon.ChatRemoveSuggestion",
      "category": "chat",
      "side": "client",
      "provider": "host",
      "summary": "Remove a command suggestion.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "description": "The command with slash."
        }
      ],
      "returns": [],
      "fivem": "GAMPEON function. The chat:removeSuggestion event of the chat resource calls it."
    },
    {
      "name": "StartResource",
      "category": "resource",
      "side": "server",
      "provider": "host",
      "summary": "Start a resource and its dependencies.",
      "description": "Needs the permission command.start for the calling resource: `add_ace resource.<name> command.start allow`.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource name."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true if it runs afterwards."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "StopResource",
      "category": "resource",
      "side": "server",
      "provider": "host",
      "summary": "Stop a resource and everything that depends on it.",
      "description": "Needs the permission command.stop for the calling resource.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource name."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true if it was running."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "SendNUIMessage",
      "category": "nui",
      "side": "client",
      "provider": "host",
      "summary": "Send data to the page of this resource.",
      "description": "The page receives it as the data of a message event: window.addEventListener('message', (e) => { ... e.data ... }). The data is sent as JSON, at most 1 MB.",
      "params": [
        {
          "name": "data",
          "type": "table",
          "description": "Any value that converts to JSON, usually a table with a type field."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "Always true."
        }
      ],
      "example": "SendNUIMessage({ type = 'open', items = { 'bread', 'water' } })",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "SendNuiMessage",
      "category": "nui",
      "side": "client",
      "provider": "host",
      "summary": "Send a JSON text to the page of this resource.",
      "params": [
        {
          "name": "jsonString",
          "type": "string",
          "description": "A JSON text, for example from json.encode."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "false if the text is not valid JSON."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "RegisterNUICallback",
      "category": "nui",
      "side": "client",
      "provider": "host",
      "summary": "Answer requests from the page of this resource.",
      "description": "The page calls fetch(`https://${GetParentResourceName()}/name`, { method: 'POST', body: JSON.stringify(data) }). The handler gets the decoded body and a function cb to answer; the page gets the answer as JSON. If the handler fails or does not answer within 30 seconds, the page gets an error.",
      "params": [
        {
          "name": "callbackType",
          "type": "string",
          "description": "Name in the URL, for example \"close\"."
        },
        {
          "name": "callback",
          "type": "fun(data: any, cb: fun(result: any))",
          "description": "Called with the request body; call cb once with the answer."
        }
      ],
      "returns": [
        {
          "type": "EventHandle",
          "description": "Handle for RemoveEventHandler."
        }
      ],
      "example": "RegisterNUICallback('close', function(data, cb)\n  SetNuiFocus(false, false)\n  cb('ok')\nend)",
      "fivem": "Same as in FiveM. The low-level form with RegisterNuiCallbackType and AddEventHandler('__cfx_nui:name') is not supported."
    },
    {
      "name": "SetNuiFocus",
      "category": "nui",
      "side": "client",
      "provider": "host",
      "summary": "Give keyboard and mouse to the pages, or give them back to the game.",
      "description": "Focus is shared by all resources; the last call wins. When the resource that set the focus stops, the focus goes back to the game. Players can always take the focus back with a fixed key of the game.",
      "params": [
        {
          "name": "hasFocus",
          "type": "boolean",
          "description": "Pages get the keyboard."
        },
        {
          "name": "hasCursor",
          "type": "boolean",
          "description": "Pages get the mouse and the cursor is shown."
        }
      ],
      "returns": [],
      "example": "RegisterCommand('inventory', function()\n  SendNUIMessage({ type = 'open' })\n  SetNuiFocus(true, true)\nend)",
      "fivem": "Same as in FiveM."
    },
    {
      "name": "SetNuiFocusKeepInput",
      "category": "nui",
      "side": "client",
      "provider": "host",
      "summary": "Let the game keep getting keyboard input while a page has focus.",
      "params": [
        {
          "name": "keepInput",
          "type": "boolean",
          "description": "true: player can still move while the page has focus."
        }
      ],
      "returns": [],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "IsNuiFocused",
      "category": "nui",
      "side": "client",
      "provider": "host",
      "summary": "Whether a page has the keyboard focus.",
      "params": [],
      "returns": [
        {
          "type": "boolean",
          "description": "true while a page has focus."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "IsNuiFocusKeepingInput",
      "category": "nui",
      "side": "client",
      "provider": "host",
      "summary": "Whether the game keeps keyboard input while a page has focus.",
      "params": [],
      "returns": [
        {
          "type": "boolean",
          "description": "The value set with SetNuiFocusKeepInput."
        }
      ],
      "fivem": "Same as in FiveM."
    },
    {
      "name": "EnableBlockWorld",
      "category": "blocks",
      "side": "server",
      "provider": "blocks",
      "summary": "Switch on the block world of this server.",
      "description": "Call it once when your resource starts, before players spawn. The world stays on until the server stops, also when your resource restarts; calling it again with the same options does nothing. Every client generates the same terrain from the seed, so only changed blocks travel over the network. With `save`, the server keeps changed blocks in a save file and loads them on the next start. Zones are flat areas without blocks for fixed buildings, for example a city: nobody can dig or build there, scripts neither.",
      "params": [
        {
          "name": "options",
          "type": "table",
          "description": "{ seed = integer, save = string?, zones = { { minX, minY, maxX, maxY, groundZ, blend?, floorZ? } }? }. seed: 0 to 4294967295. save: 1 to 32 characters a-z, 0-9, _ and -. Zone values are blocks; groundZ is the height of the fixed floor, blend (default 24) the width of the smooth edge, floorZ (default groundZ - 2) the top block of the hidden foundation inside the zone. Set floorZ lower to keep water or low ground of your own map visible. At most 8 zones."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true when the world is on. Errors explain wrong options."
        }
      ],
      "example": "EnableBlockWorld({\n  seed = 1337,\n  save = 'main',\n  zones = { { minX = -60, minY = -60, maxX = 60, maxY = 60, groundZ = 6 } },\n})"
    },
    {
      "name": "IsBlockWorldEnabled",
      "category": "blocks",
      "side": "shared",
      "provider": "blocks",
      "summary": "Check whether this server has a block world.",
      "description": "On clients it turns true once the world settings arrived from the server, shortly after joining.",
      "params": [],
      "returns": [
        {
          "type": "boolean",
          "description": "true when there is a block world."
        }
      ]
    },
    {
      "name": "GetBlockWorldSeed",
      "category": "blocks",
      "side": "server",
      "provider": "blocks",
      "summary": "Seed of the block world.",
      "params": [],
      "returns": [
        {
          "type": "integer|nil",
          "description": "The seed, nil without a block world."
        }
      ]
    },
    {
      "name": "GetBlock",
      "category": "blocks",
      "side": "shared",
      "provider": "blocks",
      "summary": "Block at a position.",
      "description": "Works everywhere in the world, also far from players. GetBlock(GetEntityCoords(ped)) is the block the feet of a character are in.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "description": "Block x (or a vector3 here and leave out y and z). Decimals are rounded down."
        },
        {
          "name": "y",
          "type": "number",
          "description": "Block y."
        },
        {
          "name": "z",
          "type": "number",
          "description": "Block z, up."
        }
      ],
      "returns": [
        {
          "type": "string|nil",
          "description": "Block name such as \"stone\" or \"air\", nil without a block world."
        }
      ],
      "example": "local pos = GetEntityCoords(GetPlayerPed(source))\nif GetBlock(pos.x, pos.y, pos.z - 1) == 'sand' then\n  print('on the beach')\nend"
    },
    {
      "name": "SetBlock",
      "category": "blocks",
      "side": "server",
      "provider": "blocks",
      "summary": "Change a block for every player.",
      "description": "Scripts may set any block, including ones players cannot place. Fires blockChanged. Changes inside zones, outside the world and on the bottom layer are refused.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "description": "Block x (or a vector3 here and leave out y and z). Decimals are rounded down."
        },
        {
          "name": "y",
          "type": "number",
          "description": "Block y."
        },
        {
          "name": "z",
          "type": "number",
          "description": "Block z, up."
        },
        {
          "name": "block",
          "type": "string",
          "description": "Block name, see GetBlockTypes. \"air\" removes the block."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "false if the position is protected or outside the world."
        }
      ],
      "example": "-- a small tower of bricks\nfor z = 10, 14 do\n  SetBlock(20, 30, z, 'brick')\nend"
    },
    {
      "name": "GetBlockSurfaceZ",
      "category": "blocks",
      "side": "shared",
      "provider": "blocks",
      "summary": "Height of the highest solid block in a column.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "description": "Block x (or a vector2/vector3 here and leave out y)."
        },
        {
          "name": "y",
          "type": "number",
          "description": "Block y."
        }
      ],
      "returns": [
        {
          "type": "integer|nil",
          "description": "z of the top solid block, nil without a block world. Its top face is at z + 1, so a character stands there with its middle at about z + 2."
        }
      ],
      "example": "local z = GetBlockSurfaceZ(100, 100)\nSetEntityCoords(GetPlayerPed(source), 100.5, 100.5, z + 2.0)"
    },
    {
      "name": "IsBlockProtected",
      "category": "blocks",
      "side": "shared",
      "provider": "blocks",
      "summary": "Check whether a position is inside a zone or outside the world, where nobody can change blocks.",
      "params": [
        {
          "name": "x",
          "type": "number",
          "description": "Block x (or a vector3 here and leave out y and z). Decimals are rounded down."
        },
        {
          "name": "y",
          "type": "number",
          "description": "Block y."
        },
        {
          "name": "z",
          "type": "number",
          "description": "Block z, up."
        }
      ],
      "returns": [
        {
          "type": "boolean",
          "description": "true where blocks cannot change."
        }
      ]
    },
    {
      "name": "GetBlockTypes",
      "category": "blocks",
      "side": "shared",
      "provider": "blocks",
      "summary": "All block types.",
      "description": "The list only grows. Blocks with breakable = false (deepstone) cannot be dug by players; placeable = false means players cannot place them (ores), but scripts can.",
      "params": [],
      "returns": [
        {
          "type": "table",
          "description": "List of { id = 'stone', name = 'Stone', solid = true, breakable = true, placeable = true  }, without air."
        }
      ]
    },
    {
      "name": "GetBlockTarget",
      "category": "blocks",
      "side": "client",
      "provider": "blocks",
      "summary": "Block under the crosshair within reach.",
      "params": [],
      "returns": [
        {
          "type": "integer|nil",
          "description": "x of the block, nil when the crosshair points at nothing within reach."
        },
        {
          "type": "integer",
          "description": "y"
        },
        {
          "type": "integer",
          "description": "z"
        },
        {
          "type": "integer",
          "description": "nx: side the crosshair points at (-1, 0 or 1). Placing puts the new block at x + nx."
        },
        {
          "type": "integer",
          "description": "ny"
        },
        {
          "type": "integer",
          "description": "nz"
        }
      ],
      "example": "local x, y, z = GetBlockTarget()\nif x then\n  print('looking at ' .. GetBlock(x, y, z))\nend"
    },
    {
      "name": "SetBlockHotbar",
      "category": "blocks",
      "side": "client",
      "provider": "blocks",
      "summary": "Blocks the player can place, in the slots of the hotbar.",
      "description": "The player picks a slot with the mouse wheel or keys 1 to 9 and places it with the right mouse button. An empty list switches placing off. Send the list from your server inventory with an event; the server still asks blockPlace before anything changes.",
      "params": [
        {
          "name": "slots",
          "type": "table",
          "description": "Up to 9 entries: a block name, or { block = 'planks', count = 12 } to show a number."
        }
      ],
      "returns": [],
      "example": "RegisterNetEvent('mygame:inventory', function(items)\n  SetBlockHotbar(items) -- { { block = 'stone', count = 32 }, ... }\nend)"
    },
    {
      "name": "GetBlockHotbarSlot",
      "category": "blocks",
      "side": "client",
      "provider": "blocks",
      "summary": "Selected slot of the hotbar.",
      "params": [],
      "returns": [
        {
          "type": "integer",
          "description": "Slot from 1, 0 when the hotbar is empty."
        }
      ]
    },
    {
      "name": "SetBlockControlsEnabled",
      "category": "blocks",
      "side": "client",
      "provider": "blocks",
      "summary": "Turn digging and placing with the mouse on or off for this player.",
      "description": "On by default. Turn it off while a menu is open or in areas where your game does not allow building.",
      "params": [
        {
          "name": "enabled",
          "type": "boolean",
          "description": "false turns the controls off."
        }
      ],
      "returns": []
    },
    {
      "name": "SetBlockHudVisible",
      "category": "blocks",
      "side": "client",
      "provider": "blocks",
      "summary": "Show or hide the built-in crosshair, block outline and hotbar.",
      "description": "Hide it when your resource draws its own hotbar with a web interface (NUI). The controls keep working.",
      "params": [
        {
          "name": "visible",
          "type": "boolean",
          "description": "false hides it."
        }
      ],
      "returns": []
    }
  ],
  "globals": [
    {
      "name": "source",
      "side": "shared",
      "type": "integer?",
      "summary": "ID of the player who sent the network event that is being handled.",
      "description": "Only set while a network event handler runs; nil otherwise. Copy it into a local variable before calling Wait, because another event can change it.",
      "example": "RegisterNetEvent('myshop:buy', function(itemId)\n  local player = source\n  Wait(100)\n  TriggerClientEvent('myshop:bought', player, itemId)\nend)"
    }
  ],
  "events": [
    {
      "name": "onResourceStart",
      "side": "shared",
      "summary": "A resource has started. Fired for every resource, including your own.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Name of the resource that started."
        }
      ],
      "example": "AddEventHandler('onResourceStart', function(name)\n  if name == GetCurrentResourceName() then\n    print('ready')\n  end\nend)"
    },
    {
      "name": "onResourceStop",
      "side": "shared",
      "summary": "A resource is stopping. Save data here.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Name of the resource that stops."
        }
      ],
      "example": "AddEventHandler('onResourceStop', function(name)\n  if name == GetCurrentResourceName() then\n    -- save state\n  end\nend)"
    },
    {
      "name": "playerJoining",
      "side": "server",
      "summary": "A player joined and was verified. `source` is the new player ID.",
      "params": [
        {
          "name": "oldId",
          "type": "string",
          "description": "Player ID as string (same as source)."
        }
      ],
      "example": "AddEventHandler('playerJoining', function()\n  print(GetPlayerName(source) .. ' joined')\nend)"
    },
    {
      "name": "playerDropped",
      "side": "server",
      "summary": "A player left. `source` is the player ID; GetPlayerName and GetPlayerIdentifiers still work inside the handler.",
      "params": [
        {
          "name": "reason",
          "type": "string",
          "description": "Why the player left."
        }
      ],
      "example": "AddEventHandler('playerDropped', function(reason)\n  print(GetPlayerName(source) .. ' left: ' .. reason)\nend)"
    },
    {
      "name": "gampeon:chatInput",
      "side": "client",
      "summary": "The player pressed Enter in the chat window of the game. The built-in chat resource handles it.",
      "params": [
        {
          "name": "text",
          "type": "string",
          "description": "What the player typed, at most 512 bytes, without control characters. Starts with / for commands."
        }
      ],
      "example": "AddEventHandler('gampeon:chatInput', function(text)\n  print('typed: ' .. text)\nend)"
    },
    {
      "name": "gampeon:nuiFocusReleased",
      "side": "client",
      "summary": "The player held Escape for 2 seconds and took mouse and keyboard back from a web interface. The page stays visible, so close your menu here.",
      "params": [
        {
          "name": "resourceName",
          "type": "string",
          "description": "Resource whose page had the focus."
        }
      ],
      "example": "AddEventHandler('gampeon:nuiFocusReleased', function(name)\n  if name == GetCurrentResourceName() then\n    SendNUIMessage({ action = 'close' })\n  end\nend)"
    },
    {
      "name": "blockBreak",
      "side": "server",
      "summary": "A player wants to dig a block. `source` is the player. Call CancelEvent to keep the block, before any Wait.",
      "params": [
        {
          "name": "x",
          "type": "integer",
          "description": "Block x."
        },
        {
          "name": "y",
          "type": "integer",
          "description": "Block y."
        },
        {
          "name": "z",
          "type": "integer",
          "description": "Block z."
        },
        {
          "name": "block",
          "type": "string",
          "description": "The block that would be removed."
        }
      ],
      "example": "AddEventHandler('blockBreak', function(x, y, z, block)\n  if block == 'crystal' and not HasPickaxe(source) then\n    CancelEvent()\n    return\n  end\n  AddItem(source, block, 1)\nend)"
    },
    {
      "name": "blockPlace",
      "side": "server",
      "summary": "A player wants to place a block. `source` is the player. Call CancelEvent to refuse, before any Wait.",
      "params": [
        {
          "name": "x",
          "type": "integer",
          "description": "Block x."
        },
        {
          "name": "y",
          "type": "integer",
          "description": "Block y."
        },
        {
          "name": "z",
          "type": "integer",
          "description": "Block z."
        },
        {
          "name": "block",
          "type": "string",
          "description": "The block from the selected hotbar slot."
        }
      ],
      "example": "AddEventHandler('blockPlace', function(x, y, z, block)\n  if not TakeItem(source, block, 1) then\n    CancelEvent()\n  end\nend)"
    },
    {
      "name": "blockChanged",
      "side": "server",
      "summary": "A block changed, by a player or by SetBlock. Arrives right after the change, in the next server tick.",
      "params": [
        {
          "name": "x",
          "type": "integer",
          "description": "Block x."
        },
        {
          "name": "y",
          "type": "integer",
          "description": "Block y."
        },
        {
          "name": "z",
          "type": "integer",
          "description": "Block z."
        },
        {
          "name": "oldBlock",
          "type": "string",
          "description": "Block before."
        },
        {
          "name": "newBlock",
          "type": "string",
          "description": "Block now."
        },
        {
          "name": "player",
          "type": "integer",
          "description": "Player ID, 0 for scripts."
        }
      ],
      "example": "AddEventHandler('blockChanged', function(x, y, z, old, new, player)\n  print(('%s: %s -> %s at %d %d %d'):format(player, old, new, x, y, z))\nend)"
    },
    {
      "name": "blockHotbarSelect",
      "side": "client",
      "summary": "The player picked another slot of the hotbar.",
      "params": [
        {
          "name": "slot",
          "type": "integer",
          "description": "Slot from 1, 0 when the hotbar is empty."
        },
        {
          "name": "block",
          "type": "string|nil",
          "description": "Block in that slot."
        }
      ],
      "example": "AddEventHandler('blockHotbarSelect', function(slot, block)\n  SendNUIMessage({ type = 'slot', slot = slot })\nend)"
    }
  ],
  "types": [
    {
      "name": "EventHandle",
      "summary": "Returned by AddEventHandler and RegisterNetEvent.",
      "fields": [
        {
          "name": "key",
          "type": "integer",
          "description": "Internal ID of the handler."
        },
        {
          "name": "name",
          "type": "string",
          "description": "Name of the event."
        }
      ]
    },
    {
      "name": "vector2",
      "summary": "Read-only vector with 2 components. Supports + - * /, # (length) and swizzles like v.xy.",
      "fields": [
        {
          "name": "x",
          "type": "number",
          "description": "x component."
        },
        {
          "name": "y",
          "type": "number",
          "description": "y component."
        }
      ]
    },
    {
      "name": "vector3",
      "summary": "Read-only vector with 3 components. Supports + - * /, # (length) and swizzles like v.xy.",
      "fields": [
        {
          "name": "x",
          "type": "number",
          "description": "x component."
        },
        {
          "name": "y",
          "type": "number",
          "description": "y component."
        },
        {
          "name": "z",
          "type": "number",
          "description": "z component."
        }
      ]
    },
    {
      "name": "vector4",
      "summary": "Read-only vector with 4 components. Supports + - * /, # (length) and swizzles like v.xy.",
      "fields": [
        {
          "name": "x",
          "type": "number",
          "description": "x component."
        },
        {
          "name": "y",
          "type": "number",
          "description": "y component."
        },
        {
          "name": "z",
          "type": "number",
          "description": "z component."
        },
        {
          "name": "w",
          "type": "number",
          "description": "w component."
        }
      ]
    },
    {
      "name": "promise",
      "summary": "Result that arrives later. Use p:resolve(...), p:reject(err), p:next(onResolved, onRejected) and Citizen.Await(p).",
      "fields": [
        {
          "name": "resolve",
          "type": "fun(self: promise, ...: any)",
          "description": "Fulfil the promise with values."
        },
        {
          "name": "reject",
          "type": "fun(self: promise, err: any)",
          "description": "Fail the promise."
        },
        {
          "name": "next",
          "type": "fun(self: promise, onResolved?: function, onRejected?: function): promise",
          "description": "Run a function when it is done."
        }
      ]
    }
  ],
  "manifest": {
    "files": [
      "fxmanifest.lua",
      "resource.lua"
    ],
    "summary": "Every resource is a folder with a manifest. GAMPEON reads the FiveM format directly.",
    "directives": [
      {
        "name": "shared_script",
        "aliases": [
          "shared_scripts"
        ],
        "summary": "Scripts that run on server and client, before the side-specific scripts."
      },
      {
        "name": "server_script",
        "aliases": [
          "server_scripts"
        ],
        "summary": "Scripts that run on the server."
      },
      {
        "name": "client_script",
        "aliases": [
          "client_scripts"
        ],
        "summary": "Scripts that run on the client of every player."
      },
      {
        "name": "file",
        "aliases": [
          "files"
        ],
        "summary": "Other files the resource ships, such as data or UI files."
      },
      {
        "name": "dependency",
        "aliases": [
          "dependencies"
        ],
        "summary": "Resources that must be running before this one starts."
      },
      {
        "name": "export",
        "aliases": [
          "exports"
        ],
        "summary": "Names of client exports (documentation for other resources)."
      },
      {
        "name": "server_export",
        "aliases": [
          "server_exports"
        ],
        "summary": "Names of server exports."
      },
      {
        "name": "ui_page",
        "summary": "HTML page of the resource, shown over the game (NUI). It is sent to players with the other client files; list its scripts, styles and images under files."
      }
    ],
    "patterns": "Paths can use wildcards (`server/*.lua`, `**/*.lua`) and refer to files of other resources with `@resource/path.lua`.",
    "ignored": [
      "fx_version",
      "game",
      "games",
      "lua54",
      "author",
      "description",
      "version",
      "name",
      "loadscreen",
      "loadscreen_manual_shutdown",
      "this_is_a_map",
      "provide",
      "data_file",
      "replace_level_meta",
      "before_level_meta",
      "after_level_meta",
      "use_experimental_fxv2_oal",
      "use_fxv2_oal",
      "repository",
      "node_version"
    ],
    "example": "fx_version 'cerulean'\ngame 'gta5' -- ignored by GAMPEON, kept for FiveM\n\nname 'myshop'\nversion '1.0.0'\n\nshared_script 'config.lua'\nserver_script 'server/*.lua'\nclient_script 'client/main.lua'"
  },
  "sandbox": {
    "summary": "Every resource runs in its own Lua 5.4 state with limits on memory and run time.",
    "libraries": [
      "base",
      "coroutine",
      "table",
      "string",
      "math",
      "utf8",
      "os (time, clock, date, difftime only)"
    ],
    "standardGlobals": [
      "_G",
      "_VERSION",
      "assert",
      "collectgarbage",
      "coroutine",
      "error",
      "getmetatable",
      "ipairs",
      "math",
      "next",
      "os",
      "pairs",
      "pcall",
      "rawequal",
      "rawget",
      "rawlen",
      "rawset",
      "select",
      "setmetatable",
      "string",
      "table",
      "tonumber",
      "tostring",
      "type",
      "utf8",
      "warn",
      "xpcall"
    ],
    "removed": [
      "io",
      "debug",
      "package",
      "require",
      "dofile",
      "loadfile",
      "module",
      "string.dump",
      "os.execute",
      "os.exit",
      "os.getenv",
      "os.remove",
      "os.rename",
      "os.tmpname",
      "os.setlocale"
    ],
    "limits": [
      {
        "name": "Memory",
        "value": "64 MB per resource (default)"
      },
      {
        "name": "Instructions",
        "value": "50 million per entry (event handler, thread step or export call). Long loops must call Wait."
      }
    ],
    "notNative": "GTA natives such as GetEntityCoords or CreateVehicle do not exist in GAMPEON and never will. Player functions (GetPlayers, GetPlayerIdentifiers, DropPlayer …) come from the GAMPEON server. A GAMPEON game API for characters, vehicles and the world comes with the engine plugin."
  }
}
