DEMI-PROJECT-PACKAGE ¾{"files":[{"hash":"sha256:08335954d192159053e1a683f2058963909e7f53fd68e5c14ecc6cb5f4120a3f","path":"README.md","size":1652},{"hash":"sha256:ea3ee97970442c6e6c5ee42f5f0cd70a1a4c11b2a3de5ae3f08a9f9ce13ef29c","path":"scripts/demi/gameplay/events.lua","size":1283},{"hash":"sha256:a4fc86fc29887c74e1a7073f47e384bd8e9260e37a0aaac2e16e6f1a6bac345b","path":"tests/events_test.lua","size":665}],"format_version":1,"manifest":{"dependencies":{},"engine":"^0.1.0","exported_events":[],"files":["README.md","scripts/demi/gameplay/events.lua","tests/events_test.lua"],"format_version":1,"name":"demi.gameplay.events","public_modules":["demi.gameplay.events"],"version":"1.0.0"},"package_type":"DemiProjectPackage"}# Gameplay events `demi.gameplay.events` connects Lua gameplay systems through named, queued events. Each queue belongs to its caller. Create one per scene, game, or subsystem; there is no global event bus and no dependency on physics or rendering. ## Use Install from this checkout's development registry: ```sh demi package add demi.gameplay.events@1.0.0 --registry /path/to/DemiEngine/packages ``` ```lua local Events = require("demi.gameplay.events") local events = Events.new() local unsubscribe = events:on("door_opened", function(payload) print("Opened " .. payload.id) end) events:emit("door_opened", {id = "front_door"}) events:flush() unsubscribe() ``` `emit(name, payload)` queues an event; it does not call listeners immediately. `flush()` dispatches queued events in emission order. Higher listener priorities run first (the optional third argument to `on`); equal priorities retain registration order. `on` returns an unsubscribe function. Events emitted by a listener are processed during the same flush. Avoid cycles that keep emitting indefinitely. Payloads are Lua references, not snapshots; mutating a table before flushing changes what listeners receive. Callback errors propagate to the caller. This is a local gameplay queue, not a network transport or persistent event log. ## Development This package replaces the development-era name `demi.gameplay.core`. There is no alias package. The Lua import remains `demi.gameplay.events`. Reinstall dependent packages from the local registry to refresh their manifests and lock hashes. Run `demi package test packages/sources/demi.gameplay.events` from the engine root. local Events = {} Events.__index = Events function Events.new() return setmetatable({ listeners = {}, queue = {}, next_order = 1 }, Events) end function Events:on(name, callback, priority) assert(type(name) == "string" and type(callback) == "function") local entry = { callback = callback, priority = priority or 0, order = self.next_order, active = true } self.next_order = self.next_order + 1 self.listeners[name] = self.listeners[name] or {} table.insert(self.listeners[name], entry) return function() entry.active = false end end function Events:emit(name, payload) table.insert(self.queue, { name = name, payload = payload }) end function Events:flush() while #self.queue > 0 do local event = table.remove(self.queue, 1) local listeners = self.listeners[event.name] or {} table.sort(listeners, function(a, b) return a.priority == b.priority and a.order < b.order or a.priority > b.priority end) for index = 1, #listeners do if listeners[index].active then listeners[index].callback(event.payload) end end local retained = {} for index = 1, #listeners do if listeners[index].active then table.insert(retained, listeners[index]) end end self.listeners[event.name] = retained end end return Events local Test = require("demi.test") local Events = require("demi.gameplay.events") Test.case("priority and registration order are deterministic", function() local bus, order = Events.new(), {} bus:on("hit", function() table.insert(order, "late") end, 0) bus:on("hit", function() table.insert(order, "first") end, 10) bus:emit("hit", {}) bus:flush() Test.equal(table.concat(order, ","), "first,late") end) Test.case("unsubscribe during dispatch is safe", function() local bus, calls, stop = Events.new(), 0 stop = bus:on("tick", function() calls = calls + 1; stop() end) bus:emit("tick"); bus:emit("tick"); bus:flush() Test.equal(calls, 1) end)