ScriptsAboutBlogToolsKnowledge BaseReviewsFAQBasketDocsSupport
Guides

How to Create a Safe Zone in FiveM (In-Game Creator or Config)

Safe zones are areas where combat is disabled โ€” spawn points, hospitals, shops, and anywhere players getting killed would break the experience. This guide covers how zone detection actually works, the code that disables combat properly, and why the in-game creator approach beats hand-editing coordinates.

What a safe zone does

A safe zone is a defined area where the server enforces protection โ€” typically disabling weapons, preventing damage, and showing the player an indicator. When a player enters, combat is neutralised; when they leave, normal rules resume.

Common locations:

  • Spawn / new player areas โ€” so fresh players are not spawn-killed
  • Hospitals โ€” so injured players can recover
  • Shops and markets โ€” to keep commerce civil
  • Event or hub areas โ€” wherever fighting would break the experience

Zone detection: the part that decides your performance

This is where safe zone scripts are won or lost. There are three ways to know a player is inside a zone, and they are not equal.

The bad way โ€” a distance loop every frame:

-- Do not do this
CreateThread(function()
    while true do
        local coords = GetEntityCoords(PlayerPedId())
        for _, zone in pairs(Config.Zones) do
            if #(coords - zone.center) < zone.radius then
                -- inside
            end
        end
        Wait(0)
    end
end)

That runs every frame, for every zone, forever. Ten zones is survivable. Forty is a measurable frame cost on every client, whether or not anyone is near one. This pattern is one of the most common causes of unexplained client lag, and it is why safe zones have a reputation for being heavy.

The acceptable way โ€” the same loop with a variable wait:

CreateThread(function()
    while true do
        local sleep = 1000
        local coords = GetEntityCoords(PlayerPedId())
        for _, zone in pairs(Config.Zones) do
            local dist = #(coords - zone.center)
            if dist < zone.radius + 50 then
                sleep = 0  -- only tighten up when actually near a zone
            end
        end
        Wait(sleep)
    end
end)

Idle cost drops to almost nothing because the thread sleeps a full second when the player is nowhere near a zone.

The right way โ€” let ox_lib handle it:

lib.zones.poly({
    points = {
        vec3(150.2, -1040.5, 29.4),
        vec3(158.7, -1040.5, 29.4),
        vec3(158.7, -1032.1, 29.4),
        vec3(150.2, -1032.1, 29.4),
    },
    thickness = 4.0,
    onEnter = function()
        TriggerEvent('safezone:enter')
    end,
    onExit = function()
        TriggerEvent('safezone:exit')
    end,
})

ox_lib's zone system handles the spatial checks efficiently and calls you only on transitions. You write no loop at all. If you are already running the ox stack, this is the correct answer.

Disabling combat properly

Entering the zone is only half of it. Here is what actually needs to happen:

local inZone = false

RegisterNetEvent('safezone:enter', function()
    inZone = true
    local ped = PlayerPedId()
    SetEntityInvincible(ped, true)
    SetCanAttackFriendly(ped, false, false)
    NetworkSetFriendlyFireOption(false)
    -- holster whatever they are carrying
    SetCurrentPedWeapon(ped, `WEAPON_UNARMED`, true)
end)

RegisterNetEvent('safezone:exit', function()
    inZone = false
    local ped = PlayerPedId()
    SetEntityInvincible(ped, false)
    SetCanAttackFriendly(ped, true, true)
    NetworkSetFriendlyFireOption(true)
end)

-- firing has to be suppressed continuously while inside
CreateThread(function()
    while true do
        if inZone then
            DisablePlayerFiring(PlayerId(), true)
            SetPlayerCanDoDriveBy(PlayerId(), false)
            Wait(0)
        else
            Wait(500)
        end
    end
end)

Note the shape of that last thread: Wait(0) only while inside a zone, Wait(500) otherwise. DisablePlayerFiring has to be called every frame to work, but only when it matters.

Reference pages for SetEntityInvincible and PlayerPedId cover those natives in detail.

The exploit nobody mentions

Everything above runs on the client, and the client can be modified.

A player with a modified game can simply not run your DisablePlayerFiring loop. Client-side protection stops honest players from accidentally shooting in a hospital. It does not stop someone who wants to.

For a safe zone that actually holds, the server has to be involved โ€” validating damage events, checking whether the attacker and victim were both inside a protected area, and rejecting what should not be possible. Our client vs server guide explains why this split exists and protecting your server from cheaters covers the wider pattern.

When you evaluate a safe zone script, this is the question to ask: does it validate server-side, or is it a client loop with a nice UI? Most free releases are the latter.

Getting your coordinates

The traditional workflow is painful: stand in the world, run a coordinate command, copy the vector out of the console, paste it into a config, restart, discover the zone is three metres off, repeat.

For a circle that is merely tedious. For a polygon covering an irregular hospital forecourt with a car park attached, you are copying eight to sixteen vectors by hand and getting one wrong. Our guide on finding and adding coordinates covers the manual method properly.

The in-game creator approach

The alternative is drawing the zone where it lives. You walk the boundary, drop a point at each corner, and the polygon is saved for you โ€” no console, no config file, no restart.

The practical differences:

  • Irregular shapes become trivial. Real protected areas are rarely circles. A hospital with an ambulance bay and a car park is a polygon, and drawing it takes a minute.
  • You see the boundary as you place it, so "three metres off" does not happen.
  • Editing is not a redeploy. Move a point, save, done.
  • Non-developers can do it. An admin can add a zone without touching a Lua file.

This is what people are searching for when they look for a *safezone creator* rather than a safezone script โ€” the difference is whether zone creation is a config task or an in-game one.

Rules worth having per zone

Once zones are easy to create, you will want them to behave differently:

  • Disable weapons โ€” the baseline
  • Block fist fighting โ€” spawn areas need this, a fight club does not
  • God mode โ€” full damage immunity, for hospitals
  • Vehicle speed limit โ€” stops someone driving through a crowded spawn at 200 km/h
  • Job whitelist โ€” police and EMS keep their weapons while everyone else is disarmed

That last one matters more than it sounds. A safe zone that disarms responding officers during an incident creates a worse problem than the one it solved.

Testing

  1. Restart and check the console for errors.
  2. Walk into each zone โ€” confirm weapons and damage are disabled and the indicator shows.
  3. Walk out โ€” confirm combat resumes. Getting stuck protected outside the zone is the classic exit-detection bug.
  4. Test entering mid-combat.
  5. Test the job whitelist with an actual whitelisted player.
  6. Run resmon 1 and check idle cost with nobody near a zone. It should be effectively zero.

Common issues

  • Protection does not apply โ€” the zone coordinates do not cover where you are standing. Turn on the script's debug drawing if it has one.
  • Zone feels laggy โ€” almost always a Wait(0) loop running regardless of proximity. See the detection section above.
  • Stuck protected after leaving โ€” exit detection failed, usually a zone system that only checks entry.
  • Players still take damage inside โ€” something else is applying damage server-side, or another resource is fighting yours. Finding the script conflict covers the method.

The easy path

If you would rather not build and maintain this yourself, [Viper Safe-Zone](/scripts/7623027) is an in-game safezone creator: draw polygon zones by walking the boundary, manage them live from a menu, and set weapons, fist fighting, god mode, vehicle speed limit and a job whitelist per zone. It runs on QBCore, QBox and ESX with automatic detection, and validates server-side rather than trusting the client.

See the Viper in-game safezone creator โ†’

Premium FiveM scripts for QBCore & ESX

Viper Development builds escrow-protected, dual-framework scripts that install clean and run without dragging your server down.

Keep reading

Related guides