{"id":15722503,"url":"https://github.com/insality/defold-event","last_synced_at":"2025-04-14T03:08:48.283Z","repository":{"id":225485305,"uuid":"766094372","full_name":"Insality/defold-event","owner":"Insality","description":"Cross-Context Defold Event System","archived":false,"fork":false,"pushed_at":"2025-03-29T23:58:54.000Z","size":680,"stargazers_count":52,"open_issues_count":1,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-14T03:08:41.276Z","etag":null,"topics":["defold","defold-extension","defold-library","defold-module","event","profiling"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Insality.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"insality","ko_fi":"insality","buy_me_a_coffee":"insality"}},"created_at":"2024-03-02T10:21:20.000Z","updated_at":"2025-04-11T20:49:42.000Z","dependencies_parsed_at":"2024-10-24T16:50:41.904Z","dependency_job_id":"e6e7d500-0cda-4ccb-a25c-52ce7551d9ec","html_url":"https://github.com/Insality/defold-event","commit_stats":null,"previous_names":["insality/defold-event"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insality%2Fdefold-event","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insality%2Fdefold-event/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insality%2Fdefold-event/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insality%2Fdefold-event/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Insality","download_url":"https://codeload.github.com/Insality/defold-event/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248813795,"owners_count":21165634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["defold","defold-extension","defold-library","defold-module","event","profiling"],"created_at":"2024-10-03T22:08:11.859Z","updated_at":"2025-04-14T03:08:48.259Z","avatar_url":"https://github.com/Insality.png","language":"Lua","funding_links":["https://github.com/sponsors/insality","https://ko-fi.com/insality","https://buymeacoffee.com/insality","https://www.buymeacoffee.com/insality"],"categories":[],"sub_categories":[],"readme":"![](media/logo.png)\n\n[![GitHub release (latest by date)](https://img.shields.io/github/v/tag/insality/defold-event?style=for-the-badge\u0026label=Release)](https://github.com/Insality/defold-event/tags)\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/insality/defold-event/ci_workflow.yml?style=for-the-badge)](https://github.com/Insality/defold-event/actions)\n[![codecov](https://img.shields.io/codecov/c/github/Insality/defold-event?style=for-the-badge)](https://codecov.io/gh/Insality/defold-event)\n\n[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge\u0026logo=GitHub-Sponsors\u0026logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge\u0026logo=ko-fi\u0026logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge\u0026logo=buy-me-a-coffee\u0026logoColor=black)](https://www.buymeacoffee.com/insality)\n\n\n# Event\n\n**Event** - is a single file Lua module for the [Defold](https://defold.com/) game engine. It provides a simple and efficient way to manage events and callbacks in your game.\n\n\n## Features\n\n- **Event Management**: Create, subscribe, unsubscribe, and trigger events.\n- **Cross-Context**: You can subscribe to events from different scripts.\n- **Callback Management**: Attach callbacks to events with optional data.\n- **Global Events**: Create and subscribe global events that can be triggered from anywhere in your game.\n- **Logging**: Set a logger to log event activities.\n- **Memory Allocations Tracker**: Detects if an event callback causes a huge memory allocations.\n\n\n## Setup\n\n### [Dependency](https://www.defold.com/manuals/libraries/)\n\nOpen your `game.project` file and add the following line to the dependencies field under the project section:\n\n**[Defold Event](https://github.com/Insality/defold-event/archive/refs/tags/10.zip)**\n\n```\nhttps://github.com/Insality/defold-event/archive/refs/tags/10.zip\n```\n\n### Library Size\n\n\u003e **Note:** The library size is calculated based on the build report per platform\n\n| Platform         | Library Size |\n| ---------------- | ------------ |\n| HTML5            | **2.07 KB**  |\n| Desktop / Mobile | **3.54 KB**  |\n\n\n### Memory Allocation Tracking\n\n**Enabling in `game.project`**\n\nTo monitor memory allocations for event callbacks, add to your `game.project`:\n\n```ini\n[event]\nmemory_threshold_warning = 50\n```\n\n- `memory_threshold_warning`: Threshold in kilobytes for logging warnings about memory allocations. `0` disables tracking.\n\nThe event memory tracking is not 100% accurate and is used to check unexpected huge leaks in the event callbacks. The memory tracking applied additional memory allocations for tracking purposes.\n\nMemory allocation tracking is turned off in release builds, regardless of the `game.project` settings.\n\n\n## API Reference\n\n### Quick API Reference\n\n```lua\nlocal event = require(\"event.event\")\nevent.set_logger(logger)\nevent.set_memory_threshold(threshold)\n\nlocal event_instance = event.create(callback, [callback_context])\nevent_instance:subscribe(callback, [callback_context])\nevent_instance:unsubscribe(callback, [callback_context])\nevent_instance:is_subscribed(callback, [callback_context])\nevent_instance:trigger(...)\nevent_instance:is_empty()\nevent_instance:clear()\n\nlocal events = require(\"event.events\")\nevents.subscribe(name, callback, [callback_context])\nevents.unsubscribe(name, callback, [callback_context])\nevents.is_subscribed(name, callback, [callback_context])\nevents.trigger(name, ...)\nevents.is_empty(name)\nevents.clear(name)\nevents.clear_all()\n```\n\n### Setup and Initialization\n\nTo start using the Event module in your project, you first need to import it. This can be done with the following line of code:\n\n```lua\nlocal event = require(\"event.event\")\n```\n\n### Core Functions\n\n**event.create**\n---\n```lua\nevent.create(callback, [callback_context])\n```\nGenerate a new event instance. This instance can then be used to subscribe to and trigger events. The `callback` function will be called when the event is triggered. The `callback_context` parameter is optional and will be passed as the first parameter to the callback function. Usually, it is used to pass the `self` instance. Allocate `64` bytes per instance.\n\n- **Parameters:**\n  - `callback`: The function to be called when the event is triggered. Or the event instance to subscribe.\n  - `callback_context` (optional): The first parameter to be passed to the callback function.\n\n- **Return Value:** A new event instance.\n\n- **Usage Example:**\n\n```lua\nlocal function callback(self)\n\tprint(\"clicked!\")\nend\n\nfunction init(self)\n\tself.on_click_event = event.create(callback, self)\nend\n```\n\n### Event Instance Methods\n\nOnce an event instance is created, you can interact with it using the following methods:\n\n**event:subscribe**\n---\n```lua\nevent:subscribe(callback, [callback_context])\n```\nSubscribe a callback to the event or other event. The callback will be invoked whenever the event is triggered. The `callback_context` parameter is optional and will be passed as the first parameter to the callback function. If the callback with context is already subscribed, the warning will be logged. Allocate `160` bytes per first subscription and `104` bytes per next subscriptions.\n\n- **Parameters:**\n  - `callback`: The function to be executed when the event occurs, or another event instance.\n  - `callback_context` (optional): The first parameter to be passed to the callback function. Not used for event instance.\n\n- **Return Value:** `true` if the subscription was successful, `false` otherwise.\n\n- **Usage Example:**\n\n```lua\non_click_event:subscribe(callback, self)\n```\n\nYou can subscribe an other event instance to be triggered by the event. Example:\n```lua\nevent_1 = event.create(callback)\nevent_2 = event.create()\nevent_2:subscribe(event_1) -- Now event2 will trigger event1\nevent_2:trigger() -- callback from event1 will be called\n```\n\n**event:unsubscribe**\n---\n```lua\nevent:unsubscribe(callback, [callback_context])\n```\nRemove a previously subscribed callback from the event. The `callback_context` should be the same as the one used when subscribing the callback. If there is no `callback_context` provided, all callbacks with the same function will be unsubscribed.\n\n- **Parameters:**\n  - `callback`: The callback function to unsubscribe, or the event instance to unsubscribe.\n  - `callback_context` (optional): The first parameter to be passed to the callback function. If not provided, will unsubscribe all callbacks with the same function. Not used for event instances.\n\n- **Return Value:** `true` if the unsubscription was successful, `false` otherwise.\n\n- **Usage Example:**\n\n```lua\non_click_event:unsubscribe(callback, self)\n```\n\n**event:is_subscribed**\n---\n```lua\nevent:is_subscribed(callback, [callback_context])\n```\nDetermine if a specific callback is currently subscribed to the event. The `callback_context` should be the same as the one used when subscribing the callback.\n\n- **Parameters:**\n  - `callback`: The callback function in question. Or the event instance to check.\n  - `callback_context` (optional): The first parameter to be passed to the callback function.\n\n- **Return Value:** `true` if the callback is subscribed to the event, `false` otherwise.\n\n- **Usage Example:**\n\n```lua\nlocal is_subscribed = on_click_event:is_subscribed(callback, self)\n```\n\n**event:trigger**\n---\n```lua\nevent:trigger(...)\n```\nTrigger the event, causing all subscribed callbacks to be executed. Any parameters passed to `trigger` will be forwarded to the callbacks. The return value of the last executed callback is returned. The `event:trigger(...)` can be called as `event(...)`.\n\n- **Parameters:** Any number of parameters to be passed to the subscribed callbacks.\n\n- **Return Value:** The return value of the last callback executed.\n\n- **Usage Example:**\n\n```lua\non_click_event:trigger(\"arg1\", \"arg2\")\n\n-- The event can be triggered as a function\non_click_event(\"arg1\", \"arg2\")\n```\n\n**event:is_empty**\n---\n```lua\nevent:is_empty()\n```\nCheck if the event has no subscribed callbacks.\n\n- **Return Value:** `true` if the event has no subscribed callbacks, `false` otherwise.\n\n- **Usage Example:**\n\n```lua\nlocal is_empty = on_click_event:is_empty()\n```\n\n**event:clear**\n---\n```lua\nevent:clear()\n```\nRemove all callbacks subscribed to the event, effectively resetting it.\n\n- **Usage Example:**\n\n```lua\non_click_event:clear()\n```\n\n\n### Configuration Functions\n\n**event.set_logger**\n---\nCustomize the logging mechanism used by Event module. You can use **Defold Log** library or provide a custom logger. By default, the module uses the `pprint` logger.\n\n```lua\nevent.set_logger([logger_instance])\n```\n\n- **Parameters:**\n  - `logger_instance` (optional): A logger object that follows the specified logging interface, including methods for `trace`, `debug`, `info`, `warn`, `error`. Pass `nil` to remove the default logger.\n\n- **Usage Example:**\n\nUsing the [Defold Log](https://github.com/Insality/defold-log) module:\n```lua\n-- Use defold-log module\nlocal log = require(\"log.log\")\nlocal event = require(\"event.event\")\n\nevent.set_logger(log.get_logger(\"event\"))\n```\n\nCreating a custom user logger:\n```lua\n-- Create a custom logger\nlocal logger = {\n    trace = function(_, message, context) end,\n    debug = function(_, message, context) end,\n    info = function(_, message, context) end,\n    warn = function(_, message, context) end,\n    error = function(_, message, context) end\n}\nevent.set_logger(logger)\n```\n\nRemove the default logger:\n```lua\nevent.set_logger(nil)\n```\n\n**event.set_memory_threshold**\n---\nSet the threshold for logging warnings about memory allocations in event callbacks. Works only in debug builds. The threshold is in kilobytes. If the callback causes a memory allocation greater than the threshold, a warning will be logged.\n\n```lua\nevent.set_memory_threshold(threshold)\n```\n\n- **Parameters:**\n  - `threshold`: Threshold in kilobytes for logging warnings about memory allocations. `0` disables tracking.\n\n- **Usage Example:**\n\n```lua\nevent.set_memory_threshold(50)\nevent.set_memory_threshold(0) -- Disable tracking\n```\n\n\n### Global Events Module\n\nThe Event library comes with a global events module that allows you to create and manage global events that can be triggered from anywhere in your game. This is particularly useful for events that need to be handled by multiple scripts or systems.\n\nTo start using the **Events** module in your project, you first need to import it. This can be done with the following line of code:\n\nGlobal events module requires careful management of subscriptions and unsubscriptions to prevent errors.\n\n\n```lua\nlocal events = require(\"event.events\")\n```\n\n**events.subscribe**\n---\n```lua\nevents.subscribe(name, callback, [callback_context])\n```\nSubscribe a callback to the specified global event.\n\n- **Parameters:**\n  - `name`: The name of the global event to subscribe to.\n  - `callback`: The function to be executed when the global event occurs.\n  - `callback_context` (optional): The first parameter to be passed to the callback function.\n\n- **Usage Example:**\n\n```lua\nfunction init(self)\n\tevents.subscribe(\"on_game_over\", callback, self)\nend\n```\n\n**events.unsubscribe**\n---\n```lua\nevents.unsubscribe(name, callback, [callback_context])\n```\nRemove a previously subscribed callback from the specified global event. The `callback_context` should be the same as the one used when subscribing the callback. If there is no `callback_context` provided, all callbacks with the same function will be unsubscribed.\n\n- **Parameters:**\n  - `name`: The name of the global event to unsubscribe from.\n  - `callback`: The callback function to unsubscribe.\n  - `callback_context` (optional): The first parameter to be passed to the callback function. If not provided, will unsubscribe all callbacks with the same function.\n\n- **Usage Example:**\n\n```lua\nfunction final(self)\n\tevents.unsubscribe(\"on_game_over\", callback, self)\nend\n```\n\n**events.is_subscribed**\n---\n```lua\nevents.is_subscribed(name, callback, [callback_context])\n```\nDetermine if a specific callback is currently subscribed to the specified global event.\n\n- **Parameters:**\n  - `name`: The name of the global event in question.\n  - `callback`: The callback function in question.\n  - `callback_context` (optional): The first parameter to be passed to the callback function.\n\n- **Return Value:** `true` if the callback is subscribed to the global event, `false` otherwise.\n\n- **Usage Example:**\n\n```lua\nlocal is_subscribed = events.is_subscribed(\"on_game_over\", callback, self)\n```\n\n**events.trigger**\n---\n```lua\nevents.trigger(name, ...)\n```\nThrow a global event with the specified name. All subscribed callbacks will be executed. Any parameters passed to `trigger` will be forwarded to the callbacks. The return value of the last executed callback is returned.\n\n- **Parameters:**\n  - `name`: The name of the global event to trigger.\n  - `...`: Any number of parameters to be passed to the subscribed callbacks.\n\n- **Usage Example:**\n\n```lua\nevents.trigger(\"on_game_over\", \"arg1\", \"arg2\")\n```\n\n**events.is_empty**\n---\n```lua\nevents.is_empty(name)\n```\nCheck if the specified global event has no subscribed callbacks.\n\n- **Parameters:**\n  - `name`: The name of the global event to check.\n\n- **Return Value:** `true` if the global event has no subscribed callbacks, `false` otherwise.\n\n- **Usage Example:**\n\n```lua\nlocal is_empty = events.is_empty(\"on_game_over\")\n```\n\n**events.clear**\n---\n```lua\nevents.clear(name)\n```\nRemove all callbacks subscribed to the specified global event.\n\n- **Parameters:**\n  - `name`: The name of the global event to clear.\n\n- **Usage Example:**\n\n```lua\nevents.clear(\"on_game_over\")\n```\n\n**events.clear_all**\n---\n```lua\nevents.clear_all()\n```\nRemove all callbacks subscribed to all global events.\n\n- **Usage Example:**\n\n```lua\nevents.clear_all()\n```\n\nThe **Events** module provides a powerful and flexible way to manage global events in your Defold projects. Use it to create modular and extensible systems that can respond to events from anywhere in your game.\n\n\n## Use Cases\n\nRead the [Use Cases](USE_CASES.md) file to see several examples of how to use the Event module in your Defold game development projects.\n\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\nUsed libraries:\n- [Lua Script Instance](https://github.com/DanEngelbrecht/LuaScriptInstance/)\n\n\n## Issues and suggestions\n\nIf you have any issues, questions or suggestions please [create an issue](https://github.com/Insality/defold-event/issues).\n\n\n## 👏 Contributors\n\n\u003ca href=\"https://github.com/Insality/defold-event/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contributors-img.web.app/image?repo=insality/defold-event\"/\u003e\n\u003c/a\u003e\n\n\n## Changelog\n\n\u003cdetails\u003e\n\n### **V1**\n\t- Initial release\n\n### **V2**\n\t- Add global events module\n\t- The `event:subscribe` and `event:unsubscribe` now return boolean value of success\n\n### **V3**\n\t- Event Trigger now returns value of last executed callback\n\t- Add `events.is_empty(name)` function\n\t- Add tests for Event and Global Events modules\n\n\n### **V4**\n\t- Rename `lua_script_instance` to `event_context_manager` to escape conflicts with `lua_script_instance` library\n\t- Fix validate context in `event_context_manager.set`\n\t- Better error messages in case of invalid context\n\t- Refactor `event_context_manager`\n\t- Add tests for event_context_manager\n\t- Add `event.set_memory_threshold` function. Works only in debug builds.\n\n### **V5**\n\t- The `event:trigger(...)` can be called as `event(...)` via `__call` metamethod\n\t- Add default pprint logger. Remove or replace it with `event.set_logger()`\n\t- Add tests for context changing\n\n### **V6**\n\t- Optimize memory allocations per event instance\n\t- Localize functions in the event module for better performance\n\n### **V7**\n\t- Optimize memory allocations per event instance\n\t- Default logger now empty except for errors\n\n### **V8**\n\t- Optimize memory allocations per subscription (~35% less)\n\n### **V9**\n\t- Better error tracebacks in case of error in subscription callback\n\t- Update annotations\n\n### **V10**\n\t- The `event:unsubscribe` now removes all subscriptions with the same function if `callback_context` is not provided\n\t- You can use events instead callbacks in `event:subscribe` and `event:unsubscribe`. The subcribed event will be triggered by the parent event trigger.\n\t- Update docs and API reference\n\u003c/details\u003e\n\n## ❤️ Support project ❤️\n\nYour donation helps me stay engaged in creating valuable projects for **Defold**. If you appreciate what I'm doing, please consider supporting me!\n\n[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge\u0026logo=GitHub-Sponsors\u0026logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge\u0026logo=ko-fi\u0026logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge\u0026logo=buy-me-a-coffee\u0026logoColor=black)](https://www.buymeacoffee.com/insality)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finsality%2Fdefold-event","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finsality%2Fdefold-event","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finsality%2Fdefold-event/lists"}