ESX.GetPlayers() Server trhead hitch warning

I have 200 people on the server active, and a lot of thread hooks, doing a server-side Profiler, all that generates these thread hooks is this function:

ESX.GetPlayers ()

Is there any other way to do this?

1 Like

ESX.GetPlayers() returns a list of player ids that have a character loaded.

Resources that use this function then loop through the IDs and use ESX.GetPlayerFromId(id), then perform a whatever task they want with that without any sort of wait. Sometimes this is an async query - so you send 200 database queries almost all at once, or some events (which are in themselves async).

You can alleviate some of the stress by changing how the xPlayer data is retrieved.

Add the following function to ESX/server/functions.lua

ESX.GetExtendedPlayers = function()
	return ESX.Players
end

Example of a typical xPlayer loop

		local xPlayers = ESX.GetPlayers()
		for _,playerId in ipairs(xPlayers) do
			local xPlayer = ESX.GetPlayerFromId(playerId)

			MySQL.Async.fetchAll('SELECT status FROM users WHERE identifier = @identifier', {
				['@identifier'] = xPlayer.identifier
			}, function(result)
				local data = {}

				if result[1].status then
					data = json.decode(result[1].status)
				end

				xPlayer.set('status', data)
				TriggerClientEvent('esx_status:load', playerId, data)
			end)
		end

Replacement

		local xPlayers = ESX.GetExtendedPlayers()
		for k,v in pairs(xPlayers) do		
			MySQL.Async.fetchAll('SELECT status FROM users WHERE identifier = @identifier', {
				['@identifier'] = v.identifier
			}, function(result)
				local data = {}

				if result[1].status then
					data = json.decode(result[1].status)
				end

				v.set('status', data)
				TriggerClientEvent('esx_status:load', k, data)
			end)
		end

If you are using any resources for blips (for EMS, police, etc) check how they are performing this task as well. By default ambulancejob and policejob send a server event when a cop loads in, which tells all clients to check if they are ems/police and send a server callback.

The callback, which is triggered by (for example) 6 cops then runs 6 async xPlayer loops to get the ids of all active cops and report back to the client.

Lastly, if you are using gcphone then you need to replace the following function

function getSourceFromIdentifier(identifier, cb)
	local xPlayers = ESX.GetPlayers()
	
	for i=1, #xPlayers, 1 do
		local xPlayer = ESX.GetPlayerFromId(xPlayers[i])
		
		if(xPlayer.identifier ~= nil and xPlayer.identifier == identifier) or (xPlayer.identifier == identifier) then
			cb(xPlayer.source)
			return
		end
	end
	cb(nil)
end

Replace with

function getSourceFromIdentifier(identifier, cb)
	local xPlayer = ESX.GetPlayerFromIdentifier(identifier)
	if xPlayer then cb(xPlayer.source) else cb(nil) end
end
5 Likes

You are a genius, now I am not clear in this part how I should replace?

local xAll = ESX.GetExtendedPlayers()
    	for i=1, #xAll, 1 do
      		local xTarget = ESX.GetPlayerFromId(xAll[i])
      		if havePermission(xTarget) then		-- you can exclude some ranks to NOT reciveing reports
        		if xPlayer.source ~= xTarget.source then
		    		TriggerClientEvent('chatMessage', xTarget.source, _U('report', GetPlayerName(xPlayer.source), xPlayer.source, message))
        		end
      		end
		end

or here

local xPlayers = ESX.GetPlayers()

for i=1, #xPlayers, 1 do
		local xPlayer = ESX.GetPlayerFromId(xPlayers[i])
		local status  = xPlayer.get('status')

		whenList = whenList .. string.format('when identifier = \'%s\' then \'%s\' ', xPlayer.identifier, json.encode(status))

		if firstItem == false then
			whereList = whereList .. ', '
		end
		whereList = whereList .. string.format('\'%s\'', xPlayer.identifier)

		firstItem = false
		playerCount = playerCount + 1
	end

With the table being returned, the key (or index) is equal to the players id (or source), while the value is their xPlayer data.

So k is the same as v.source / v.playerId - up to you how you want to reference it.

You do not need to use ESX.GetPlayerFromId() at all, it is what causes a bulk of the lag (calling functions from other resources has a lot of overhead).


local xPlayers = ESX.GetExtendedPlayers()

for k,v in pairs(xPlayers) do

    if havePermission(v) and xPlayer.source ~= k then

        TriggerClientEvent('chatMessage', k, _U('report', GetPlayerName(xPlayer.source), xPlayer.source, message))

    end

end

In the second example you’re trying to update esx_status, so you can reference my updated version

I’d appreciate feedback on how all of this performs too, since it’s getting pushed into an update for ESX Legacy.

Edit

Here’s an example of performance when triggering esx_society:getOnlinePlayers with 200 active players.
Keep in mind this is being triggered by a single client - the default blips in esx_policejob trigger the callback for every cop online.

I’ve fixed up esx_society to use the new looping function.

Edit

Full comparison of performance differences when modifying esx_society.
The top two just swap out the function.
The bottom two modify the callback event to stop it from performing the same task 10 times in quick succession.

Now the only thing that causes me thread snags is the gcphone in this function:

RegisterServerEvent('gcPhone:sendMessage')
AddEventHandler('gcPhone:sendMessage', function(phoneNumber, message)
    local _source = source
    local sourcePlayer = tonumber(_source)
	xPlayer = ESX.GetPlayerFromId(_source)
    identifier = xPlayer.identifier
    addMessage(sourcePlayer, identifier, phoneNumber, message)
end)

Using the snippet I included in the first reply?

function getSourceFromIdentifier(identifier, cb)
	local xPlayers = ESX.GetExtendedPlayers()
    for k,v in pairs(xPlayers) do
		--if k then cb(k) else cb(nil) end
		if(v.identifier ~= nil and v.identifier == identifier) or (v.identifier == identifier) then
			cb(k)
			return
		end
	end	
end
function getSourceFromIdentifier(identifier, cb)
	local xPlayer = ESX.GetPlayerFromIdentifier(identifier)
	if xPlayer then cb(xPlayer.source) else cb(nil) end
end

There’s no need to loop through xPlayers to get a source from an identifier.

Shouldn’t it be ESX.GetPlayerFromIdentifier(identifier) instead of ESX.GetPlayerFromId(identifier)? since if you are getting xPlayer from Id why do you even need xPlayer xD.

typo :sweat_smile:

2 Likes

This does not work for me, the SMS do not arrive, I quit the gcphone and everyone arrives, but it is like the SMS are not updated. That’s why I did it as I happened to you.

The original code I sent had a mistake in it, as RealMasBagus pointed out. I edited the previous messages containing the function.

not found, no refresh de sms

The only way that works for me is the following:

function getSourceFromIdentifier(identifier, cb)
	local xPlayers = ESX.GetExtendedPlayers()
    for k,v in pairs(xPlayers) do
		--if k then cb(k) else cb(nil) end
		if(v.identifier ~= nil and v.identifier == identifier) or (v.identifier == identifier) then
			cb(k)
			return
		end
	end	
end

But all the high thread hooks are given to me by the gcphone, when they send sms.

It is the following line:

RegisterServerEvent('gcPhone:sendMessage')
AddEventHandler('gcPhone:sendMessage', function(phoneNumber, message, gpsData)
    local _source = source
    local sourcePlayer = tonumber(_source)
	xPlayer = ESX.GetPlayerFromId(_source)
    identifier = xPlayer.identifier
    addMessage(sourcePlayer, identifier, phoneNumber, message, gpsData)
end)
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.