Back again with another question. Does anyone have any suggestions on how I could reduce the resource usage?

CreateThread(function()
	while true do
		sleep = 1000
		local PlayerJob = QBCore.Functions.GetPlayerData().job
		if not IsPedInAnyVehicle(PlayerPedId()) and PlayerJob.name == Config.GarbageCompany['job'] and PlayerJob.onduty then
			local playerPos = GetEntityCoords(PlayerPedId())
			for i = 1, #Config.GarbageObjects do
				local rubbishEntity = GetClosestObjectOfType(playerPos, 2.0, GetHashKey(Config.GarbageObjects[i]), false, false, false)
				if DoesEntityExist(rubbishEntity) then
					local rubbishCoords = GetEntityCoords(rubbishEntity)
					sleep = 5
					if #(playerPos - rubbishCoords) <= 1.5 then
						DrawText3D(rubbishCoords.x, rubbishCoords.y, rubbishCoords.z, '~g~E~w~ - Pickup Rubbish')
						if IsControlJustReleased(0, 38) then
							TriggerServerEvent("test-resource:server:deleteObject", rubbishEntity)
						end
					end
				end
			end
		end
		Wait(sleep)
	end
end)

Currently we’re sitting around 0.56ms when near the object, 0.00ms when I’m not. This is the first time I’ve dealt with objects that are already placed around the map as opposed to player created.

Thank you in advance :slight_smile:

Edit:

I should probably mention that these are the objects in Config.GarbageObjects:

Config.GarbageObjects = {
	"prop_rub_binbag_01",
	"prop_rub_binbag_01b",
	"prop_rub_binbag_03",
	"prop_rub_binbag_03b",
	"prop_rub_binbag_04",
	"prop_rub_binbag_05",
	"prop_rub_binbag_06",
	"prop_rub_binbag_08",
}

The more objects I add to the list, the higher the usage gets.

1 Like

I could be mistaken but it looks like in your for loop, under the DoesEntityExist clause, it sets your sleep time to 5ms for the DrawText function. I don’t see anywhere that it’s changed back to 1 second. So if a player ever encounters garbage, their thread loop time is shortended significantly.

I do have a Wait further down? Also, I’ve run into another issue where the above code also won’t detect any objects unless I restart the resource whilst the players are in the server.

1 Like

set under every then “sleep = 5000”.

This did nothing but create delayed actions when coming up to the props.

Edit: So I’ve discovered that it is the sleep = 5 that is causing the large resource usage amount however that is also causing the DrawMarker to flicker.

I probably can’t go through the entire script for you, but little things I can help with…

-- Step 1. reduce the calls to GetHashKey, and use compilation time hashing
-- https://cookbook.fivem.net/2019/06/23/lua-support-for-compile-time-jenkins-hashes/
Config.GarbageObjects = {
	`prop_rub_binbag_01`,
	`prop_rub_binbag_01b`,
	`prop_rub_binbag_03`,
	`prop_rub_binbag_03b`,
	`prop_rub_binbag_04`,
	`prop_rub_binbag_05`,
	`prop_rub_binbag_06`,
	`prop_rub_binbag_08`,
}

-- Iterate through this loop using
for _, hash in ipairs(Config.GarbageObjects) do
    -- Do something with the hash here
    print('The hash in garbage is', hash)
end

So accounting for this, you’d end up with something like…

-- Step 1. reduce the calls to GetHashKey, and use compilation time hashing
-- https://cookbook.fivem.net/2019/06/23/lua-support-for-compile-time-jenkins-hashes/
Config.GarbageObjects = {
	`prop_rub_binbag_01`,
	`prop_rub_binbag_01b`,
	`prop_rub_binbag_03`,
	`prop_rub_binbag_03b`,
	`prop_rub_binbag_04`,
	`prop_rub_binbag_05`,
	`prop_rub_binbag_06`,
	`prop_rub_binbag_08`,
}

CreateThread(function()
	while true do
        local ped = PlayerPedId()
		sleep = 1000

        -- Check if ped is on foot before getting player data
        if IsPedOnFoot(ped) then
            local PlayerJob = QBCore.Functions.GetPlayerData().job

            if PlayerJob.name == Config.GarbageCompany['job'] and PlayerJob.onduty then
                local playerPos = GetEntityCoords(ped)
                -- Updated iteration for garbage hashes
                for _, garbageHash in ipairs(Config.GarbageObjects) do
                    -- Removed the GetHashKey every time the loop runs
                    local rubbishEntity = GetClosestObjectOfType(playerPos, 2.0, garbageHash, false, false, false)
                    if DoesEntityExist(rubbishEntity) then
                        local rubbishCoords = GetEntityCoords(rubbishEntity)
                        sleep = 5
                        if #(playerPos - rubbishCoords) <= 1.5 then
                            DrawText3D(rubbishCoords.x, rubbishCoords.y, rubbishCoords.z, '~g~E~w~ - Pickup Rubbish')
                            if IsControlJustReleased(0, 38) then
                                TriggerServerEvent("test-resource:server:deleteObject", rubbishEntity)
                            end
                        end
                    end
                end
            end
        end

		Wait(sleep)
	end
end)

From best I can tell, your script will also attempt to draw text on the closest entity of every type if they’re in range. So if you have all 8 garbage types next to you, you’re drawing 8x pick up rubbish, and will delete all 8 at once if button is clicked?

If this is not intentional you’ll need to keep track of the closest entity and check if playerPos - rubbishCoords < currentDistance. :slight_smile:

If you want to find and interact only with a singular nearest object you can do something along the lines of…

---Finds a singular closest object from a list of object hashes
---@param playerCoords vector3
---@return integer
---@return vector3
---@return number
function FindNearestGarbageObject(playerCoords)
    local closestEntity = nil
    local closestCoords = nil
    local closestDistance = nil

    for _, hash in ipairs(Config.GarbageObjects) do
        local entity = GetClosestObjectOfType(playerCoords, 2.0, hash, false, false, false)

        if DoesEntityExist(entity) then
            local coords = GetEntityCoords(entity)
            local distance = #(playerCoords - coords)

            if closestDistance == nil or distance < closestDistance then
                closestEntity = entity
                closestCoords = coords
                closestDistance = distance
            end
        end
    end

    return closestEntity, closestCoords, closestDistance
end

Last thing from me you might consider is splitting the responsibilities into two separate loops.

  1. Update global variables for entity / coords for text / interaction (1000ms)
  2. Draw text & monitor button press when entity/coords are present

That means you consistently check your player’s position, and any objects that are around them, while not doing all the heavy checks in rapid succession just because you’re standing next to some garbage. And you can reset the globals after interacting with an object, to make the text instantly disappear if that’s a concern.

-- Step 1. reduce the calls to GetHashKey, and use compilation time hashing
-- https://cookbook.fivem.net/2019/06/23/lua-support-for-compile-time-jenkins-hashes/
Config.GarbageObjects = {
	`prop_rub_binbag_01`,
	`prop_rub_binbag_01b`,
	`prop_rub_binbag_03`,
	`prop_rub_binbag_03b`,
	`prop_rub_binbag_04`,
	`prop_rub_binbag_05`,
	`prop_rub_binbag_06`,
	`prop_rub_binbag_08`,
}

CreateThread(function()
	while true do
        local ped = PlayerPedId()
		sleep = 1000

        -- Check if ped is on foot before getting player data
        if IsPedOnFoot(ped) then
            local PlayerJob = QBCore.Functions.GetPlayerData().job

            if PlayerJob.name == Config.GarbageCompany['job'] and PlayerJob.onduty then
                local playerPos = GetEntityCoords(ped)
                -- Updated iteration for garbage hashes
                for _, garbageHash in ipairs(Config.GarbageObjects) do
                    -- Removed the GetHashKey every time the loop runs
                    local rubbishEntity = GetClosestObjectOfType(playerPos, 2.0, garbageHash, false, false, false)
                    if DoesEntityExist(rubbishEntity) then
                        local rubbishCoords = GetEntityCoords(rubbishEntity)
                        sleep = 5
                        if #(playerPos - rubbishCoords) <= 1.5 then
                            DrawText3D(rubbishCoords.x, rubbishCoords.y, rubbishCoords.z, '~g~E~w~ - Pickup Rubbish')
                            if IsControlJustReleased(0, 38) then
                                TriggerServerEvent("test-resource:server:deleteObject", rubbishEntity)
                            end
                        end
                    end
                end
            end
        end

		Wait(sleep)
	end
end)

Using this exact code, the DrawMarker no longer appears. If I add GetHashKey to the modename as part of GetClosestObjectOfType like so:

local rubbishEntity = GetClosestObjectOfType(playerPos, 2.0, GetHashKey(garbageHash), false, false, false)

then the DrawMarker works.

I’m assuming the reason why this is resource hungry is because GetHashKey is being looped through Config.GarbageObjects? I’m no programmer and I am still learning LUA (something new every day it seems) so I’ll probably need the simplest of explanations :sweat_smile:

I also have no idea how to implement this:

---Finds a singular closest object from a list of object hashes
---@param playerCoords vector3
---@return integer
---@return vector3
---@return number
function FindNearestGarbageObject(playerCoords)
    local closestEntity = nil
    local closestCoords = nil
    local closestDistance = nil

    for _, hash in ipairs(Config.GarbageObjects) do
        local entity = GetClosestObjectOfType(playerCoords, 2.0, hash, false, false, false)

        if DoesEntityExist(entity) then
            local coords = GetEntityCoords(entity)
            local distance = #(playerCoords - coords)

            if closestDistance == nil or distance < closestDistance then
                closestEntity = entity
                closestCoords = coords
                closestDistance = distance
            end
        end
    end

    return closestEntity, closestCoords, closestDistance
end

Could you give a simple example? I appreciate the help.

See the difference?

The above code example has issues drawing the DrawText, for whatever reason.

Currently this is the code I have:

Config.lua

Config.GarbageObjects = {
	"prop_rub_binbag_01",
	"prop_rub_binbag_01b",
	"prop_rub_binbag_03",
	"prop_rub_binbag_03b",
	"prop_rub_binbag_04",
	"prop_rub_binbag_05",
	"prop_rub_binbag_06",
	"prop_rub_binbag_08"
}

client.lua

local function FindNearestGarbageObject(playerCoords)
    local closestEntity = nil
    local closestCoords = nil
    local closestDistance = nil

    for _, hash in ipairs(Config.GarbageObjects) do
        local entity = GetClosestObjectOfType(playerCoords, 2.0, GetHashKey(hash), false, false, false)

        if DoesEntityExist(entity) then
            local coords = GetEntityCoords(entity)
            local distance = #(playerCoords - coords)

            if closestDistance == nil or distance < closestDistance then
                closestEntity = entity
                closestCoords = coords
                closestDistance = distance
            end
        end
    end

    return closestEntity, closestCoords, closestDistance
end

CreateThread(function()
	while true do
		local sleep = 1000
		if IsPedOnFoot(PlayerPedId()) then
			local playerJob = QBCore.Functions.GetPlayerData().job
			if playerJob.name == Config.GarbageCompany['job'] and playerJob.onduty then
				local playerPos = GetEntityCoords(PlayerPedId())
				local rubbishEntity = FindNearestGarbageObject(playerPos)
				if DoesEntityExist(rubbishEntity) then
					local rubbishCoords = GetEntityCoords(rubbishEntity)
					if not hasBag and canTakeBag then
						sleep = 5
						if #(playerPos - rubbishCoords) <= 1.5 then
							DrawText3D(rubbishCoords.x, rubbishCoords.y, rubbishCoords.z, '~g~E~w~ - Pickup Rubbish')
							if IsControlJustReleased(0, 38) then
								hasBag = true
								TakeAnim(rubbishEntity)
								TriggerServerEvent("test-resource:server:deleteObject", rubbishEntity)
								disposeRubbish()
							end
						end
					end
				end
			end
		end
		Wait(sleep)
	end
end)

It has worked well in only displaying the DrawText on one object when in a cluster however it hasn’t reduced the resource usage at all – it floats around the 0.47ms mark when near an object that matches.

I use this approach in all scripts, there is no problem with rendering, try reducing it to 3,
In the version that you presented, it generally makes no sense to write sleep in this line.
The whole point is that the distance check is used once every 1 second or 2 seconds, and as soon as this condition is met, the sleep variable becomes 5

This is the result from using the code you’ve shared :slight_smile: As you can see, no DrawText.

Here is the result from the code I previously shared

I don’t know why you don’t have drawing, I don’t have problems with this, sort it out further.


And in general, switching to qtarget

I personally don’t like qb-target and neither does our player base however I’m currently trying to reduce the resource usage.

You need to update the config values to use the appropriate quotation for my code to work.
If re-adding GetHashKey() worked, you probably did not update the config values to use the Jenkins hashes. :slight_smile:

If you read the cookbook entry on these hashes, you can see that they reduced their resource usage from 43ms to 18ms by not calling GetHashKey()

-- See the difference
local isTrue = `prop_rub_binbag_01` == GetHashKey("prop_rub_binbag_01") == GetHashKey('prop_rub_binbag_01')

So with my code snippets, double check your config values are using the right quotation, as an example I’ll provide this to clarify what quotes to use:

Config.GarbageObjects = {
	`prop_rub_binbag_01`, -- works with my proposed changes
	'prop_rub_binbag_01b', -- does not work with my proposed changes
	"prop_rub_binbag_03", -- does not work with my proposed changes
}

As for the FindNearestGarbageObject implementation, maybe something like this?

function FindNearestObject(playerCoords)
    local closestEntity = nil
    local closestCoords = nil
    local closestDistance = nil

    for _, hash in ipairs(Config.GarbageObjects) do
        local entity = GetClosestObjectOfType(playerCoords, 2.0, hash, false, false, false)

        if DoesEntityExist(entity) then
            local coords = GetEntityCoords(entity)
            local distance = #(playerCoords - coords)

            if closestDistance == nil or distance < closestDistance then
                closestEntity = entity
                closestCoords = coords
                closestDistance = distance
            end
        end
    end

    return closestEntity, closestCoords, closestDistance
end

CreateThread(function()
	while true do
        local ped = PlayerPedId()
		local sleep = 1000

        -- Check if ped is on foot before getting player data
        if IsPedOnFoot(ped) then
            local PlayerJob = QBCore.Functions.GetPlayerData().job

            if PlayerJob.name == Config.GarbageCompany['job'] and PlayerJob.onduty then
                local playerPos = GetEntityCoords(ped)

                -- Fetch only one entity to draw for
                local entity, coords, distance = FindNearestObject(playerPos)

                if distance and distance <= 1.5 then
                    sleep = 5
                    DrawText3D(coords.x, coords.y, coords.z, '~g~E~w~ - Pickup Rubbish')
                    if IsControlJustReleased(0, 38) then
                        TriggerServerEvent("test-resource:server:deleteObject", entity)
                    end
                end
            end
        end

		Wait(sleep)
	end
end)

Ahh, I see the difference now with the prop models. However sadly using your example, still causes 0.50ms in usage when near the prop.

config.lua

Config.GarbageObjects = {
	`prop_rub_binbag_01`,
	`prop_rub_binbag_01b`,
	`prop_rub_binbag_03`,
	`prop_rub_binbag_03b`,
	`prop_rub_binbag_04`,
	`prop_rub_binbag_05`,
	`prop_rub_binbag_06`,
	`prop_rub_binbag_08`
}

client.lua

local function FindNearestObject(playerCoords)
	-- Code shared by OGWA (https://forum.cfx.re/t/help-reducing-resource-usage/4808843/)
    local closestEntity = nil
    local closestCoords = nil
    local closestDistance = nil
    for _, hash in ipairs(Config.GarbageObjects) do
        local entity = GetClosestObjectOfType(playerCoords, 2.0, hash, false, false, false)
        if DoesEntityExist(entity) then
            local coords = GetEntityCoords(entity)
            local distance = #(playerCoords - coords)
            if closestDistance == nil or distance < closestDistance then
                closestEntity = entity
                closestCoords = coords
                closestDistance = distance
            end
        end
    end
    return closestEntity, closestCoords, closestDistance
end

CreateThread(function()
	while true do
        local ped = PlayerPedId()
		local sleep = 1000
        if IsPedOnFoot(ped) then
            local PlayerJob = QBCore.Functions.GetPlayerData().job
            if PlayerJob.name == Config.GarbageCompany['job'] and PlayerJob.onduty then
                local playerPos = GetEntityCoords(ped)
                local entity, coords, distance = FindNearestObject(playerPos)
				if not hasBag and canTakeBag then
					if distance and distance <= 1.5 then
						sleep = 5
						DrawText3D(coords.x, coords.y, coords.z, '~g~E~w~ - Pickup Rubbish')
						if IsControlJustReleased(0, 38) then
							hasBag = true
							TakeAnim(entity)
							TriggerServerEvent("test-resource:server:deleteObject", entity)
							disposeRubbish()
						end
					end
				end
            end
        end
		Wait(sleep)
	end
end)

I’ve now lost over 14 hours of my life on this so far :sweat_smile:

I apologize, I have now noticed the difference in the config.lua file. However, using your code, it is still producing 0.52ms in resource usage when near the prop.

Try splitting the logic into two loops. Only do the draw when the main loop finds an entity. You’re doing all the heavy lifting every 5ms that should be done at most every 250ms.

I’ve managed to get it down to 0.03ms when near the object - thank you! However I am now faced with the DrawText not going away when going off duty.

CreateThread(function()
	while true do
		local sleep = 5
		if drawEnabled then
			local pos = GetEntityCoords(PlayerPedId())
			if #(pos - coords) <= Config.RubbishDistane then
				DrawText3D(coords.x, coords.y, coords.z, '~g~E~w~ - Pickup Rubbish')
				if IsControlJustReleased(0, 38) then
					hasBag = true
					TakeAnim(entity)
					TriggerServerEvent("test-resource:server:deleteObject", entity)
					disposeRubbish()
					drawEnabled = false
				end
			end
		end
		Wait(sleep)
	end
end)

CreateThread(function()
	while true do
		local sleep = 1000
		onDuty = QBCore.Functions.GetPlayerData().job.onduty
        if IsPedOnFoot(PlayerPedId()) then
            if playerJob.name == Config.GarbageCompany['job'] and onDuty then
				entity, coords, distance = FindNearestObject(GetEntityCoords(PlayerPedId()))
				if not hasBag and canTakeBag then
					if distance and distance <= Config.RubbishDistane then
						drawEnabled = true
					else
						drawEnabled = false
					end
				end
            end
        end
		Wait(sleep)
	end
end)

I’ve probably made a simple mistake.