İçeriğe geç
Your first FiveM resource

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.

Bu dokümanlar şimdilik İngilizce.

Last updated

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).

Plays from youtube-nocookie.com after you click.

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:

Text
resources/
└─ [local]/
   └─ hello_world/
      ├─ fxmanifest.lua
      ├─ client.lua
      └─ server.lua

The 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:

resources/[local]/hello_world/fxmanifest.lua
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 uses rdr3).
  • client_script and server_script list 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

resources/[local]/hello_world/client.lua
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, source is always 0. args is a table of the words after the command.
  • TriggerEvent('chat:addMessage', ...) sends a local event to the chat resource, which shows the message.
  • false means anyone can use it. true would require the command.hello ACE permission.

4. A server command

resources/[local]/hello_world/server.lua
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:

Text
refresh
ensure hello_world
  • refresh rescans the resources folder, so FXServer notices your new folder.
  • ensure starts 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:

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:

Text
ensure hello_world

The 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:

Lua
print('hello_world client loaded')

ensure again and look for the line in F8. Now break something on purpose:

Lua
RegisterCommand('broken', function()
    local ped = nil
    print(ped.health) -- indexing nil
end, false)

Run /broken and F8 shows something like:

Text
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:

client.lua
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 / IsModelAVehicle check the model exists and is a vehicle. Natives accept a model name string and hash it for you.
  • RequestModel + HasModelLoaded loads the model. Wait(500) gives the game time, without it the loop would freeze the game.
  • PlayerPedId() is your character, GetEntityCoords its position (a vector3).
  • 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).
  • SetModelAsNoLongerNeeded frees 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:

Text
hello_world/
├─ fxmanifest.lua
├─ config.lua          shared settings
├─ client/
│  └─ main.lua
└─ server/
   └─ main.lua
fxmanifest.lua
fx_version 'cerulean'
game 'gta5'

shared_script 'config.lua'
client_scripts { 'client/*.lua' }
server_scripts { 'server/*.lua' }
config.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.
  • refresh finds new resources, ensure starts or restarts them.
  • Client scripts run in the game, server scripts in FXServer, and print goes 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.