Skip to main content
TRACK B·PRODUCTION ENGINEERING·Verified June 2026 · Lua 5.4 · ox_lib 3.x
Learning with an AI assistant?
Copies this lesson plus 2026 ground rules (no lua54 'yes', Cfx.re Portal, correct callback signatures) as a ready-to-paste mentor prompt.

Handling basics and safe edits

Every vehicle in FiveM has a hidden file that controls how it drives: how fast it accelerates, how hard it brakes, how much grip the tires have. That file is handling.meta, and it is XML, not Lua. In this lesson you build a tiny streaming resource called qu_handling, change one safe value, test it, and prove you can revert it with one click in GitHub Desktop.

You'll ship
A safely edited handling.meta for one vehicle, tested in-game, with a Git backup you can revert.
Time
~25 minutes
You'll learn
What handling.meta does, where it lives, the safe-edit workflow, the four fields beginners touch, and how to revert when things go wrong.
Prereqs
Git and GitHub Desktop and basic resource structure.
BEFORE YOU START

Build it

Make the resource folder and the stream folder

You have an empty qu_handling resource with a stream/ folder ready for the handling file.

Handling files live inside a stream/ folder. The stream/ folder is a special FiveM folder: anything inside it replaces the matching base-game file without modifying the game itself. Using your operating system's file manager (Windows File Explorer or the VS Code Explorer panel), open your server's resources folder -- the same folder you set up in Module 01 (commonly ...\txData\<your-server>\resources\ on a txAdmin install). Create new folders and empty files there to match this layout:

code
resources/qu_handling/
  fxmanifest.lua
  stream/
    handling.meta
  client.lua

Leave the files empty for now. You fill them in the next steps.

Write fxmanifest.lua

FiveM knows this resource ships a handling override and a client script.

A file in stream/ is loaded automatically for models, but a data file like handling.meta needs an explicit data_file line so FiveM knows what kind of data it is. Open fxmanifest.lua and paste this exactly:

Note one thing that trips people up: streamed model files (.yft/.ytd) auto-mount from stream/ with no manifest entry, but a metadata file registered with data_file must ALSO be declared in a files {} block, or the mounter cannot locate the file and the override silently fails. That is why the manifest below lists stream/handling.meta twice, once in files {} and once in data_file. (The conventional home for such metas is a data/ folder, but stream/ works fine as long as the files entry points at it.)

code
fx_version 'cerulean'
game 'gta5'

author 'You'
description 'Vehicle handling overrides'
version '1.0.0'

-- Tells FiveM to load our handling override from stream/
files {
    'stream/handling.meta'
}

data_file 'HANDLING_FILE' 'stream/handling.meta'

client_script 'client.lua'

Do not add a lua54 'yes' line. Lua 5.4 is the only runtime on modern FXServer, so that line is deprecated and ignored.

Copy one complete, working handling entry

stream/handling.meta contains a complete entry from the vehicle you will test.

Do not hand-author a five-field handling block. A real CHandlingData item contains many related fields and may include sub-handling data; a partial sample is not a safe runnable file.

Start with an add-on vehicle that already works on your staging server. In that vehicle's vehicles.meta, find its <modelName>, then read the <handlingId> in the same item. In the package's handling.meta, find the complete <Item type="CHandlingData"> whose <handlingName> exactly matches that id. Copy the whole item, including every field and SubHandlingData, into the standard CHandlingDataMgr wrapper in stream/handling.meta.

The model name and handling id are often similar, but they are not required to match. Follow the actual vehicles.meta link instead of guessing. Also keep the original vehicle resource's load order documented: two resources defining the same handling id can override each other, so this staging override must be ensured after the vehicle resource and removed when the exercise is done.

Fields you will commonly inspect include:

  • fInitialDriveForce, which contributes to acceleration rather than representing horsepower directly.
  • fInitialDriveMaxFlatVel, which contributes to theoretical drive speed but does not guarantee the measured top speed by itself.
  • fBrakeForce, which contributes to braking strength.
  • fTractionCurveMax, one part of the tire-grip model that interacts with other traction fields.
  • fMass, which affects physics mass but should not be tuned in isolation.

There is no universal “normal range” that fits every class. Use the working vehicle's values as the baseline and change one field by 5 to 10 percent.

Commit the untouched baseline to Git

GitHub Desktop holds a clean snapshot you can revert to before any edit.

This is the step that makes every later edit safe. Open the resource folder in GitHub Desktop (see the GitHub Desktop lesson), stage qu_handling, and commit it with a clear message:

code
chore: handling baseline before brake edit

Now prove the revert works before you trust it. Add a temporary comment line near the top of handling.meta, save, and confirm GitHub Desktop shows exactly that one file changed. Then right-click the change and choose Discard changes. Reopen the file and confirm the comment is gone.

If you are not using Git yet, copy the whole folder and name the copy qu_handling_BACKUP before touching any XML.

Add a /testhandling command

One command spawns a test car in front of you so you can feel each edit fast.

Typing spawn menus every time is slow. Paste this helper into client.lua. It spawns a vehicle right in front of you and puts you in the driver seat.

Security note: the false at the end of RegisterCommand means this command is NOT permission-restricted, so on a live server ANY connected player could run /testhandling and spawn networked vehicles everyone sees. This is a staging/dev tool only. Keep qu_handling off your public server, or change the false to true and grant yourself the command.testhandling ACE permission, and remove the resource entirely when the exercise is done.

code
local testVehicle

RegisterCommand('testhandling', function(_, args)
    local model = args[1]
    if not model then
        print('[qu_handling] usage: /testhandling your_model_name')
        return
    end

    local hash = joaat(model)
    if not IsModelInCdimage(hash) or not IsModelAVehicle(hash) then
        print('[qu_handling] invalid vehicle model: ' .. model)
        return
    end

    RequestModel(hash)
    local deadline = GetGameTimer() + 5000
    while not HasModelLoaded(hash) do
        if GetGameTimer() > deadline then
            print('[qu_handling] model load timed out: ' .. model)
            return
        end
        Wait(0)
    end

    if testVehicle and DoesEntityExist(testVehicle) then
        DeleteEntity(testVehicle)
    end

    local ped = PlayerPedId()
    local coords = GetEntityCoords(ped)
    local heading = GetEntityHeading(ped)

    testVehicle = CreateVehicle(hash, coords.x, coords.y, coords.z, heading, true, false)
    SetPedIntoVehicle(ped, testVehicle, -1) -- -1 = driver seat
    SetModelAsNoLongerNeeded(hash) -- let the game unload the model later

    print('[qu_handling] spawned ' .. model .. ' for handling test')
end, false)

AddEventHandler('onResourceStop', function(resourceName)
    if resourceName == GetCurrentResourceName() and testVehicle and DoesEntityExist(testVehicle) then
        DeleteEntity(testVehicle)
    end
end)

Start the resource and spawn the baseline car

The car spawns with the unedited handling so you have something to compare against.

Open server.cfg and add this line:

code
ensure qu_handling

Save, then in the txAdmin Live Console run:

code
restart qu_handling

Join the server, then run the test command in-game chat. Replace your_model_name with the real spawn name of the vehicle whose handling entry you copied in Step 3 (for example, adder for the stock Adder). The word your_model_name is a placeholder; typing it literally will print invalid vehicle model: your_model_name because no such model exists:

code
/testhandling adder

You should drop into the vehicle you named (an Adder, if you used adder). Drive it, then brake. Remember how long it takes to stop, because the next step changes exactly that.

Change ONE value, restart, and feel the difference

The car brakes noticeably harder, and you know the brake field caused it.

Open stream/handling.meta and change only the brake force. Leave every other number alone:

code
<!-- Example: a 10 percent increase from an original 0.800000 -->
<fBrakeForce value="0.880000" />

The game reads handling only at resource start, so the edit does nothing until you restart. In the txAdmin Live Console run:

code
restart qu_handling

Calculate the new number from the value in your own complete entry; do not assume its original value is 0.800000. Spawn a fresh car with /testhandling your_model_name, drive the same route, then brake at the same marker. If the evidence is better, commit the exact model and percentage. Otherwise revert. Changing one value at a time is the key habit: if you change four numbers and the car feels wrong, you cannot identify the cause.

Keep reading the full lesson

Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.

Still ahead in this lesson
  • How it works
  • If something went wrong
  • What you can do now
  • Try it yourself

The remainder of Handling basics and safe edits is available to FiveM School members.