Creating Custom FiveM Scripts - A Complete Guide
Want to add unique features to your FiveM server? This guide teaches you how to create custom scripts from scratch, covering everything from basic Lua to advanced FiveM concepts.
Understanding FiveM Scripting
FiveM uses Lua for server-side and client-side scripts. Understanding the difference is crucial:
| Type | Runs On | Purpose |
|---|---|---|
| Client | Player's game | UI, player actions, visuals |
| Server | Server machine | Database, sync, validation |
| Shared | Both | Config, shared functions |
Lua Basics for FiveM
Variables & Data Types
-- Local variables (use these!)
local playerName = "John"
local playerAge = 25
local isOnline = true
local inventory = {"item1", "item2", "item3"}
-- Tables (Lua's arrays/objects)
local playerData = {
name = "John",
money = 5000,
job = "police"
}
-- Accessing table data
print(playerData.name) -- "John"
print(playerData["money"]) -- 5000
Functions
-- Basic function
local function greet(name)
return "Hello, " .. name .. "!"
end
-- Callback pattern
local function getPlayerData(source, callback)
local data = GetPlayerData(source)
callback(data)
end
-- Usage
getPlayerData(1, function(data)
print(data.name)
end)
Control Flow
-- If/else
if playerData.job == "police" then
print("Officer on duty")
elseif playerData.job == "medic" then
print("Medic on duty")
else
print("Civilian")
end
-- Loops
for i = 1, 10 do
print(i)
end
for key, value in pairs(playerData) do
print(key, value)
end
-- While loop
local count = 0
while count < 5 do
count = count + 1
end
---
Creating Your First Resource
Resource Structure
my-first-resource/
├── fxmanifest.lua
├── client.lua
├── server.lua
└── config.lua
fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'Your Name'
description 'My first FiveM resource'
version '1.0.0'
shared_scripts {
'config.lua'
}
client_scripts {
'client.lua'
}
server_scripts {
'server.lua'
}
config.lua (Shared)
Config = {}
Config.Debug = true
Config.ServerName = "My RP Server"
Config.Locations = {
police = {
{x = 425.13, y = -979.56, z = 30.71, name = "Mission Row"}
},
hospital = {
{x = 298.67, y = -584.33, z = 43.26, name = "Pillbox Hill"}
}
}
Config.NotifyTime = 5000 -- milliseconds
---
Client-Side Scripting
Basic Client Script
Create client.lua:
local isMenuOpen = false
-- Register a command
RegisterCommand('mymenu', function()
if isMenuOpen then
CloseMenu()
else
OpenMenu()
end
end, false)
-- Register key mapping
RegisterKeyMapping('mymenu', 'Open My Menu', 'keyboard', 'F5')
function OpenMenu()
isMenuOpen = true
SetNuiFocus(true, true) -- Enable mouse
SendNUIMessage({
action = 'open',
title = 'My Menu'
})
end
function CloseMenu()
isMenuOpen = false
SetNuiFocus(false, false) -- Disable mouse
SendNUIMessage({
action = 'close'
})
end
Using Native Functions
FiveM provides access to GTA V native functions:
-- Get player ped
local playerPed = PlayerPedId()
-- Get player coordinates
local coords = GetEntityCoords(playerPed)
print(coords.x, coords.y, coords.z)
-- Spawn a vehicle
local function SpawnVehicle(model)
local hash = GetHashKey(model)
-- Request model
RequestModel(hash)
while not HasModelLoaded(hash) do
Wait(100)
end
-- Get player position
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
local heading = GetEntityHeading(playerPed)
-- Create vehicle
local vehicle = CreateVehicle(hash, coords.x, coords.y, coords.z, heading, true, false)
-- Put player in vehicle
SetPedIntoVehicle(playerPed, vehicle, -1)
-- Set as mission entity
SetEntityAsMissionEntity(vehicle, true, true)
-- Clean up
SetModelAsNoLongerNeeded(hash)
end
-- Register command
RegisterCommand('car', function(source, args)
local model = args[1] or 'adder'
SpawnVehicle(model)
end, false)
Creating Blips
local function CreateBlip(coords, sprite, color, scale, name)
local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
SetBlipSprite(blip, sprite)
SetBlipColour(blip, color)
SetBlipScale(blip, scale)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(name)
EndTextCommandSetBlipName(blip)
return blip
end
-- Create a police station blip
CreateThread(function()
for _, location in pairs(Config.Locations.police) do
CreateBlip(
vector3(location.x, location.y, location.z),
60, -- sprite
3, -- color (blue)
0.8, -- scale
location.name
)
end
end)
3D Text Markers
local function Draw3DText(x, y, z, text)
local onScreen, _x, _y = World3dToScreen2d(x, y, z)
if onScreen then
SetTextScale(0.35, 0.35)
SetTextFont(4)
SetTextProportional(1)
SetTextColour(255, 255, 255, 215)
SetTextEntry("STRING")
SetTextCentre(1)
AddTextComponentString(text)
DrawText(_x, _y)
end
end
CreateThread(function()
while true do
local sleep = 1000
for _, location in pairs(Config.Locations.police) do
local playerCoords = GetEntityCoords(PlayerPedId())
local dist = #(playerCoords - vector3(location.x, location.y, location.z))
if dist < 10 then
sleep = 0
Draw3DText(location.x, location.y, location.z + 1, "~b~" .. location.name)
if dist < 2 then
Draw3DText(location.x, location.y, location.z + 0.5, "~g~Press ~y~E ~g~to enter")
end
end
end
Wait(sleep)
end
end)
---
Server-Side Scripting
Basic Server Script
Create server.lua:
-- Player data storage
local Players = {}
-- On player connecting
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
local source = source
local identifier = GetPlayerIdentifierByType(source, 'license')
print('Player connecting: ' .. name .. ' (' .. identifier .. ')')
end)
-- On player spawning
RegisterNetEvent('playerSpawned')
AddEventHandler('playerSpawned', function()
local source = source
-- Initialize player data
Players[source] = {
money = 1000,
job = 'unemployed',
inventory = {}
}
-- Send data to client
TriggerClientEvent('updatePlayerData', source, Players[source])
end)
-- Player disconnecting
AddEventHandler('playerDropped', function(reason)
local source = source
Players[source] = nil
print('Player disconnected: ' .. reason)
end)
Server Commands
-- Admin command
RegisterCommand('givemoney', function(source, args, rawCommand)
-- Check if source is console (source = 0) or has admin permission
if source == 0 then
print('Console cannot use this command')
return
end
local targetId = tonumber(args[1])
local amount = tonumber(args[2])
if not targetId or not amount then
print('Usage: /givemoney [id] [amount]')
return
end
-- Add money to target
if Players[targetId] then
Players[targetId].money = Players[targetId].money + amount
TriggerClientEvent('updatePlayerData', targetId, Players[targetId])
TriggerClientEvent('chatMessage', source, 'SERVER', {0, 255, 0},
'Gave %%CODEBLOCK_11%%#x27; .. amount .. ' to player ' .. targetId)
else
TriggerClientEvent('chatMessage', source, 'SERVER', {255, 0, 0},
'Player not found')
end
end, false)
---
Client-Server Communication
Triggering Events
Client → Server: %%CODEBLOCK_12%%
Server → Client: %%CODEBLOCK_13%%
Secure Server Callbacks
%%CODEBLOCK_14%%
---
Creating a Job System
Server-Side Job Handler
%%CODEBLOCK_15%%
%%CODEBLOCK_16%%
---
Creating UI with NUI
HTML/CSS/JS Interface
Create html/index.html:
%%CODEBLOCK_17%%
Create html/style.css:
%%CODEBLOCK_18%%
Create html/script.js:
%%CODEBLOCK_19%%
Update fxmanifest.lua
%%CODEBLOCK_20%%
Client-Side NUI Handler
%%CODEBLOCK_21%%
---
Exporting Functions
Make your resource usable by other resources:
%%CODEBLOCK_22%%
Using Exports from Other Resources
%%CODEBLOCK_23%%
---
Debugging Tips
Debug Commands
%%CODEBLOCK_24%%
Error Handling
%%CODEBLOCK_25%%
---
Best Practices
resourceName:eventName---
Conclusion
You now have the foundation to create custom FiveM scripts. Start small, test often, and gradually build more complex systems.
Next steps:
- >Create a simple job system
- >Add a phone UI
- >Build an inventory system
- >Integrate with a database