Propagating events
#1
Hey guys, awesome work!
I've found this masterpiece two days ago and didn't have the time to further investigate the code - sorry for that! I hope this is the correct section for this.

I noticed, that event's are globally registered.
(An example of what I mean: One event HOOK_PLAYER_MOVING, raised for every player)
Many plugins (especially mini-games or stuff like MagicCarpet) are only interested in the events of some players. Coulnd't it significantly improve the performance of these plugins if events were propagated to the affected entity (for example the player) where they can then be selectively registered by plugins? Leaving the global events only for plugins that really need all fired events.

So MagicCarpet would look something like this (using pseudo-API for removing hook):
local CarpetHooks = {} -- array with hooks

-- bind Command
PluginManager:BindCommand("/mc", "magiccarpet", HandleCarpetCommand, " - Spawns a magical carpet")

function HandleCarpetCommand(Player){
	C_Hook = CarpetHooks[ Player ]
	
	if( C_Hook == nil ) then
		-- register move-event for player - who requested carpet
		CarpetHooks[ Player ] = Player.AddHook(HOOK_PLAYER_MOVE, OnCarpetPlayerMove)
	else
		-- deregister move-event for player - who disabled carpet
		C_Hook::remove() -- pseudo-API for removing hooks
		CarpetHooks[ Player ] = nil
	end
}

function OnCarpetPlayerMove(Player)
	-- handle moving carpet ...
end

This should reduce the amount of invocations into the lua-code (I don't know how expensive these invocations are) - but it should be more expensive than identifiying the affected entity and propagating the events especially on servers with many players, shouldn't it?
But in order to be an improvement, this would need a RemoveHook function, to deregister an event - Is there already something similar?

The only problem I found with this approach are events on blocks: If I want to listen the event of only one block (for example the redstone-status), the chunk containing this block can't be unloaded - even when it is unused - or it is unloaded anyway and the event is deregistered, which would then lead to the handler never being called again - except it is re-registered by the plugin.

Is there another reason (except the amount of work), for the events being global only up to date?
Reply
Thanks given by:
#2
Hello, welcome to the forum.
(I have moved your thread to the Lua discussion subforum, I think this is a better place for it.)
This does make sense for player-based and entity-based hooks, true. It doesn't make much sense for the other hooks.
A slight problem with your approach would be threading - if you removed a hook but it was already trying to execute in a different server thread, the removal code would need to take this into account.

It hasn't been implemented mostly because no-one requested it yet and we have other things to do, usually more important than improving existing API.

I personally think that the performance impact of this hooks being global is negligible, the C++-to-Lua transition is quite fast, so calling it even a few hundred times for a large server shouldn't be that much of a problem. We've had some tests done where the C++ code would call into Lua several hundred thousand times per second and yet it wasn't using the full CPU.
Reply
Thanks given by: Seadragon91
#3
(01-26-2015, 03:22 AM)xoft Wrote: A slight problem with your approach would be threading - if you removed a hook but it was already trying to execute in a different server thread, the removal code would need to take this into account.
Oh okay. I thought that this was no problem, since - while striving the forum - I read somewhere, that Lua-Calls were synchronous.
Just to get my head around this:
Are lua-calls completely asynchronous and then only resynchronized through mutexes or semaphors within API-Calls like SetBlock()?

(01-26-2015, 03:22 AM)xoft Wrote: I personally think that the performance impact of this hooks being global is negligible, the C++-to-Lua transition is quite fast, so calling it even a few hundred times for a large server shouldn't be that much of a problem.
I didn't know how many of these move-events were actually fired, it just sounded like very much to me. And very much multiplied by the amount of players... sounded like even more.Big Grin

If lua-calls are really that fast, then I don't see many advantages left, but one of them might be interesting:
To some extend, the Server-Performance is up to the Plugin-Developer. If the developer's way of deciding if a player is of interest to him is rather slow, deciding only once (OnPlayerJoin and registering all necessary events there) would be significantly faster than doing it every time within the global eventhandler. But, on the fly, I couldn't find an example where this is the case, so this might actually not be of much interest.

(01-26-2015, 03:22 AM)xoft Wrote: It hasn't been implemented mostly because no-one requested it yet and we have other things to do, usually more important than improving existing API.
I might spend some time on that - if you don't mind it being implemented. But I fear that the amount of time needed to implement this is too high.
Reply
Thanks given by:
#4
The lua is synchronous. However multiple threads can call into the c++ hook dispatching code, which has to ensure that only one thread calls into lua at once. More complex dispatching code would have to have more complex synchronization.

If you want to implement it go ahead.
Reply
Thanks given by:
#5
Lua itself is synchronous, but the server around it is not. Before any Lua code is executed, a plugin-specific mutex is acquired. All the API calls that the plugin makes are then executed with that mutex locked. This is the core of the problem - one thread acquires this mutex and calls the Remove API, and in between those two, the world tick thread tries to call the hook. The world tick thread is waiting to acquire the mutex, while the first thread is busy removing the hook. This is the situation that needs to be taken extreme care of.

If you're willing to put some effort into coding the server, may I recommend picking a different task? There are quite a few plugin requests, some of them really relevant and useful, such as the Grief Prevention plugin ( https://forum.cuberite.org/showthread.php?tid=1671 ). Or if you want to code something in C++ instead, how about fixing the crash that occurs when a task is scheduled by a plugin and the plugin is unloaded before the task runs ( https://github.com/mc-server/MCServer/issues/1556 ). These would be, in my personally biased opinion, much more useful Smile
Reply
Thanks given by: Seadragon91




Users browsing this thread: 4 Guest(s)