Jump to content

[ENHANCEMENT] client - Render events enchanting


IIYAMA

Recommended Posts

  • 11 months later...
  • 2 months later...
  • Moderators

I noticed after applying Kenix his update that 1 of the loops wasn't inverted. Which is important for removing addRenderEvent's within the same process.

 

 

	-- for i = 1, len( targetFunctions ) do -- old
    for i = len( targetFunctions ), 1, -1 do

 

Just like with domino, when you take a domino out of the line, it will not work well...

Edited by IIYAMA
Link to comment
  • Scripting Moderators
On 16/05/2019 at 22:17, IIYAMA said:

I noticed after applying Kenix his update that 1 of the loops wasn't inverted. Which is important for removing addRenderEvent's within the same process.

 

 


	-- for i = 1, len( targetFunctions ) do -- old    for i = len( targetFunctions ), 1, -1 do

 

Just like with domino, when you take a domino out of the line, it will not work well...

In removeRenderEvent function?

  • Like 1
Link to comment
  • Moderators

 

 

 

If the loop isn't invert, this will not work well:

-- This will go wrong
function theFunction () -- first function
  removeRenderEvent(theFunction)
end

addRenderEvent(theFunction)


-- add multiple functions, else the context isn't valid
for i=1, 9 do
  addRenderEvent(function () end)
end

 

functions:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

 

Looping: from 1 t/m 10

| = index

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

 

Deleting 1 during the loop:

|

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

 

Going on:

   | > > > > > > > >

2, 3, 4, 5, 6, 7, 8, 9, 10, nothing

 

 

error:

                                  |

2, 3, 4, 5, 6, 7, 8, 9, 10, nothing

 

If the loop is inverted, the loop will not have problems when items are removed.

 

 

 

 

 

 

  • Like 1
Link to comment
  • 5 weeks later...
On 19/05/2019 at 14:24, IIYAMA said:

 

 

 

If the loop isn't invert, this will not work well:


-- This will go wrongfunction theFunction () -- first function  removeRenderEvent(theFunction)endaddRenderEvent(theFunction)-- add multiple functions, else the context isn't validfor i=1, 9 do  addRenderEvent(function () end)end

 

functions:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

 

Looping: from 1 t/m 10

| = index

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

 

Deleting 1 during the loop:

|

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

 

Going on:

   | > > > > > > > >

2, 3, 4, 5, 6, 7, 8, 9, 10, nothing

 

 

error:

                                  |

2, 3, 4, 5, 6, 7, 8, 9, 10, nothing

 

If the loop is inverted, the loop will not have problems when items are removed.

 

 

 

 

 

 



@IIYAMA It cause another problem with render events, it starts call from end (which is not needed to us, because dx render events has "layers" and you need to call from 1 to table lenght)

If you're using table.remove function, everything should work fine after remove item from table.
Let me explain:

local a = {};

local func = function() end;

a[ #a + 1 ] = func;

for i = 1, #a do
 if a[ i ] == func then
  table.remove( a, i );
 end
end

print( #( a ) ); -- 0

table.remove safely remove item from table, it's not contains a [1] index with nil value, it's clear

Proof:

local a = {};

local func = function() end;

a[ #a + 1 ] = func;

for i = 1, #a do
 if a[ i ] == func then
  table.remove( a, i );
 end
end

print( #( a ) ); -- 0

local func = function() end;

a[ #a + 1 ] = func;


print( #( a ) ); -- 1



Another example:
 

local a = { 1, 2 };

local func = function() end;

a[ #a + 1 ] = func;

for i = 1, #a do
 if a[ i ] == func then
  table.remove( a, i );
 end
end

print( #( a ) ); -- 2

local func = function() end;

a[ #a + 1 ] = func;

for i = 1, #a do
 if a[ i ] == func then
  table.remove( a, i );
 end
end

print( #( a ) ); -- 2

And finally (shows how another items is reordered): from { [1] = 1, [2] = func, [3] = 2} -> {[1] = 1, [2] = 2}
 

local func = function() end;

local a = { 1, func, 2 };

print( "before", #a ); -- 3

for i = 1, #a do
 if a[ i ] == func then
  table.remove( a, i );
 end
end

print( "after", #a ); -- 2

 

test here: https://www.Lua.org/cgi-bin/demo

Edited by Kenix
Link to comment
  • Moderators
49 minutes ago, Kenix said:

It cause another problem with render events, it starts call from end

Agree, didn't thought about that one, but the counting up loop would still have the issue of skipping one(or more > context) of the items for that frame. The loop doesn't take into account that the index should stop increasing when an item is removed, as the array is collapsing.

 

 

Would this not cover all the issues?

local i = 1
repeat 
	if a[ i ] == func then
		table.remove( a, i )
	else
		i = i + 1
	end
until not a[ i ]

 

 

 

 

Link to comment
3 hours ago, IIYAMA said:

Agree, didn't thought about that one, but the counting up loop would still have the issue of skipping one(or more > context) of the items for that frame. The loop doesn't take into account that the index should stop increasing when an item is removed, as the array is collapsing.

 

 

Would this not cover all the issues?


local i = 1repeat 	if a[ i ] == func then		table.remove( a, i )	else		i = i + 1	enduntil not a[ i ]

 

 

 

 

I think it's good solution. Code updated.

Link to comment
  • Moderators
7 hours ago, Kenix said:

I think it's good solution. Code updated.

It should be applied on the render function.

The removeRenderEvent is fine as it was.

 

 

The problem:

 

<New frame>

Queue start < fix should be applied here

Func 1

removeRenderEvent (in func 1)

Func 2 (error)

Queue end

 

Link to comment
  • 1 month later...
  • Scripting Moderators
On 19/06/2019 at 12:30, IIYAMA said:

It should be applied on the render function.

The removeRenderEvent is fine as it was.

 

 

The problem:

 

<New frame>

Queue start < fix should be applied here

Func 1

removeRenderEvent (in func 1)

Func 2 (error)

Queue end

 

This problem still exist? 

Link to comment
  • Moderators

@Kenix

@majqq

 

I think the issue is now solved. Had to make some annoying adjustments, before it finally did work in all the different context/scenario's. Now it doesn't matter when and where an item gets remove at any index. When it is remove, it gets temporary replaced by a boolean and removed while iterating/rendering. The render order is correct as well. I know it is a bit of a dirty hack, but it does not :scrambleup: things up as it is in some of the known scenario's.

 

Let me know if there are no new issues occurring.

 

New version: (need some more testing, just in case)

Spoiler

     
------------------------------
-- Authors: IIYAMA and Kenix --
------------------------------

local addEventHandler 		= addEventHandler;
local removeEventHandler 	= removeEventHandler;
local table_remove			= table.remove;
local unpack				= unpack;
local len					= table.getn;
local root					= root;

local renderEvents = {
	"onClientRender",
	"onClientPreRender",
	"onClientHUDRender"
}

local allTargetFunctions = {} -- All attached functions will be stored in this table.

local acceptedRenderEventTypes = {} -- Is type in use? See: renderEvents.
local renderEventTypeStatus = {} -- Is the event in use? (so it doesn't have to be attached again)

do
	-- prepare the data
	for i=1, len( renderEvents ) do
		local event = renderEvents[i]
		allTargetFunctions[event] = {}
		acceptedRenderEventTypes[event] = true
		renderEventTypeStatus[event] = false
	end
end


-- render all events here
local processTargetFunction = function ( timeSlice )
	local targetFunctions = allTargetFunctions[ eventName ]
	
	local i = 1
	local itemCount = len(targetFunctions)
	
	repeat
		if targetFunctions[ i ] == true then -- remove = true
			table_remove( targetFunctions, i )
			itemCount = itemCount - 1
		else
			local targetFunctionData = targetFunctions[i]
			local arguments = targetFunctionData[2]
			
			if not arguments then
				targetFunctionData[ 1 ]( timeSlice )
			else
				if timeSlice then
					targetFunctionData[ 1 ]( timeSlice, unpack( arguments ) )
				else
					targetFunctionData[ 1 ]( unpack( arguments ) )
				end
			end
			
			i = i + 1
		end
	until i > itemCount
end


-- check if a function is already attached
function isRenderEventAdded (theFunction, event)
	if not event or not acceptedRenderEventTypes[event] then
		event = "onClientRender"
	end
	
	local targetFunctions = allTargetFunctions[event]
	
	for i = 1, len( targetFunctions ) do
		if targetFunctions[i] and targetFunctions[i] ~= true and targetFunctions[i][1] == theFunction then
			return true
		end
	end
	
	return false
end

local isRenderEventAdded = isRenderEventAdded

-- add render event, default type is onClientRender
function addRenderEvent(theFunction, event, ...)
	if not event or not acceptedRenderEventTypes[event] then
		event = "onClientRender"
	end
	
	if not isRenderEventAdded(theFunction) then
		local targetFunctions = allTargetFunctions[event]
		
		-- Don't pass an arguments if it not needed.
		local aArgs = { ... };
		local mArgs = len( aArgs ) > 0 and aArgs or nil;
		
		targetFunctions[ len( targetFunctions ) + 1 ] = { theFunction, mArgs }
		
		-- attach an event
		if not renderEventTypeStatus[event] then
			addEventHandler (event, root, processTargetFunction, false, "high")
			renderEventTypeStatus[event] = true
		end
		
		return true
	end
	
	return false
end

-- remove a render event
function removeRenderEvent(theFunction, event)
	if not event or not acceptedRenderEventTypes[event] then
		event = "onClientRender"
	end
	
	local targetFunctions = allTargetFunctions[event]
	
	for i = 1, len( targetFunctions ) do
		if targetFunctions[i] and targetFunctions[i] ~= true and targetFunctions[i][1] == theFunction then
			targetFunctions[i] = true -- true = remove
			
			if len( targetFunctions ) == 0 then
				if renderEventTypeStatus[event] then
					removeEventHandler (event, root, processTargetFunction)
					renderEventTypeStatus[event] = false
				end
			end
			
			return true
		end
	end
	
	return false
end

 

 

 

Edited by IIYAMA
  • Like 1
Link to comment
  • Scripting Moderators
3 hours ago, IIYAMA said:

@Kenix

@majqq

 

I think the issue is now solved. Had to make some annoying adjustments, before it finally did work in all the different context/scenario's. Now it doesn't matter when and where an item gets remove at any index. When it is remove, it gets temporary replaced by a boolean and removed while iterating/rendering. The render order is correct as well. I know it is a bit of a dirty hack, but it does not :scrambleup: things up as it is in some of the known scenario's.

 

Let me know if there are no new issues occurring.

 

New version: (need some more testing, just in case)

  Reveal hidden contents


      -------------------------------- Authors: IIYAMA and Kenix --------------------------------local addEventHandler 		= addEventHandler;local removeEventHandler 	= removeEventHandler;local table_remove			= table.remove;local unpack				= unpack;local len					= table.getn;local root					= root;local renderEvents = {	"onClientRender",	"onClientPreRender",	"onClientHUDRender"}local allTargetFunctions = {} -- All attached functions will be stored in this table.local acceptedRenderEventTypes = {} -- Is type in use? See: renderEvents.local renderEventTypeStatus = {} -- Is the event in use? (so it doesn't have to be attached again)do	-- prepare the data	for i=1, len( renderEvents ) do		local event = renderEvents[i]		allTargetFunctions[event] = {}		acceptedRenderEventTypes[event] = true		renderEventTypeStatus[event] = false	endend-- render all events herelocal processTargetFunction = function ( timeSlice )	local targetFunctions = allTargetFunctions[ eventName ]		local i = 1	local itemCount = len(targetFunctions)		repeat		if targetFunctions[ i ] == true then -- remove = true			table_remove( targetFunctions, i )			itemCount = itemCount - 1		else			local targetFunctionData = targetFunctions[i]			local arguments = targetFunctionData[2]						if not arguments then				targetFunctionData[ 1 ]( timeSlice )			else				if timeSlice then					targetFunctionData[ 1 ]( timeSlice, unpack( arguments ) )				else					targetFunctionData[ 1 ]( unpack( arguments ) )				end			end						i = i + 1		end	until i > itemCountend-- check if a function is already attachedfunction isRenderEventAdded (theFunction, event)	if not event or not acceptedRenderEventTypes[event] then		event = "onClientRender"	end		local targetFunctions = allTargetFunctions[event]		for i = 1, len( targetFunctions ) do		if targetFunctions[i] and targetFunctions[i] ~= true and targetFunctions[i][1] == theFunction then			return true		end	end		return falseendlocal isRenderEventAdded = isRenderEventAdded-- add render event, default type is onClientRenderfunction addRenderEvent(theFunction, event, ...)	if not event or not acceptedRenderEventTypes[event] then		event = "onClientRender"	end		if not isRenderEventAdded(theFunction) then		local targetFunctions = allTargetFunctions[event]				-- Don't pass an arguments if it not needed.		local aArgs = { ... };		local mArgs = len( aArgs ) > 0 and aArgs or nil;				targetFunctions[ len( targetFunctions ) + 1 ] = { theFunction, mArgs }				-- attach an event		if not renderEventTypeStatus[event] then			addEventHandler (event, root, processTargetFunction, false, "high")			renderEventTypeStatus[event] = true		end				return true	end		return falseend-- remove a render eventfunction removeRenderEvent(theFunction, event)	if not event or not acceptedRenderEventTypes[event] then		event = "onClientRender"	end		local targetFunctions = allTargetFunctions[event]		for i = 1, len( targetFunctions ) do		if targetFunctions[i] and targetFunctions[i] ~= true and targetFunctions[i][1] == theFunction then			targetFunctions[i] = true -- true = remove						if len( targetFunctions ) == 0 then				if renderEventTypeStatus[event] then					removeEventHandler (event, root, processTargetFunction)					renderEventTypeStatus[event] = false				end			end						return true		end	end		return falseend

 

 

 

Thanks, i will let you know, if something will not work.

Besides, what do does in this case?

L2Ob0f6.png

Edited by majqq
Link to comment
  • Moderators
4 hours ago, majqq said:

Thanks, i will let you know, if something will not work.

Besides, what do does in this case?

L2Ob0f6.png

It is just an extra block, which runs 1 time. Similar to:

if true then
  
end

 

And no it is not required, but it makes it very clear that some action are happening there.

You more often see those be used to enclose reused variables, without overwrite the original.

local base = 1
if 1 == base then
  do
    local base = 2
    print(base) -- 2
  end
  
  do
    local base = 3
    print(base) -- 3
  end
  print(base) -- 1
end

 

Or delete unused variables:

local a
do
  local b = 100
  a = b * b * b
end

 

  • Like 1
  • Thanks 1
Link to comment
  • Moderators

Updated the topic to latest version.

 

P.s. if you are interested in always receive notifications when the newest version comes out. There is a feature on the forum called: following. This allows you to keep track of updates from a selected topic. It is located on the right + top corner of where the topic starts.

  • Like 1
Link to comment
  • 2 weeks later...
  • Moderators

 

On 14/08/2019 at 23:27, Einheit-101 said:

I find it ridiculous that this isnt a basic thing of Lua itself, it should not eat less performance when functions are stored as local variables...

It is indeed a little bit strange. But on the other side, it does provide a way to optimise access for variables manually. It is just a pity that it can't optimise itself based on patterns of variable requests.

Link to comment
  • 7 months later...
  • IIYAMA changed the title to [ENHANCEMENT] client - Render events enchanting
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...