Not sure what you mean by those “server symbols”, but I’ve used ETW to debug which resources cause low fps before the update with warnings was released, and I could also see the server process in WPA with ability to debug which resources cause much load on server side because the server was hosted locally on my PC.
Just tried it, and yes server debugging also works. Of course it only shows you which resources take up time, but it sadly doesn’t show you exactly which part of the resource takes up time. Though of course I wouldn’t expect that from such tool to tell me exactly which classes take up how much.
Client side debugging requires the ‘symbols’ @Syntasu was talking about.
(these symbols)
srvC:\cachepathhttps://runtime.fivem.net/client/symbols/
So I’m still slightly confused, how did you know “Intial code -> Runs at 119ms” and that your “Improved code -> Runs at 7ms”. Do i use ETW and look at duration time? (So make it go down)
Yes. In some cases it’s also possible to split the resource you want to debug into 2 or more separate resources so that you will be able to see more accurately which part of the resource takes much time.
Anyone know of a comparable tool to ETW for Linux servers? Would love to optimize server side without spinning up a Windows server locally…
Hi there Syntasu, I was wondering if you could help me understand the issue(s) with this resource. It’s a moneydrop resource where peds drop cash on death. I only altered the code to work with the ESX payment system so I’ll paste my code below.
The script works as expected with no warnings displayed initially. After about 15 minutes on the server, the players will see a 6 ms warning. This will slowly climb, about 1 ms every 10 minutes or so. I’ve seen it get to 30ms before logging to reset it.
I have tried altering the onticks throughout the resource and some would delay the onset of the warning and slow the climb but would never stop it from happening.
Looking at the script below, could you tell me what is most likely causing the problem described and what I can do so we can keep using the resource? All my friends absolutely love using it.
Thanks for your time!
pedindex = {}
objval = {}
Citizen.CreateThread(function()
while true do
Wait(0)
for k,v in pairs(objval) do
if DoesEntityExist(k) then
if IsControlPressed(0,38) then -- E
Citizen.CreateThread(function() RampTowardsPlayer(k) end)
Wait(50)
end
end
end
end
end)
AddEventHandler("onClientMapStart", function()
Citizen.CreateThread(function()
while true do
Citizen.Wait(0) -- have tried 0-20, issues persisted
PopulatePedIndex()
ResetIndexOnDeath()
for k,v in pairs(pedindex) do
if DoesEntityExist(k) then
veh = GetVehiclePedIsIn(k, false)
if not IsPedInVehicle(k, veh, true) then
if IsEntityDead(k) then
SpawnMoneyWithRandomValue(k,5,13)
pedindex[k] = nil
HighlightObject(k)
end
end
end
end
for k,v in pairs(objval) do
if DoesEntityExist(k) then
dist = DistanceBetweenCoords(PlayerPedId(-1), k)
if (dist.x < 0.4) and (dist.y < 0.4) and (dist.z < 1) then
TriggerServerEvent('getPaid', v.worth)
DeleteObject(k)
PlaySoundFrontend(-1, "PICK_UP", "HUD_FRONTEND_DEFAULT_SOUNDSET")
objval[k] = nil
end
HighlightObject(k)
end
end
end
end)
end)
function HighlightObject(object)
x, y, z = table.unpack(GetEntityCoords(object, true))
SetDrawOrigin(x, y, z, 0)
RequestStreamedTextureDict("helicopterhud", false)
DrawSprite("helicopterhud", "hud_corner", -0.01, -0.01, 0.006, 0.006, 0.0, 255, 0, 0, 200)
DrawSprite("helicopterhud", "hud_corner", 0.01, -0.01, 0.006, 0.006, 90.0, 255, 0, 0, 200)
DrawSprite("helicopterhud", "hud_corner", -0.01, 0.01, 0.006, 0.006, 270.0, 255, 0, 0, 200)
DrawSprite("helicopterhud", "hud_corner", 0.01, 0.01, 0.006, 0.006, 180.0, 255, 0, 0, 200)
ClearDrawOrigin()
end
function DistanceBetweenCoords(ent1, ent2)
local x1,y1,z1 = table.unpack(GetEntityCoords(ent1, true))
local x2,y2,z2 = table.unpack(GetEntityCoords(ent2, true))
local deltax = x1 - x2
local deltay = y1 - y2
local deltaz = y1 - y2
dist = math.sqrt((deltax * deltax) + (deltay * deltay) + (deltaz * deltaz))
xout = math.abs(deltax)
yout = math.abs(deltay)
zout = math.abs(deltaz)
result = {distance = dist, x = xout, y = yout, z = zout}
return result
end
function ResetIndexOnDeath()
if IsEntityDead(GetPlayerPed(-1)) then
for k,v in pairs(objval) do
objval[k] = nil
end
end
end
function PopulatePedIndex()
local handle, ped = FindFirstPed()
local finished = false -- FindNextPed will turn the first variable to false when it fails to find another ped in the index
repeat
if not IsEntityDead(ped) then
pedindex[ped] = {}
end
finished, ped = FindNextPed(handle) -- first param returns true while entities are found
until not finished
EndFindPed(handle)
end
function SpawnMoneyWithRandomValue(ped, lowlimit, upperlimit)
value = math.random(lowlimit, upperlimit)
money, quantity = MoneyVariance(value)
x, y, z = table.unpack(GetEntityCoords(ped, true))
z = z + 1.3
i = 0
while i < quantity do
x2 = math.random() + math.random(-2,2)
y2 = math.random() + math.random(-2,2)
z2 = math.random() + math.random(6,9)
i = i + 1
tempobject = CreateObject(GetCashHash((RoundNumber((money / quantity), 0))), x, y, z, true, false, true)
SetActivateObjectPhysicsAsSoonAsItIsUnfrozen(tempobject, true)
SetEntityDynamic(tempobject, true)
ApplyForceToEntity(tempobject, 1, x2, y2, z2, 0.0, 3.0, 0.0, 0, 0, 1, 1, 0, 1)
objval[tempobject] = { worth = (RoundNumber((money / quantity), 0)) }
end
end
function RampTowardsPlayer(entity)
local t = 0.0
while t < 1.0 do
Wait(25)
t = t + 0.01
vec3 = VectorLerp(GetEntityCoords(entity, true), GetEntityCoords(PlayerPedId(), true), t)
vehicle = GetRandomVehicleInSphere(vec3,2.0,0,0)
if DoesEntityExist(entity) and (vehicle < 2) then -- The reason we choose two is because sometimes it returns 1 and I don't know why. I assume it also returns 2 sometimes just incase.
SetEntityCoords(entity, vec3)
elseif not DoesEntityExist(entity) then
t = 1.0
end
end
end
function VectorLerp(vec1, vec2, t)
vecOut = vec1 - (t * (vec1 - vec2))
return vecOut
end
function GetCashHash(money)
local propA = "prop_anim_cash_note"
local propB = "prop_anim_cash_pile_01"
local propC = "prop_cash_envelope_01"
local propD = "prop_cash_pile_02" --smaller prop
local propE = "prop_cash_pile_01" -- bigger wad of cash
local propF = "prop_money_bag_01"
local model = 0
if (money >= 0) then
model = propA
end
if (money >= 20) then
model = propB
end
if (money >= 100) then
model = propC
end
if (money >= 250) then
model = propD
end
if (money >= 500) then
model = propE
end
if (money >= 1500) then
model = PropF
end
return GetHashKey(model)
end
function MoneyVariance(value)
local RNG = math.random()
local basevalue = value
local multiplier = 1.0
local quantity = math.random(5,6)
if (RNG <= 0.75) then
multiplier = 0.85
quantity = math.random(2,4)
end
if (RNG <= 0.45) then
multiplier = 1.1
quantity = math.random(1,2)
end
if (RNG >= 0.35) then
multiplier = 1.3
quantity = math.random(1,2)
end
if (RNG >= 0.20) then
multiplier = 1.6
quantity = math.random(1,2)
end
if (RNG >= 0.04) then
multiplier = 2.3
quantity = math.random(1,2)
end
if (RNG >= 0.02) then
multiplier = 4.0
quantity = 1
end
if(RNG >= 0.009) then
multiplier = 5.0
quantity = math.random(1,3)
end
finalvalue = basevalue * multiplier
return finalvalue, quantity
end
angleint = 0
function CapsuleCheckForNearbyPed(inputped)
x, y, z = table.unpack(GetEntityCoords(inputped, true))
flag = 12
radius = 60
Wait(7)
for i = angleint, 72 do
angleint = angleint + 1
AdjustAngleInt()
local angle = math.rad(i * 5)
local startX = (60.0 * math.cos(angle)) + x;
local startY = (60.0 * math.sin(angle)) + y;
local endX = x - (startX - x)
local endY = y - (startY - y)
ray = StartShapeTestCapsule(startX,startY,z,endX,endY,z,radius,flag,inputped,7)
_, _, _, _, result = GetShapeTestResult(ray)
return result
end
end
function AdjustAngleInt()
if angleint > 72 then
angleint = 1
end
end
function DetectNpcByAiming()
local aiming = false
local entity
if IsPlayerFreeAiming(PlayerId()) then
aiming, entity = GetEntityPlayerIsFreeAimingAt(PlayerId())
if (aiming) then
if IsEntityAPed(entity) then
return entity
end
end
end
end
function GetTableLength(temptable)
local count = 0
for _ in pairs(temptable) do
count = count+1
end
return count
end
function RoundNumber(num, numDecimalPlaces)
if numDecimalPlaces and numDecimalPlaces>0 then
local mult = 10^numDecimalPlaces
return math.floor(num * mult + 0.5) / mult
end
return math.floor(num + 0.5)
end
And you are sure the pedIndex and objVal are cleaned up correctly. Maybe try printing the count in these tables (foreach -> entry ~= nil -> count++) like each minute. I suspect that the tables might be incrementally growing… which seems to fit your descriptions.
I’m currently busy, I will take a more thorough look another time
Thanks very much Sytasu, I’ll start to try and see if I can figure out which variable might be causing the problem. Could you tell me how I should properly empty or unset a variable(objval, for example)? I’m searching but not finding a way to handle that.
Thanks for your time!
I guess just setting it to nil should be fine. But I could be wrong as where the table will keep growing and thus iterating over the nil index as well.
In that case you could try to find the nearest nil value in the table and write it to that index. This should keep the table from having a million nil index. I do not know if this is done for you or not.
Hi there Syntasu,
I think I figured out what variable is causing the issue by placing each one in the ondeath event one at a time:
function ResetIndexOnDeath()
if IsEntityDead(GetPlayerPed(-1)) then
for k,v in pairs(objval) do
objval[k] = nil
objval[tempobject] = nil
pedindex[k] = nil
pedindex[ped] = nil
end
end
end
And then letting the ms warning grow some before killing myself. The warning disappears when I add the pedindex[k] and pedindex[ped] in the loop.
This gets rid of the ms warning but breaks the resource in that you can’t get any more money once you die.
The problem I now face is controlling that variable in the event that someone doesn’t die. I don’t understand the code well enough to know when I can safely set it to nil or if that should even be how I handle it. Perhaps the table should be truncated somehow, keeping only the latest x entries in the table? I don’t know if that can be done in lua.
I’d very much welcome your input in the matter. Thanks for your time!
I can’t seem to resolve the lag issue without stopping the resource from working. I can get the ms warnings to go away but can’t seem to get it to keep that variable working.
I’ve tried a bunch of variations. The commented lines have all been tried both separately and together.
function ResetIndexOnDeath()
if IsEntityDead(GetPlayerPed(-1)) then
for k,v in pairs(objval) do
objval[k] = nil
--objval[tempobject] = nil
--pedindex[k] = nil
--pedindex[ped] = nil
--pedindex = {}
end
end
end
Do you happen to have an idea of what I can do to resolve this issue?
I think you could safely increase your delay to one second
Did you even read the topic? Or are you just posting it here and waiting for someone to fix it for you?
Seriously… the last example is the exact same code.
You dont…
sorry if i ask but, the hitch frame time warnings in console… are they related to server code or client code? (my server starts to get hitch frametime warnings after 25 / 26 players)
hitch frame time warnings in the server console indicate server code issues. Like: heavy operations or a lot of mysql operations per second (which is the case with esx/vrp)
So it’s normal using ESX to have such hitch warnings?
Yeah, this is 100% related to server code. The key here is to find out which resource causes these hitches. As @d0p3t said, this is mainly due to doing heavy operations.
Mainly you want to run hitch free, since hitches can cause people to lose connection from your server if the hitches are too severe.
that’s what i’m trying to do… the main problem here is how to find the right script that causes it since it only happens when 26+ players are online
I suggest doing what @d0p3t suggested, see how many queries there are being executed. Probably some script that scales badly with the amount of people.