Skip to content

Refactor event system - #5240

Draft
FileEX wants to merge 11 commits into
multitheftauto:masterfrom
FileEX:refactor/event_system
Draft

Refactor event system#5240
FileEX wants to merge 11 commits into
multitheftauto:masterfrom
FileEX:refactor/event_system

Conversation

@FileEX

@FileEX FileEX commented Aug 19, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a new, more efficient event system. The event system has been somewhat of a bottleneck in MTA for years, and experienced scripters have often recommended limiting the number of handlers attached to frequently triggered events.

The current event system is also unnecessarily complex and performs a significant amount of redundant work. During the investigation of the existing implementation, I found several cases where the same work is repeated unnecessarily.

New event system

The new system is designed to minimize unnecessary iterations and make event dispatch and management considerably cheaper.

Main improvements

  • Faster and more efficient event dispatch
  • Event handlers can be associated with a specific entity type

For example:

addEventHandler('onClientElemenStreamIn', root, function()
	-- code
end, true, 'normal', 'vehicle')

This allows the event system to avoid invoking handlers for entity types they are not interested in.

  • New isEventHandled function to check whether a handler function is already attached to an event

Performance

Thanks to @Dryxio for helping with performance measurements.

Frame time with empty onClientRender handlers (Neon = old events system)
image

At the moment, dispatching 5,000 onClientRender handlers (with empty Lua handlers) takes approximately 0.3 ms on the C++ side, compared to around 11 ms with the old system. These results are not yet fully representative, as they do not account for things such as Lua timing or debug hooks.

Adding 5,000 handlers with the old system takes around 2 seconds (17,022 ms) and causes noticeable lag (the spinning loading circle appears at the bottom of the screen). With the new system, it takes around 200 ms with no visible loading.

local t = getTickCount();
local count = 5000;

for i = 1, count do
	addEventHandler2('onClientRender', root, function()
		
	end, false);
end

print('Added '..count..' handlers in '..(getTickCount() - t)..' ms');

With a 100 FPS cap, the new system currently maintains around 80 FPS with 5K handlers. With the old event system, 5K handlers reduce the framerate to around 25 FPS.

There are still some areas that require further investigation and optimization. For example, CClientPerfStatLuaTiming::GetSingleton()->UpdateLuaTiming currently has a significant impact on performance and can reduce the framerate by roughly 30-40 FPS, resulting in around 40-50 FPS in this particular test.

CLuaArguments may also be another area worth optimizing. I want to investigate these areas further and squeeze as much performance as possible out of the new event system.

Additional changes

For setting event globals such as source, this, etc., I used a Lua-side implementation instead of setting them directly from C++. This approach was inspired by @Pirulax 's PR and appears to be both cleaner and more efficient than the previous implementation.

Scope

For now, this PR only implements the new system on the client side.

Once the remaining issues have been resolved and the implementation is considered ready, the same system can be implemented on the server side (shared).

This PR is intended to introduce the new event system, but not replace the existing system yet.

To keep this PR reasonably sized, I am not converting all existing built-in events to the new system here. That will be done in a separate PR.

The planned migration is therefore:

  1. This PR -> introduce the new event system
  2. Next PR -> migrate the existing built-in events to the new system
  3. Final PR -> completely remove the old event system

Checklist

  • Your code should follow the coding guidelines.
  • Smaller pull requests are easier to review. If your pull request is beefy, your pull request should be reviewable commit-by-commit.

@FileEX FileEX added the enhancement New feature or request label Aug 19, 2026
@FileEX FileEX linked an issue Aug 19, 2026 that may be closed by this pull request
1 task
@FileEX
FileEX marked this pull request as draft August 19, 2026 21:16
@MohabCodeX

Copy link
Copy Markdown
Contributor

What a coincidence, I was actually looking into the event system bottlenecks as well! Really nice work on this, the benchmarks and entityType filtering look great.

Just took a quick look at the code and noticed a couple of small things:

In RemoveHandlersForEntity, it loops through all 80 built-in event maps plus custom ones whenever any entity gets destroyed, which makes every destroyElement an O(80) operation. If a resource creates and deletes lots of temporary entities (like projectiles or markers) that never had handlers attached, that's going to do a bunch of redundant map lookups. Adding a quick boolean flag or counter on CClientEntity to early exit when there are no handlers attached would bring it down to O(1) for all those empty entities.

Also with TriggerEventOnChildren, doing handlersTable.find(child) for every single child might add up on heavy maps when triggering on root with callOnChildren = true.

For the UpdateLuaTiming drop you mentioned, it's probably because of the tag/string resolution per call. Maybe caching the tag when the handler is added or accumulating the delta times per frame could get the profiler stats back without killing the dispatch speed.

Solid PR overall.

@FileEX

FileEX commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

Good point. I added the mentioned handler count for each entity to avoid unnecessary map lookups. Thanks for the review!

@PlatinMTA

Copy link
Copy Markdown
Contributor

Event handlers can be associated with a specific entity type

Thank you so much dude

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add support for element types for addEventHandler

3 participants