# How event.register_handler Callbacks Process Menu and Game Events in YimMenuV2

> Discover how YimMenuV2's event.register_handler processes menu and game events. Learn about callback registration and event dispatching in the Event.cpp source.

- Repository: [YimMenu/YimMenuV2](https://github.com/YimMenu/YimMenuV2)
- Tags: how-to-guide
- Published: 2026-07-17

---

**TLDR:** `event.register_handler` **stores Lua function references in a global** `g_eventHandlers` **map inside** [`src/game/scripting/libraries/Event.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/scripting/libraries/Event.cpp)**, which the main loop iterates each frame via `LuaManager::Update()` to dispatch queued game and menu events to registered callbacks.**

YimMenuV2 exposes a lightweight Lua scripting layer that allows modders to react to in-game actions and UI interactions through a unified event system. Understanding how `event.register_handler` callbacks process these events requires examining the C++ implementation in the scripting libraries, the global handler registry, and the main update loop that drives execution.

## Event Registration Architecture

### The Handler Registry in Event.cpp

At the core of the system is [`src/game/scripting/libraries/Event.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/scripting/libraries/Event.cpp), which implements the `RegisterHandler` C-function exposed to Lua as `event.register_handler`. When a script invokes this function, the implementation extracts the event name string and a `sol::function` reference, storing them in a global `std::unordered_map<std::string, std::vector<sol::function>>` named `g_eventHandlers`.

This design allows multiple callbacks for a single event type. The map key is the event identifier (e.g., `"entity.damage"` or `"menu.select"`), while the value is a vector of Lua function references maintained alive by the Sol2 binding library.

### Lua-to-C++ Binding

The registration process marshals Lua arguments into C++ using Sol2’s abstraction layer. The `RegisterHandler` function validates the event name, checks for duplicates if necessary, and pushes the callback into the vector associated with that event key. Because the script engine holds these references, the Lua callbacks remain valid until explicitly unregistered or the script is unloaded.

## Event Dispatch Pipeline

### Game Loop Integration

Event dispatch occurs on the main game thread inside [`src/core/scripting/LuaManager.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/scripting/LuaManager.cpp). Each tick, the `LuaManager::Update()` method invokes `Event::ProcessPending()`, which iterates over a thread-local queue called `g_pendingEvents`. This queue contains events triggered by native game code or UI actions during the current frame.

For each queued event, the system looks up `g_eventHandlers[eventName]` and invokes every stored `sol::function` sequentially. Arguments are passed using `sol::variadic_args`, allowing callbacks to receive variable-length parameter lists (e.g., victim ID, attacker ID, and damage amount for combat events).

### Menu Event Propagation

The UI layer in [`src/game/frontend/Menu.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/frontend/Menu.cpp) utilizes the same dispatch mechanism. When a user interacts with a widget (e.g., pressing a button or changing a dropdown), the menu code calls `event.trigger("menu.buttonPress", buttonId)` or similar. These calls enqueue the event into `g_pendingEvents`, ensuring that menu actions and game events share a consistent processing pipeline.

Because both event types flow through `Event::ProcessPending()`, scripts can register handlers for game events like `"player.join"` alongside menu events like `"menu.tabChange"` without distinguishing between the two at the registration level.

## Callback Lifecycle and Thread Safety

### Unregistering Handlers

Scripts can remove callbacks via `event.unregister_handler` (implemented as the `UnregisterHandler` C-function in [`Event.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/Event.cpp)). This function searches `g_eventHandlers` for the specific event name, locates the matching `sol::function` reference in the vector, and erases it. Once removed, the Sol2 reference is released, allowing Lua’s garbage collector to reclaim the function if no other references exist.

### Thread-Safety Guarantees

All handler registration and dispatch operations occur synchronously on the main game thread. The `g_eventHandlers` map and `g_pendingEvents` queue are not protected by mutexes because `LuaManager::Update()` runs sequentially with the game logic, eliminating race conditions between script callbacks and game state modifications.

## Practical Code Examples

### Registering a Game Event Handler

The following Lua snippet registers a callback for player damage events, allowing the script to modify or log combat data:

```lua
-- Register callback for entity damage events
event.register_handler("entity.damage", function(victim, attacker, damage)
    print(string.format("Entity %d took %f damage from %d", victim, damage, attacker))
    -- Apply custom logic, e.g., damage multipliers or alerts
end)

```

### Handling Menu Interactions

To execute code when a specific menu button is pressed, register a handler for the menu event namespace:

```lua
-- React to menu button presses
event.register_handler("menu.buttonPress", function(buttonId)
    if buttonId == "godmodeToggle" then
        -- Toggle god mode logic here
        print("God mode toggled via menu")
    end
end)

```

### Triggering Events from C++

Native code can fire events that Lua scripts receive by pushing to the pending queue. This example from [`src/game/scripting/libraries/Event.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/scripting/libraries/Event.cpp) demonstrates the internal trigger mechanism:

```cpp
// Called by game systems or UI code to queue an event
void TriggerGameEvent(const std::string& name, sol::variadic_args args) {
    // Pack arguments and push to the queue for processing next frame
    g_pendingEvents.emplace_back(name, args);
}

```

### Unregistering a Callback

To prevent memory leaks or stop processing specific events, remove the handler when no longer needed:

```lua
local myCallback = function(...) print("Event fired") end
event.register_handler("custom.event", myCallback)

-- Later cleanup
event.unregister_handler("custom.event", myCallback)

```

## Summary

- **Centralized Registry**: `event.register_handler` stores callbacks in a global `g_eventHandlers` map defined in [`src/game/scripting/libraries/Event.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/scripting/libraries/Event.cpp), keyed by event name.
- **Unified Dispatch**: Both game events (e.g., `"entity.damage"`) and menu events (e.g., `"menu.select"`) flow through the same `g_pendingEvents` queue processed by `Event::ProcessPending()`.
- **Main Thread Execution**: `LuaManager::Update()` in [`src/core/scripting/LuaManager.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/scripting/LuaManager.cpp) drives event dispatch on the main game thread, ensuring thread-safe access to game state.
- **Lifecycle Management**: Handlers persist until explicitly removed via `event.unregister_handler`, which releases the underlying `sol::function` reference.

## Frequently Asked Questions

### How do I unregister an event handler in YimMenuV2?

Call `event.unregister_handler(eventName, callbackFunction)` with the same function reference used during registration. The `UnregisterHandler` implementation in [`src/game/scripting/libraries/Event.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/scripting/libraries/Event.cpp) searches the `g_eventHandlers` map, removes the matching `sol::function` from the vector, and releases the reference to prevent memory leaks.

### Are event handlers thread-safe?

Yes, because all event operations execute on the main game thread. The `LuaManager::Update()` method processes the `g_pendingEvents` queue synchronously during the frame update, eliminating the need for locks or mutexes when accessing the handler registry or game state from within callbacks.

### Can Lua scripts trigger custom events that other scripts receive?

Yes. Scripts can call `event.trigger(eventName, ...)` (exposed through bindings in [`src/core/scripting/libraries/Notify.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/scripting/libraries/Notify.cpp)) to enqueue custom events. These events populate `g_pendingEvents` and are dispatched to all registered handlers during the next `Event::ProcessPending()` cycle, allowing cross-script communication.

### What is the difference between game events and menu events in the handler system?

Technically, there is no architectural difference. Game events originate from native game code (e.g., player damage, vehicle spawns), while menu events originate from [`src/game/frontend/Menu.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/frontend/Menu.cpp) (e.g., button presses). Both use the same `event.trigger` mechanism and `g_eventHandlers` registry, allowing scripts to handle UI and gameplay logic through identical callback patterns.