The style documentation for all don resources, helping maintain consistent & readable codebases.
This document is a work in progress and is subject to change. If you have any suggestions, please feel free to open an issue or a pull request.
- FiveM Docs
- Lua Reference Manual
- LuaStyleGuide
- Effective FiveM Lua
- FiveM Style Guide for Lua
- Lua Language Server
There is much debate over two or four spaces for indentation, while four spaces is the most common (amongst FiveM developers), two spaces is the most common amongst Lua developers.
The Programming in Lua book, the Lua Reference Manual, the Beginning Lua Programming book, the lua-users-wiki and the Project Error use two spaces for indentation. As such, to follow the lua standard, we will use two spaces for indentation.
Example
for _, v in pairs(table) do
print(v)
endWhitespace is instrumental in making code readable and should be used to separate elements.
Following English grammar,
,'s and all operators (except..) should have a space after them at least. However, both parentheses and brackets should not have a space before or after them.
Example
local table = {1, 2, 3}
local string = 'hello'..'world' or 'hello' .. 'world'
local number = 1 + 2 * 3 / 4 - 5
local function hello_world() return string endWell written code should be self-documenting however, there will be times where additional comments are needed.
Single line comments should have a space after the
--and multiline comments should have--after the closing]].Comments should endeavour to explain why the code is performing a function, opposed to what function it is performing.
Each region of code should be separated by a:
-------------------------------- [SUBREGION? REGION] --------------------------------Those regions follow the following format, with optonal subregions:
Region Sub Region FUNCTIONSLOCAL|GLOBAL|CLASSEVENTSHANDLER|NETWORKCALLBACKSTHREADSAnnotations should be used where reasonable and follow the Lua Language Server annotations.
Example
-------------------------------- LOCAL FUNCTIONS --------------------------------
---@param ped number The teleporting ped.
---@param coords {x: number, y: number, z: number}|vector3 The coordinates to teleport the ped to.
---@return boolean `true` if the player was teleported successfully.
local function teleport_ped(ped, coords)
SetEntityCoords(ped, coords.x, coords.y, coords.z)
return true
endLua 5.4 introduced the <const> keyword and as such only resources which fxmanifest sets lua54 'yes' should use <const>. This keyword is also limiting as once a variable is declared, it's immutable.
Constants should be named using
UPPER_SNAKE_CASE1, with global constants prefixed with an underscore.If available & applicable, the
<const>keyword should be used as well to indicate that the variable is a constant.
Example
_MY_GLOBAL_CONSTANT = 1
local MY_LOCAL_CONSTANT = 0One of Lua's strengths is its dynamic typing and as such, it is important to name variables descriptively & unambiguously.
As a general rule, a variable should be more descriptive as its scope gets larger. Using
iandkinside aforloop is fine, but using them to describe a global variable is not.Variables should be named using
snake_case, with global variables usingUPPER_SNAKE_CASE1.
booleanvariables should be prefixed with a verb, prefixed withis,has,can, etc.number,stringorvectorvariables should use a simple noun or adjective.indexorkeyvariables should useiork, respectively (moving towardsj,l, etc. if needed).unusedvariables should be named_.
Example
DEBUG_MODE = true
local is_player_dead = false
local player_ped = PlayerPedId()
local coords = GetEntityCoords(player_ped)
for i = 1, 10 do
print(i)
endObjects are a powerful tool in Lua, storing all lua data types, allowing them to be arrays, objects, classes and more. Due to their multi-faceted nature, there are some conventions we should follow.
Objects should be named using
PascalCaseif outside the scope of a function, andcamelCaseif inside the scope of a function.Arrays should be declared without number keys, unless needed.
An objects name should be a plural noun, describing what the object is (where applicable).
Example
local Players = {
{name = 'John', age = 21},
{name = 'Jane', age = 22}
}
local function print_players(players)
if type(players) ~= 'table' then return end
for i = 1, #players do
local player = players[i]
print(player.name)
end
endThe Lua has 3 hidden beauties' that make it a powerful language. First is that functions are first class citizens, second is that its scope is global by default and the third is its automatic garbage collection, this makes uniformity in naming functions essential to let these features shine.
Local functions should be named using
snake_case, with global functions should be named usingPascalCase.Functions should only be completing a single task and their name should reflect this.
Functions should be decalred in precedence of their use, with all local functions at the top of the file, followed by global functions and then class functions.
Example
local TeleportedPlayers = {}
---@param ped integer The teleporting ped.
---@param target integer The target ped.
---@return boolean `true` if the player was teleported successfully.
local function teleport_to_ped(ped, target)
local player_id = GetPlayerFromServerId(ped)
local coords = GetEntityCoords(target)
SetEntityCoords(ped, coords.x, coords.y, coords.z)
TeleportedPlayers[player_id] = true
return true
end
---@return boolean? `true` if the player has teleported.
function HasPlayerTeleported(player)
return TeleportedPlayers[player]
endTip
Most functions should be local, as they are not intended to be called from outside the file.
Whilst Lua doesn't have classes, it does have objects and these objects can be manipulated to act like classes.
Classes should be named using
PascalCaseand the methods should be named usingflatcase.Classes should be prefixed with a
Cto indicate that it is a class.
Example
local CPlayer = {}
CPlayer.__index = CPlayer
function CPlayer.new()
local self = setmetatable({}, CPlayer)
return self
end
function CPlayer:teleportped(target)
local player_id = GetPlayerFromServerId(self.ped)
local coords = GetEntityCoords(target)
SetEntityCoords(self.ped, coords.x, coords.y, coords.z)
TeleportedPlayers[player_id] = true
return true
endNote
Modules/packages are typically made with classes or class-like architecture however, are named using flatcase and should not be prefixed with C2. This is to imitate the libraries that Lua uses, such as math, string, etc.
---@class CIntervalEvents are critical in FiveM as they are used to communicate between contexts by triggering a function in another context.
Events should be named using
PascalCaseand should be prefixed with it's file name, as well as the context of the event:'[resource]:[context]:EventName'When declaring an event, there are three options depending on use and context:
AddEventHandlerfor local events (client <---> client or server <---> server).RegisterNetEventfor Network events declared on the client.RegisterServerEventfor Network events declared on the server.The handler function for each of the above should be used when declaring and if possible, use a local function to handle the event.
Example
---@param reason string The reason for kicking the player.
---@param source integer The player\'s server ID.
---@param resource string The resource name.
local function kick_player(reason, source, resource)
DropPlayer(source, reason)
print(('Player %s was kicked from %s for %s'):format(source, resource, reason))
end
RegisterServerEvent('don:server:KickPlayer', kick_player)Files are the main building blocks of any script or software, making knowing what you're working with all the more important.
Files should be named using
snake_caseand they should either:
- Be housed in a
client,serverorsharedfolder, and named accordingly.- If not housed in a folder, be prefixed with
cl_,sv_orsh_to indicate the context of the file.
Git is a powerful tool, and when used correctly can make the development process much smoother.
Commits are the bread & butter of git and as such should be descriptive & concise.
Commits follow the Conventional Commits standard, and should be prefixed with one of the following:
Type Usage buildChanges that affect the build system or external dependencies (example scopes: gulp, broccoli, npm). choreChanges that don't modify src or test files. docsDocumentation only changes. featA new feature. fixA bug fix. perfA code change that improves performance. refactorA code change that neither fixes a bug nor adds a feature. revertReverts a previous commit. styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc). testAdding missing tests or correcting existing tests. The commit message should be in the following format:
<type>(<scope>): <description>.
typeis the type of commit, as described above.scopeis the scope of the commit, and is optional.
- Where scope is the relative path to the file that the commit is affecting.
descriptionis a short description of the commit.If any changes introduce a breaking change, the commit should has its type (and scope) suffixed with a
!and the a footer with the breaking change.> ![CAUTION] > BREAKING CHANGE: <description>
Example
feat(client): Add TeleportToPed Fn
Add a function to teleport the player to a ped.revert(client)!: Revert Add TeleportToPed Fn
> ![CAUTION]
> BREAKING CHANGE: This reverts commit 123456.Versioning is a critical part of any software and as such should be done correctly.
Versioning should follow the Semantic Versioning standard and should be in the following format:
MAJOR.MINOR.PATCH.
MAJORversion when you make incompatible API changes.MINORversion when you add functionality in a backwards compatible manner.PATCHversion when you make backwards compatible bug fixes.Pre-release versions should be suffixed with a
-and the pre-release version and build metadata should be suffixed with a+and the build metadata.The version should be stored in the
fxmanifest.luafile and should be updated with each release.
Example
fx_version 'cerulean'
game 'gta5'
author 'DonHulieo'
description 'A Style Guide for Lua in FiveM'
url 'https://github.com/DonHulieo/lua-style-guide'
version '1.0.0'Footnotes
-
As a fun tidbit, conventionally named SCREAMING_SNAKE_CASE. ↩ ↩2
-
This is usually still done when naming the main object in annotation, as it is a class-like object. ie. ↩