Blog

FiveM Random Useful Things

Lua
FiveM
Coding
List

A list of random useful things related to FiveM

FiveM by CFX

Test Caution

Needs to be updated to QBox & OverExtended Info

FiveM Docs

Wikis/Libs/Useful Sites/Tools

Learning Resources

Info Dumps

Important Programs To Have

Visual Studio Code Addons

Basics To Learn In Lua

  • Get an understanding of the Syntax | https://learnxinyminutes.com/docs/lua/
  • Printing to console
  • Basic conditional statements (if/then/elseif/end)
  • Variables - local vs global
  • Loops - Types and which to use when
  • Functions - Why they’re important, when to use them, how to use parameters
  • Arrays/Table Objects - What the difference is when to use each
  • How to loop over arrays/tables

Basics To Learn For FiveM

Basics To Learn In QBCore

  • Navigating the Docs | https://bombayv.github.io/qbcore.github.io/#/README
  • Creating QBCore object variable client/server side
  • Creating QBCore commands server side
  • Getting PlayerData client and server side (has everything about a players character including job, gang, character info, money, name, licenses, metadata)
  • Creating a new item and make it usable/do something server side
  • Learn how to add/remove/detect money for a player server side
  • Learn how to add/remove/detect items for a player client and server side
  • Learn PolyZones and qb-target | 3rd eye interacting
  • Learn qb-menu | Dynamic menu creation
  • Learn qb-input | Dynamic input creation
  • Learn progressbar | Dynamic progress bar creation
  • QB-Core Important Events - OnPlayerLoaded/OnPlayerUnloaded/SetPlayerData - And how to use them to save PlayerData state locally instead of grabbing a new one every time you need it

Basics To Learn In QBox

FiveM/Lua Practices/Optimizations

  • Organize your resources into sub-folders | [vehicles] -> [cars], [boats], [planes] | etc.
  • Ensure on entire folders when possible ( ensure [qb-core] )
  • Dont use Citizen.CreateThread(), use CreateThread()
  • Dont use Citizen.Wait(), use Wait()
  • Use RegisterNetEvent("", function() end) for net events
  • Use AddEventHandler("", function() end) for non-net events
  • In server events, first set source equal to a constant (local src = source)
  • Set unused variables to _ ( for _,v in ipairs(table) )
  • Use PlayerPedId() instead of GetPlayerPed(-1)
  • Use lua math vector for distance checking #( vector3() - vector3() ) instead of GetDistanceBetweenCoords
  • Dynamically control thread wait times with variable
  • All while loops should run on a variable, not “while true do”
  • Never trust data from client on the server side. Double check everything. Injectors can send custom parameters to any server function!
  • All item/cash adds/removes should be done server side behind verification
  • Instead of table.insert(tableName, value) use tableName[#tableName+1] = value
  • Instead of ( if something ~= nil then ) use ( if something then ) - Catches all values but nil/false (can use NOT keyword before that too!)
  • Localize as many functions and variables as possible. Lua can read them faster.
  • Utilize the onPlayerLoaded, onJobUpdate, onGangUpdate, onPlayerUnloaded, setPlayerData and other event handlers to handle PlayerData state locally. It will only update when changed! Do this instead of asking QBCore for a fresh version every time you need it.
  • Use next(TABLEHERE) in your conditionals to see if a table is empty or not ( if next(data) then print(‘I have data!’) end )
  • con_miniconChannels script:* in f8 console will show console prints and errors on your screen

Array vs Object looping

-- Looping through an object table
Config.Table = {
    ['a'] = 1,
    ['b'] = 2,
    ['c'] = 3,
}

-- prints a,1 | b,2 | c,2
for key, value in pairs(Config.Table) do
    print(key, value)
end

--- Looping through an array table
Config.Array = { "d", "e", "f" }

-- prints 1,d | 2,e | 3,f
for index, value in ipairs(Config.Array) do
    print(index, value)
end

Properly Managing PlayerData State Locally

You can use these events to manage local PlayerData state properly. Instead of asking QBCore what your player data is every time something gets called, we just save it locally, and only change it when it gets changed! You can use this for items tracking as well if you only want to track items instead of the whole playerdata object.

local QBCore = exports['qb-core']:GetCoreObject()

-- Handles state on resource start (useful for live resource restarts). Will set to empty brackets {} if character not found yet.
local PlayerData = QBCore.Functions.GetPlayerData()

-- Handles state right when the player selects their character and location.
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    PlayerData = QBCore.Functions.GetPlayerData()
end)

-- Resets state on logout, in case of character change.
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
  PlayerData = {}
end)

-- Handles state when PlayerData is changed at all.
RegisterNetEvent('QBCore:Player:SetPlayerData', function(_PlayerData)
    PlayerData = _PlayerData
end)

Talking between the client and the UI.

Client -> UI client.lua

SendNUIMessage({
    action = "DoSomething",
    data = data
})

script.js

window.addEventListener("message", (event) => {
    const data = event.data;
    const action = data.action;

    switch (action) {
        case "DoSomething":
            // Do something
        case "DoNothing":
            // Do nothing
        default:
            return;
    }
});

UI -> Client script.js

$.post(`https://${GetParentResourceName()}/IDidAThing`,
        JSON.stringify({ data: 'Hi Dad', test: 'This is a test' })
    );

client.lua

RegisterNUICallback("IDidAThing", function(data, cb)
    print(data.data) -- Hi Dad
    print(data.test) -- This is a test
    cb("ok")
end)

OxMySQL

Helpful documentation. https://overextended.github.io/oxmysql/usage/

// Returns all columns for one row
MySQL.fetchSingle 

// Returns number of affected rows
MySQL.execute

// Return the insert id if valid
MySQL.insert 

// Generic function for any type of query, returns all data, inserted ids, affected rows, etc based on query
MySQL.query 
MySQL.fetchAll 
MySQL.prepare

// Attempts to execute multiple queries, and if all are valid, processes them and returns true
MySQL.transaction 

// Return first column for a single row
MySQL.fetchScalar

All of these can be MySQL.async or MySQL.sync.

Async will have a callback parameter that returns your result. Sync will hold code execution till done, and return your result from the request, so set a variable equal to the function to get it.

-- Sync
local result = MySQL.Async.fetchOne('SELECT tattoos FROM player_tattoos WHERE citizenid = ?', { Player.PlayerData.citizenid })

-- Async
MySQL.Async.fetchOne('SELECT tattoos FROM player_tattoos WHERE citizenid = ?', { Player.PlayerData.citizenid }, function(result)
    -- Stuff here
end)