garrysmod icon indicating copy to clipboard operation
garrysmod copied to clipboard

Add ents/player iterators

Open LeQuackers opened this issue 3 years ago • 6 comments

This is an attempt to close Facepunch/garrysmod-requests/issues/738. Adds iterators for entities and players which are read-only and cached. The main benefit to this is performance; crossing over from Lua to C and back every function call is slow and creates a new table each call.

Examples of use:

for i, p in player.Iterator() do
	print(p)
end

for i, e in ents.Iterator() do
	if e:GetClass() == "prop_physics" then e:Remove() end
end

LeQuackers avatar Oct 10 '20 17:10 LeQuackers

Can be improved by recaching GetAll in hooks not when you call the iterator, this allows to remove if Cache == nil check entirely

GitSparTV avatar Jan 05 '21 16:01 GitSparTV

Looks good. Misc suggestion: Instead of ipairs you can get inext beforehand.

local inext = ipairs(EntityCache)

function entity.Iterator()
    return inext, EntityCache, 0
end

GitSparTV avatar Jan 24 '21 18:01 GitSparTV

One thing I don't like with this approach is that it will call ents.GetAll() whenever an entity is created, which could be hundreds of times on map/server load, map cleanup and maybe server join, basically slowing down loading/freeze times.

robotboy655 avatar May 27 '21 14:05 robotboy655

One thing I don't like with this approach is that it will call ents.GetAll() whenever an entity is created, which could be hundreds of times on map/server load, map cleanup and maybe server join, basically slowing down loading/freeze times.

Maybe it should only set a flag variable to invalidate the cache:

local validCache = false

function player.Iterator()
        if validCache then
            PlayerCache = player.GetAll()
            validCache = true
        end

	return inext, PlayerCache, 0
end

local function RefreshPlayerCache(ent)
	if ent:IsPlayer() then
		validCache = false
	end
end

ceifa avatar May 27 '21 14:05 ceifa

One thing I don't like with this approach is that it will call ents.GetAll() whenever an entity is created, which could be hundreds of times on map/server load, map cleanup and maybe server join, basically slowing down loading/freeze times.

Maybe it should only set a flag variable to invalidate the cache:

local validCache = false

function player.Iterator()
        if validCache then
            PlayerCache = player.GetAll()
            validCache = true
        end

	return inext, PlayerCache, 0
end

local function RefreshPlayerCache(ent)
	if ent:IsPlayer() then
		validCache = false
	end
end

if validCache then should be if not validCache then.

LeQuackers avatar Jun 11 '21 16:06 LeQuackers

is there a reason why this isn't merged yet? like 99% of the time when player/ents.GetAll gets used is just to iterate it without ever using the table

with many addons, you have so many copies of the same table constantly getting created to be iterated when they could all just be iterating the same table

mgetJane avatar May 09 '22 06:05 mgetJane

why depends on player.GetAll()? I would suggest doing it directly, like:

local PlayerCache = {}

hook.Add( "OnEntityCreated", "player.Iterator", function(ent)

	if ( ent:IsPlayer() ) then

		table.insert(PlayerCache, ent)

	end

end )

hook.Add( "EntityRemoved", "player.Iterator", function(ent)

	if ( ent:IsPlayer() ) then

		for i = 1, #PlayerCache do
			if ( PlayerCache[i] == ent ) then
				table.remove(PlayerCache, i)
				break
			end
		end

	end

end )

Be1zebub avatar Jun 22 '23 19:06 Be1zebub