Your first FiveM resource
Build a FiveM resource from scratch: folder, fxmanifest.lua, client and server scripts, a /hello command, a car spawner, ensure and restart, and reading the F8 console.
In this tutorial you build a resource with a client script and a server script, add chat commands, spawn a car, and learn the edit, ensure, test loop you’ll use every day. You need a local server (see Windows setup) and VS Code (see Start developing).
1. Create the folder
Inside your server data folder, go to resources. Create a category folder [local] for your own work if it doesn’t exist, and inside it a folder for the resource:
resources/
└─ [local]/
└─ hello_world/
├─ fxmanifest.lua
├─ client.lua
└─ server.luaThe folder name is the resource name. Use lowercase, no spaces: hello_world, not Hello World.
2. Write the manifest
fxmanifest.lua tells FXServer what the resource is and which files run where:
fx_version 'cerulean'
game 'gta5'
name 'hello_world'
author 'You'
description 'My first FiveM resource'
version '1.0.0'
client_script 'client.lua'
server_script 'server.lua'fx_version 'cerulean'is the current manifest version. Always use it for new resources.game 'gta5'says this is for FiveM (RedM usesrdr3).client_scriptandserver_scriptlist the files for each side. Use the plural forms with{ }for several files, and globs like'client/*.lua'work.
You can generate manifests with the fxmanifest Generator, which also warns about common mistakes.
3. A client command
RegisterCommand('hello', function(source, args, rawCommand)
local name = args[1] or 'world'
TriggerEvent('chat:addMessage', {
color = { 0, 200, 255 },
args = { 'Hello', ('Hello, %s!'):format(name) },
})
end, false)RegisterCommand(name, handler, restricted)creates/hello. On the client,sourceis always 0.argsis a table of the words after the command.TriggerEvent('chat:addMessage', ...)sends a local event to the chat resource, which shows the message.falsemeans anyone can use it.truewould require thecommand.helloACE permission.
4. A server command
RegisterCommand('whoami', function(source, args)
if source == 0 then
print('This command was run from the server console.')
return
end
local name = GetPlayerName(source)
local license = GetPlayerIdentifierByType(source, 'license')
print(('[hello_world] %s (id %d) asked who they are'):format(name, source))
TriggerClientEvent('chat:addMessage', source, {
args = { 'Server', ('You are %s, player id %d, %s'):format(name, source, license or 'no license') },
})
end, false)On the server, source is the server ID of the player who typed the command, or 0 for the console. print goes to the server console (txAdmin Live Console), and TriggerClientEvent(name, target, ...) sends an event to one player (-1 sends to everyone).
5. Start it
In the txAdmin Live Console (or the server window), type:
refresh
ensure hello_worldrefreshrescans the resources folder, so FXServer notices your new folder.ensurestarts it (or restarts it if it’s running).
You should see Started resource hello_world. To start it on every boot, add it to server.cfg:
ensure hello_world
# or, to start everything in [local]:
ensure [local]6. Test in game
Connect to your server, press T to open chat, and type /hello or /hello Bob. Then /whoami. You should see both messages, and the server console shows the print line.
7. The edit loop
Change the message in client.lua, save, then in the server console:
ensure hello_worldThe resource restarts and every connected client reloads it. Test again. This loop (edit, save, ensure, test) is how you’ll work. You only need refresh again when you add a resource or change fxmanifest.lua.
Tip
You can also type ensure hello_world in the F8 console in game if your account has permission to run server commands (the txAdmin master admin does, through add_ace group.admin command allow).
8. Read the F8 console
Press F8 in game. This is the client console. Client print output and client errors show here. Add a print to client.lua:
print('hello_world client loaded')ensure again and look for the line in F8. Now break something on purpose:
RegisterCommand('broken', function()
local ped = nil
print(ped.health) -- indexing nil
end, false)Run /broken and F8 shows something like:
SCRIPT ERROR: @hello_world/client.lua:14: attempt to index a nil value (local 'ped')It tells you the resource, the file, the line, and what went wrong. Server errors look the same in the server console. More in Debugging.
9. Spawn a car
Now use some game natives. This is the example from the official “creating your first script” guide:
RegisterCommand('car', function(source, args)
local vehicleName = args[1] or 'adder'
if not IsModelInCdimage(vehicleName) or not IsModelAVehicle(vehicleName) then
TriggerEvent('chat:addMessage', { args = { 'Invalid vehicle model: ' .. vehicleName } })
return
end
RequestModel(vehicleName)
while not HasModelLoaded(vehicleName) do
Wait(500)
end
local playerPed = PlayerPedId()
local pos = GetEntityCoords(playerPed)
local vehicle = CreateVehicle(vehicleName, pos.x, pos.y, pos.z, GetEntityHeading(playerPed), true, false)
SetPedIntoVehicle(playerPed, vehicle, -1)
SetEntityAsNoLongerNeeded(vehicle)
SetModelAsNoLongerNeeded(vehicleName)
TriggerEvent('chat:addMessage', { args = { 'Enjoy your new ' .. vehicleName .. '!' } })
end, false)What’s going on:
IsModelInCdimage/IsModelAVehiclecheck the model exists and is a vehicle. Natives accept a model name string and hash it for you.RequestModel+HasModelLoadedloads the model.Wait(500)gives the game time, without it the loop would freeze the game.PlayerPedId()is your character,GetEntityCoordsits position (avector3).CreateVehicle(model, x, y, z, heading, isNetwork, netMissionEntity)spawns it, networked so others see it.SetPedIntoVehicle(ped, vehicle, -1)seats you as the driver (-1).SetModelAsNoLongerNeededfrees the model from memory.
Try /car sultan or /car zentorno. Find spawn names in the Model & Hash Browser.
Warning
Spawning vehicles from the client is fine for a learning server, but on a real server anyone could trigger it. Real resources spawn vehicles on the server, after checking permissions and money. See Client, server and events.
10. Organise as it grows
When the resource gets bigger, split it:
hello_world/
├─ fxmanifest.lua
├─ config.lua shared settings
├─ client/
│ └─ main.lua
└─ server/
└─ main.luafx_version 'cerulean'
game 'gta5'
shared_script 'config.lua'
client_scripts { 'client/*.lua' }
server_scripts { 'server/*.lua' }Config = {}
Config.DefaultCar = 'adder'
Config.Greeting = 'Hello'Shared scripts load first, on both sides, so Config is available in client and server files.
What you learned
- A resource is a folder with
fxmanifest.lua. refreshfinds new resources,ensurestarts or restarts them.- Client scripts run in the game, server scripts in FXServer, and
printgoes to F8 or the server console. - Errors show file and line.
- Natives do the game work, and loading models needs a wait loop.
Next: Client, server and events.
