Performance issue, sound check due to OneSync Culling

Hello,

I have developed a script that allows you to manage the sirens and flashing lights of French emergency vehicles on the driver and passenger side.

for the sound to be managed on the driver and passenger side I had to make each client (player) play their sound for this I go through events that will be executed by each player, after that we noticed that the sound was unloaded at 424 units as explained in the doc OneSync - Cfx.re Docs so we do a check every second on the server side on all vehicles that have a start siren and we check for each player if they are next to a vehicle on that it works the optimization on the client side and correct but on the server side we arrive at peaks of 2.60ms for 60 players and I would like to have the opinion of the developers here, what would I have done because I admit that if we lose performance on the server side the more players we have it will quickly become annoying

On the Fivem documentation regarding oneSync a playerEnteredScope event is played when the player enters the range of another player, it is a shame that there is not one that is executed when a player enters the loading range of an entity

Use state bags for such things: State bags - Cfx.re Docs
They can be attached to entities and will execute a state bag change handler when a player gets in scope (or the state bag is changed). No more need for manually checking who is in range etc. You simply set a value, it gets synced automatically.

Thank you for your help, I had never really used state bags and it is actually quite practical.

1 Like

Code example:

local sirens = {}
local function playSiren(entity)
    -- Prevent from playing siren twice
    if sirens[tostring(entity)] then return false end
    print('Play siren', 'State', Entity(entity).state['siren'])
    sirens[tostring(entity)] = true
    return true
end
local function stopSiren(entity)
    sirens[tostring(entity)] = nil
    print('Stop siren', 'State', Entity(entity).state['siren'])
end

AddStateBagChangeHandler('siren', nil, function(bagName, key, value, _, rep)
    -- Handle only siren 'on' state
    if value ~= true then return end

    -- Get entity (stable alternative for GetEntityFromStateBagName(bagName))
    local netId, _ = string.gsub(bagName, 'entity:', '')
    netId = tonumber(netId)
    local timeoutAt = GetGameTimer() + 1500
    while not NetworkDoesEntityExistWithNetworkId(netId) do
        Wait(0)
        if GetGameTimer() >= timeoutAt then
            return print('Timeout, entity network doesnt exist', netId)
        end
    end
    local entity = NetworkGetEntityFromNetworkId(netId)

    -- Ensure entity exists
    if not DoesEntityExist(entity) then return print('Entity doesnt exist', entity) end

    -- Ensure handler executed only once (local player replication)
    local amOwner = NetworkGetEntityOwner(entity) == PlayerId()
    if amOwner and replicated then return end

    -- Ensure entity has the state assigned
    while Entity(entity).state['siren'] ~= true do Wait(0) end

    -- Play siren
    if playSiren(entity) then
        -- Wait entity exists and while siren is enabled
        while DoesEntityExist(entity) and Entity(entity).state['siren'] == true do
            Wait(200)
        end
    
        -- Stop siren
        stopSiren(entity)
    end
end)

RegisterCommand('sirenon', function()
    local vehicle = GetVehiclePedIsIn(PlayerPedId())
    if not DoesEntityExist(vehicle) then
        return
    end
    Entity(vehicle).state:set('siren', true, true)
end, false)

RegisterCommand('sirenoff', function()
    local vehicle = GetVehiclePedIsIn(PlayerPedId())
    if not DoesEntityExist(vehicle) then
        return
    end
    Entity(vehicle).state:set('siren', false, true)
end, false)
1 Like

There is a new native for that GetEntityFromStateBagName - FiveM Natives @ Cfx.re Docs :slight_smile:

I used this before, but it’s not reliable in some cases like this one:

I think I found a bug with the state bag handler. With the given code, if one player stays on the server near an entity with the siren enabled, and another player joins the server and approaches the entity, the handler does execute for the second player, but the entity was 0 by calling GetEntityFromStateBagName(bagName)

I haven’t run into that issue for ages now. As from my own observation it is fixed.
But I know what you mean. Used to happen when it was first introduced.

Tested it before and double-checked it again just now, on server artifacts version 12817 and FiveM Client “Latest Unstable”. Issue is there.

image

I used this code:

local sirens = {}
local function playSiren(entity)
    -- Prevent from playing siren twice
    if sirens[tostring(entity)] then return false end
    print('Play siren', 'State', Entity(entity).state['siren'])
    sirens[tostring(entity)] = true
    return true
end
local function stopSiren(entity)
    sirens[tostring(entity)] = nil
    print('Stop siren', 'State', Entity(entity).state['siren'])
end

AddStateBagChangeHandler('siren', nil, function(bagName, key, value, _, rep)
    local entity = GetEntityFromStateBagName(bagName)
    print('siren', bagName, key, value, _, rep, 'Entity:', entity, DoesEntityExist(entity))
    if entity == 0 or rep == true then return end

    -- Handle only siren 'on' state
    if value ~= true then return end

    -- Ensure entity has the state assigned
    while Entity(entity).state['siren'] ~= true do Wait(0) end

    -- Play siren
    if playSiren(entity) then
        -- Wait entity exists and while siren is enabled
        while DoesEntityExist(entity) and Entity(entity).state['siren'] == true do
            Wait(200)
        end
    
        -- Stop siren
        stopSiren(entity)
    end
end)

It’s worth to mentioned that I tested it on one PC with two FiveM instances (-cl2).

Hello,
I’m restarting the topic because people complained that they could no longer hear the sound and that they had to disconnect and reconnect to hear them. The server was at 70 players and I checked my code but it’s the StateBagHandler that is supposed to play as in your example, I also did a search on the forum but I can’t find anything similar or posts that are old and have been corrected
Thank you for your help

I’m almost sure the issue is behind not releasing properly sound ids or you just reach out the limit at some point.

For example you use table VehicleData[netid] to store info about soundId’s by netId as a key, but the problem is that netId is a number and this may cause the issue of indexing the data. Try to convert netId to a string before doing anything with the table, like this VehicleData[tostring(netid)].

ah yes indeed it is a problem that I had not thought of, thank you, I will let you know if the problem still persists

1 Like

I’m sorry to bother you with this but players still complain that after a certain time the siren cuts out.

It’s hard to tell what’s wrong then. You need to debug this case, add some logs into the code. For example I would check first if sound id is given correctly:

print('Sound ID', soundId)

Then wait if someone will have issue and ask them for logs from F8 console.

Basically, you need to check 2 things:

  1. If statebag handlerworks correctly. Maybe code in statebag handler is not executed at all. You need to add logs in statebag handler.
  2. And in case statebag handler works correctly you also need more logs into it to trackdown if execution is correct there and such things as soundId.

hello,
so we tested and printed all the values ​​and we didn’t notice anything, the problem that my colleague pointed out to me was a bad use of the RequestScriptAudioBank so we modified a few lines of code and we didn’t get any feedback from the players, it was simply a bad manipulation on our part, thank you anyway for helping us especially for the state bags

1 Like