[HowTo] FiveM lua Scripting Complete (not yet) guide

Goals of these lessons:

  • Give some guidelines for the ones who are just starting to code;

  • Provide help for those who are already experienced;

  • Help the ones who are struggling with some of fx framework functionality;

  • Encourage people to write clean, efficient and optimized code;

  • Spread my own knowledge and explore what I did not know yet.

Will make YouTube videos for all lessons and upload them to the

Playlist

Playlist:

1.0 - Creating first simple script

2.1 - Functions and variables

2.2 - Events and Server Scripts

2.3 - Understanding how events are triggered and executed

3.1 - Introduction to Threads, finding ingame coordinates


1.0 Creating first resource

In this lesson:

  • We will go through basics of FiveM scripts and their structure;
  • Touch a bit of how scripts work;
  • You will create a basic resource and script that prints Hello world! to client’s console.

For the lesson you will need:

  • A server on which you would have a full access to files and an ability to control resources (start, stop them);
  • Basic knowledge of server resource control;
  • Basic knowledge and skills in work with text editing software;
  • Any text editing application of your choice used for programming codes.

Introduction

FiveM servers are programmed using multiple different resources (scripts). It makes it simple to write, understand, share, import and manage. Most of the resources work independently, but they can use functions from other resources or tell them to do specific actions.
Resources can have server, client or both scripts. The difference between these 2 is that client scripts run on client machine and does not interrupt server (if it is not instructed to do so). While server scripts run on the server and complete tasks there unless a task on any of the client’s machine is triggered. Picture below represents that all client scripts run separately on each client machine and server scripts are running only on server.

A thing to note is that client can not trigger tasks on other client’s machine directly. It has to go through server. For example if client A wants to send a message to a client B it must be triggered through server task as obviously none of the clients have a direct connection to each other, they are all connected through server.

Starting from very basics. In this lesson you will learn how to create a basic client sided script that prints "hello world" to clients console and chat.

  1. In your server directory locate a \server-data\resources folder. Create a new folder called hello_world (it is important that this folder name would not have any spaces)

  2. Open the folder you just created and create two new text files client.lua and fxmanifest.lua. Your folder should look like this.

  3. Open fxmanifest.lua file that you just created using any text editor of your choice (I recommend Notepad++ or Virtual Studio Code)

  4. This file is used to provide specific information about the resource like scripts that it will be using, version, game, dependencies, it can also have version or description.

  5. What you should type in for now is game, fx_version, and script used in resource. Enter the fx_version:

fx_version 'bodacious'
  1. Specify the game that you will be using the resource for it can either be gta5 (Grand Theft Auto V) or rdr3 (Red Dead Redemption 3).
game 'gta5'
  1. Specify script(s) that you will use for the resource
client_scripts {
	'client.lua',
}
  1. Now when the manifest is created and script used is specified you can start creating the script itself. Open the client.lua file that you have created before.

  2. Simply write a line that will print a string to client’s console

print('Hello World!')
  1. Connect to your server if you haven’t yet. Press F8 and type refresh. This will refresh the list of your resources. A message with new resource should appear.

  2. In the console type in start hello_world. This will start the resource you have just created and start the actions you have given to it, which in this case is print Hello World!

If you want the resource to be started every time the server launches type in the same start hello_world in server.cfg file`.


2.0 Variables, functions, events, commands

You will get most value out of this course if you will follow lessons it in sequence.

In this lesson:

  • You will be introduced to variables and functions;

  • We will talk about what is the difference between events and functions;

  • You will create a script resource that uses variables and functions;

  • You will create an event and learn how to use them;

  • You will create server sided script;

For this lesson you will need:

  • A knowledge of how to create resources;

  • A knowledge of FiveM resource structure.

Introduction

Variables are holders of a specific data and can be used anywhere in it’s scope. Most of the times in FiveM scripts you will use local variable scope as global variables are only registered in scope of the same resource and the same machine. That means that variables registered on server sided script can not be directly accessed inside of client sided script.
Lua language does not require to specify a type of variable. The type is being assigned automatically, so often it is recommended to assign an empty or default value to the variables that are being registered.

local c = 5     -- creates variable c is holding an integer value of 5.
local d = 10.3  -- creates variable d is holding a floating point value of 10.3.
local s = ''    -- creates variable s is holding an empty string type variable.
local t = {1, 2, 2, 5}    -- creates a table called t.

Knowledge of functions, variables and their usage is inevitable skill that must be equired by everyone who wants to create any complex scripts, make them more clean, easier to read and edit. Any repetetive code can usually be replaced with functions and/or variables using use that is a good programming practice.

2.1 Creating and using simple functions and variables

Lets modify the resource created last time and register a command that uses variables and functions.

  1. Open client.lua file from hello_world resource that you have created on your last lesson.

  2. Register a function called PrintHello

function printHello()
end
  1. Now the function is empty and would not do anything if it was called. Let’s move the line that prints Hello World! to this function, so it prints the line whenever the function is called.
function printHello()
	print('Hello World!')
end
  1. Let’s add a variable that holds a number and call it age. And print it together with Hello World!
function printHello()
   local age = 5
   -- Prints Hello World! My age is 5
   print('Hello World! My age is ' .. age)
end

As you can see the variable age was used in the line that would be printed and the value that it has assigned would appear on screen. But! it is not being called and to call it we will use a command.
You can use double dots to connect strings or other variables to a single string.

  1. Register a command using RegisterCommand native function. You need to pass three arguments to this function: commandName (string), handlerFunction(function), restricted(boolean).
    commandName defines what user has to enter in order to trigger the code inside handlerFunction function and restricted tells if the command is restricted to any permission level. More about this native you can find here RegisterCommand - FiveM Natives @ Cfx.re Docs.
RegisterCommand('printhello', function(source, args)
end, false)
  1. Now let’s call the function PrintHello that we have recently created. To call a function you just need to enter it’s name and closed parentheses.
RegisterCommand('printhello', function(source, args)
	PrintHello()
end, false)
  1. Restart or start your resource in server and try using command /printhello. The line prints to the client’s console.

  2. Command function handler is holding two variables: source and args.
    source variable holds player’s that enters the command server id.
    args is holding an holds a table of words (not strings, you will see that later) that were enterd after command. Each word would be a seperate element of the table. That means that if player types in to the chat '/printhello my name is Mike’ the args table would look like this:
    args = {'my', 'name', 'is', 'Mike'}

  3. Let’s modify our PrintHello function and give it some parameters. You can enter any parameters names inside parentheses of function to be used locally in the function. Let’s add a parameter and call it name. This will hold a name that the client can enter.

function PrintHello(name)
  1. Change the PrintHello function for it to use the name variable. Let’s add it to the string that gets printed.
print('Hello, my name is ' .. name .. ' I am ' .. age)

Now you can use the function to print a name that client enters together with command. To implement this you need to pass the parameters over to the function when calling it. That you can do by entering them inside the parentheses on function call.
If your function accepts more than one parameter they have to be passed in the same order as they are registered.

  1. Create new variable, inside command handler, called inputName and assign an argument that client enters together with the command. As data of words does not have any data type assigned to it you need to use tostring function that converts data to string type.
local inputName = tostring(args[1])

As the args is a table of data you need to specify which of table’s element you want to use.
In this case if player would enter /printhello John Johnson only John would be assigned to inputName variable.

  1. Call PrintHello function passing inputName as an argument.
RegisterCommand('printhello', function(source, args)
	local inputName = tostring(args[1])
	printHello(inputName)
end, false)

Task
Modify the PrintHello function and add a new parameter called age. Create new variable called inputAge inside RegisterCommand handler, assign second argument to it (args[2]) and pass it as a parameter to PrintHello function.
Hints:
*Multiple function parameters have to be seperated using comma (,);
*You can use tonumber function if you want to convert the argument to integer.

2.2 Creating, using events and what is their difference between functions

Events are using functions as handlers, but events make it slightly more flexible, they can be triggered between different resources. Events registered in server script can be triggered from client and visa versa. They are used as main link between client and server scripts as they can be used to pass any data both ways server to client or client to server. Basically it can be used as a way to tell another machine to do specific tasks.
About other characteristics of events you will learn later.
Let’s create a small server sided script with an event that prints a line to all clients who are in server.

  1. Inside your hello_world resource folder create new file called server.lua. This file will be used to write code that will be executed in server side.

  2. Open fxmanifest.lua file that is inside your hello_world resource folder. Add the following code to specify that the file you just created will be used in server side.

server_scripts {
	'server.lua',
}
  1. Open server.lua file and register new event called hello_world:printToAll. It is recommended to write resource name in front of event name in order to avoid redundant names as events in other resources can be called the same.
RegisterServerEvent('hello_world:printToAll')
  1. Once event is registered you can add event handlers. Same as command - event has a handlerFunction which contains code of the event.
AddEventHandler('hello_world:printToAll', function()
end)

You can add event handlers anywhere, even in other resources and once the event gets triggered code in all of the handlers will be executed.

  1. Open your client.lua file, create new command called 'printtoall' and trigger server event using TriggerServerEvent function.
TriggerServerEvent('hello_world:printToAll')
  1. In order to add message to player’s chat you need to trigger an event called chatMessage which is registered in native chat resource and it can can accept 3 parameters author, color, text. author can be any String variable and is printed in front of text. This time we just gonna use author parameter and pass text that we want to print to it.
    When client event is triggered in server side code second parameter passed after event name must be player server ID, this time if we want to send it to all clients just use -1 which will trigger the event of all clients.
TriggerClientEvent('chatMessage', -1, "Hello world")
  1. Run the resource, try using command and all players will get the message to their chat.

Task
You are able to add multiple event handlers, even in other resources (only thing to keep in mind is that they have to be on the same side – server events on server side and client on client). Your task is to create a new resource called console_log and add new event handler of event you created.
Hints:

  • Create new resource called console_log and add server script;

  • Add another handler for hello_world:printToAll – event that you created today;

  • Write code that prints Player used 'printtoall' command in server console.
    print('Player used ‘printtoall’ command')

2.3 Understanding how events are triggered and executed

You will get most value out of this course if you will follow lessons it in sequence.

In this lesson:

  • You will see how events are being executed;

  • You will see how server sided scripts can be used to send data between clients;

  • You will see how functions are being differently executed from events;

  • You will create a resource that can be used by client to send message to another client.

Introduction

As mentioned before events can be used to send certain data between server as well as clients. This time you will create a resource that can be used to send a message from one player to another.
In this case some of code is not necessary for functionality, but this lesson is just for you to understand events more in depth.

1.Create new resource called message_sender and add client.lua file together with server.lua file.

  1. In client.lua file create new event called messages:sendMessageToPlayer. Add parameters clientId and message to it. These will hold client’s who receives message id and text of message. Make it trigger server event called messages:deliverMessageToPlayer and pass the same parameters. We will create this event later.
RegisterNetEvent('messages:sendMessageToPlayer')
AddEventHandler('messages:sendMessageToPlayer', function(clientId, message)
	TriggerServerEvent('messages:deliverMessageToPlayer', clientId, message)
end)
  1. Create another event called messages:showReceivedMessage. Make it accept message parameter and print it in chat. This will be used to display message for client that receives it.
RegisterNetEvent('messages:showReceivedMessage')
AddEventHandler('messages:showReceivedMessage', function(message)
	TriggerEvent('chatMessage', message)
end)
  1. Now create a command that will be used to send the message. Message should have a receiver id and text as arguments. Create new variable clientId that will hold the id of client who receives message.
RegisterCommand('dm', function(source, args)
	local clientId = tonumber(args[1])
end, false)
  1. As mentioned before args is holding each word separate as a table. For that we need to use table.concat method to connect the table elements to a single string. You need to pass name of table to it, followed by a string that will separate elements and id of element on which you will start connecting them. Since we using first element as player id, we skip it and start from second one.
RegisterCommand('dm', function(source, args)
	local clientId = tonumber(args[1])
	local message = table.concat(args, ' ', 2)
end, false)
>Command example - /dm 15 Hello, how are you?
>In this case player with id 15 will get message `Hello, how are you?` 

6, Now you can trigger event you created before and pass the variables as parameters. After the initialization of message and clientId trigger the event.

TriggerEvent('messages:sendMessageToPlayer', clientId, message)
  1. Open the server.lua file in this resource and create new event. And add the 2 parameters to it: clientId, message.
RegisterServerEvent('messages:deliverMessageToPlayer', function(clientId, message)
	
end)
  1. Trigger the client event that displays message and pass message to it as a parameter.
RegisterServerEvent('messages:deliverMessageToPlayer', function(clientId, message)
	TriggerClientEvent('messages:showReceivedMessage', message)
end)
  1. The problem in this case would be that server would not know who is the receiver and for which client it should trigger the event. As mentioned before before you can add another argument between event name and it’s parameters which will be taken as id of client for which the event will be triggered. Last time we used -1 which was triggering the event for all clients online. This time we have taget that holds the id of client we need. Let’s add it.
    TriggerClientEvent(‘messages:showReceivedMessage’, target, message)
  2. Now you can join server, start the resource and try sending message to another player or yourself if you are alone using id.

Summary
In the diagram below you can see how this resource will work. After command is typed in a server event is triggered with playerId and message parameters. When server event is triggered it looks for id of client which is equal to playerId and triggers it’s messages:deliverMessageToPlayer with message as a parameter.


3.0 Creating actions that are concitantly happening. Introduction to threads

You will get most value out of this course if you will follow lessons it in sequence.

In this lesson:

  • You will be introduced to threads and their capabilities;

  • You will create a script that runs a thread.

For this lesson you will need:

  • A knowledge of how to create resources;

  • A knowledge of how to use functions;

  • A knowledge of FiveM resource structure.

Introduction

Threads is one more useful tool that is used in lots of scipts and is simple to implement using Citizen framework. It gives you an ability to do pretty much any tasks continuously or repeatedly while it does not interrupt any other actions and not being interrupted itself.
Thread is also an essential tool used for many FiveM and GTA V mechanics. For example in order to draw a marker or text a function has to be called every frame it has to be show. So if you would like to show a marker you would have to put in a thread and while loop. Let’s try that.

3.1 Creating thread and finding coordinates
  1. Create new resource called my_thread, create fxmanifest.lua file inside your newly created resource folder, create client.lua file and add it to fxmanifest.

  2. To create thread you can use the following code:

Citizen.CreateThread(function()	
end)
  1. In order to create constantly shown marker you need to a while loop.

  2. While loops are loops that repeat code that’s inside it while condition is true. In this case we hardcoded it to true, so we will have the actions repeating for as long as player is in server. You also need to add Citizen.Wait(0) otherwise game will crash once loop starts.
    while loops are usually used when times of repeats is not known in advance.

Citizen.CreateThread(function()
	while true do
		Citizen.Wait(0)
	end 
end)
  1. We will create script that constantly prints player character’s coordinates on his console. All player controllable characters as well as non player characters (NPS’s) in GTA V are described as peds (pedestrians). In order to get any player’s coordinates we need to get the ped which is player controllable entity. Add the following line above your while loop.
Citizen.CreateThread(function()
	local ped = GetPlayerPed(-1)
	while true do
		Citizen.Wait(0)
	end
end)
  1. No when we have the player character id, we can use it to get it’s coordinates. Use GetEntityCoords native function and insert ped as an argument. Assign it to new variable playerCoords in your while loop block after Citizen.Wait(0) function.

We are initializing ped variable outside of while loop because it is static – player ped id does not change during time. And as you can see we are initializing playerCoords variable inside while loop as it might change and should be constantly refreshed. Not putting ped initialization inside while loop allows as to save some performance.

Citizen.CreateThread(function()
	local ped = GetPlayerPed(-1)
	while true do
		Citizen.Wait(0)
		local playerCoords = GetEntityCoords(ped)
		
	end
end)
  1. Now that we have player coordinates constantly initializing we are ready to print them. Use print and add playerCoords as an argument.
Citizen.CreateThread(function()
	local ped = GetPlayerPed(-1)
	while true do
		Citizen.Wait(0)
		local playerCoords = GetEntityCoords(ped)
		print(playerCoords)
	end
end)
  1. The only problem now is that it will be printed every frame. To avoid that put a higher wait value. Lets set it to 5000 which is in milliseconds, so that would translate to 5 seconds.
Citizen.Wait(5000)
  1. You can now start or restart the resource and open console using F8. You will see player coordinates printed every 5 seconds in vector3.
    vector3 is a data type that stores 3 float numbers. That’s what function GetEntityCoords returns. To get the numbers separately you could use playerCoords.x, playerCoords.y, playerCoords.z.

Task
Now when you know how to get player character’s position try drawing two markers using DrawMarker native function. One of them should be drawing a marker on some random place in map an another one directly on player.

Hints:

  • Remember that you will need to call the function every frame so set Citizen.Wait parameter to 0.

  • You can find what parameters you need as well as a code example the link (you should only focus on marker’s coordinates for now.) DrawMarker - FiveM Natives @ Cfx.re Docs

  • For static marker create new vector3 variable and set coordinates of your choice to use as a parameter for DrawMarker function.

3.2 Markers that trigger action

You will get most value out of this course if you will follow lessons it in sequence.

In this lesson:

  • You will write an algorithm that is quite popular for actions that markers trigger;

  • Use DrawMarker function to create markers;

  • Send chat message to player when he steps on marker.

For this lesson you will need:

  • A knowledge of how to create resources;

  • A knowledge of FiveM resource structure;

  • Ability to find desired coordinates in map.

Introduction

Usually markers are meant to have some functionality – when player steps inside them something happens. Markers do not do that by them selves, a separate code should be added that checks if players is in the same position as the marker and only then trigger the tasks.
There is a variety of algorithms and logic that can be used with markers. Today you will be shown one of the most popular and most effective ones. This one works pretty well and easy to write when there are more than one markers to be drawn by the same resource.

  1. Create new resource called marker_actions and add client.lua file to it.

  2. Add a vector3 that will be used for marker position. And set any coordinates of your choice

local markerPos = vector3(0.0, 0.0, 0.0)
  1. Inside of your client.lua file create a new thread with while loop inside that will be used for all marker logic. Also register and initialize ped variable which will be used to calculate distance between marker and player character.
Citizen.CreateThread(function()
	local ped = GetPlayerPed()
	while true do
		Citizen.Wait(0)
	end
end)
  1. Now let’s draw the marker using the coordinates. First of all let’s get player character coordinates and check how far the player is from the position of marker. We can use lua’s native functionality which gives us an ability to get distance between two coordinates written in vectors. Use #(firstVector – secondVector).
Citizen.CreateThread(function()
	local ped = GetPlayerPed()
	while true do
		Citizen.Wait(0)
		local playerCoords = GetEntityCoords(ped)
		local distance = #(playerCoords - markerPos)
	end
end)
  1. Now let’s check how far the player is from the position of marker and only display it if player is close enough (not more than 100 meters away). Also if player is further away than 100 meters there is no need to keep checking if he gets closer every frame, so we can add else block that will wait for 2 seconds. That means that if player is further away than 100 meters the distance will be checked every 2 seconds, otherwise every frame. That will let us to save some performance leaks.
if distance < 100.0 then
	    DrawMarker(1, pedCoords.x, pedCoords.y, pedCoords.z - 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 2.0, 2.0, 255, 128, 0, 50, false, true, 2, nil, nil, false)
	else
	    Citizen.Wait(2000)
end
  1. Connect to server, start the resource and you will be able to see the marker in coordinates you assigned for it unless you get further away than 100 meters.
  2. Right above if statement add new variable called isInMarker and assign false to it.
local isInMarker = false
  1. But it still does not have any functionality and nothing happens if you step inside it. That we will implement now. Inside if block add another if statement that will check if player is closer than 2 meters away from marker which would be inside the marker. If so assign true to isInMarker variable and false otherwise.
if distance < 100.0 then
	    DrawMarker(1, pedCoords.x, pedCoords.y, pedCoords.z - 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 2.0, 2.0, 255, 128, 0, 50, false, true, 2, nil, nil, false)
	if distance < 2.0 then
		isInMarker = true
	end
else
	Citizen.Wait(2000)
end
  1. Now at the very bottom of thread function we can implement any functionality for the marker. Let’s just print some message to player’s chat. Add the following at the bottom of thread function.
if isInMarker then
 	TriggerEvent('chatMessage', 'You are inside the marker')
end
  1. If you would join the server, start the resource you would see that you keep getting the message constantly, that happens because the chat message is triggered every frame, together with DrawMarker function
  2. To avoid it we need to check if client has already got the message. At the top of you file initialize new variable called HasAlreadyGotMessage and assign false to it.
HasAlreadyGotMessage = false
  1. Then add an else block to statement which checks if players is less than 2 meters away. That will reset HasAlreadyGotMessage to false, so after player moves away from marker and then steps back in he will get message again.
if distance < 2.0 then
	isInMarker = true
else
	HasAlreadyGotMessage = false
end
  1. And change if block which sends the message. This will check if player has already got the message and set HasAlreadyGotMessage to true, because the message will not be sent
if isInMarker and not HasAlreadyGotMessage then
	TriggerEvent('chatMessage', 'You are inside the marker')
	HasAlreadyGotMessage = true
end

4.0 Data types, arrays, tables - more in depth

You will get most value out of this course if you will follow lessons it in sequence.

In this chapter:

  • You will learn how to manipulate data in arrays, tables;
  • You will learn how to use different string functions to change string variables.

Introduction

You must be familiar with different data types if you want to be able to tidely work with more complicated resources. You need to know how to use arrays - manipulate their data, change it, how to access it.

4.1 Writing and accessing data from simple array

In this chapter:

  • You will learn how to write and access data from simple arrays.
  1. Create a new resource called my_array, create fxmanifest.lua and client.lua files and add the client.lua file to manifest.
  2. In client.lua file create a new array called NumberArray
local NuberArray = {}

Initiation of Array and table is exactly the same in lua language, but if you assign a key name for value it will become a table.

  1. Then put in some values into the array dividing them by a comma(,).
local NumberArray = {2, 6, 8, 5.2, 6}
  1. If you want to print out the array and see it’s values you could use json.encode:
print(json.encode(NumberArray)

json is a format that can store different parameters or data in a string. Useful to transfer data or store it in database.

  1. If you would like to access a single element from this array you could use [<elementKey>] at the end of array.
print(NumberArray[3])

In some programming languages keys for array values are assigned from 0 upwards in lua it is from 1 upwards.

  1. If you would like to access each value seperately an efficient way to use it would be with for loop. Using it we could calculate sum of all elements.
local sum = 0
for i = 1, #NumberArray do
    sum = sum + NumberArray[i]
end
print(sum)

If you want to get size of an array or table you can use # in front of it. We used it in this for loop so we go through each of the elements until the last one.

  1. Another way to write the for loop is by using pairs or ipairs. That would let us get a key and value of an array.
for key, value in ipairs(NumberArray) do
    print(key, value)
end

You will see key and value of each element printed to your console. Key is a id or place of element in the array and value will be what it holds.

  1. Now let’s create a table and assign names to array elements.
local MyTable = {one = 5, two = 8}
  1. You could use e.g. MyTable.one to access each element seperately, or for with pairs.
    for key, value in pairs(MyTable) do
    print(key, value)
    end

We couldn’t use ipairs to access elements that have a key (name) assigned to them, instead we use pairs.

  1. If you would like to assign new values for each element in the array using for loop use the following:
for key, value in pairs(MyArray)
    MyArray[key] = 5
end

Each array element will now have a value of 5


Work still in progress. Will be adding more lessons and covering more of FiveM scripting fun.


08/08/2020

  • Added lesson 2.3.

22/10/2020

  • Added chapter 4.0 and lesson 4.1.
49 Likes

Very in-depth LUA scripting tutorial, I like it. Wanting to see those YouTube videos, as well as more content in it, but so far it’s NOICE!

3 Likes

As someone who just started with FiveM scripting, this is very helpful.

Hope to see more!

2 Likes

Thank you for what you are doing, I appreciate the time you are taking to do this and make these videos.

Thanks, hope it helps to learn something :slight_smile:

2 Likes

Amazing videos and friendly supporting guy. I recommend anybody these videos if they want to get into fiveM and lua!

1 Like

This post still saves my life from time to time

2 Likes

Nice tutorial libary bro! <3

1 Like

I would be very happy if you continue to do fivem scripting lessons because other people dont explain it so good.

can i somehow have more markers for cars,safe etc.

1 Like