lib-core.notify

Headless player-facing message routing. Tag-based messages with a channel registry, tag-filter routing, subscriber callbacks, and an engine.print mirror. No render code; no UI.

Version: 0.1.0 Lib-ID: lib-core.notify Requires: engine.time.now (optional, for auto-timestamp), engine.print (optional, for log mirror) Tags: notify, message, channel, routing, hud

Topology

graph LR
  this["lib-core.notify"]
  engine_time["engine.time.now (optional)"]
  engine_print["engine.print (optional)"]
  this -.->|optional| engine_time
  this -.->|optional| engine_print

Scope (v0.1.0)

v0.1 ships the data layer only:

  • Channel registry (create, destroy, list)
  • Tag-filter routing: OR-match on shared tags; empty filter = catch-all
  • Per-channel history ring-buffer (max_history capped, oldest dropped)
  • Subscribe / unsubscribe callbacks (returns opaque handle)
  • Severity sugar wrappers: info, warn, error
  • Engine-log mirror (calls engine.print on every posted message; toggle via set_engine_log_mirror)

Intentional non-goals (deferred):

  • Glob/wildcard tag filters
  • AND-combination or exclude-tag filters
  • Persistent history (disk/savegame serialization)
  • Display rendering (belongs in a future lib-core.notify-display)

Message Schema

Field Type Set by Notes
tags string[] caller (required) Non-empty; drives routing
text string caller (required) Human-readable message body
severity string caller / auto "info" / "warn" / "error"; defaults to "info"
ttl number / nil caller (optional) Display timeout in seconds; toast mode falls back to channel/display default if unset
source any / nil caller (optional) Optional source reference (e.g. originating entity)
data table / nil caller (optional) Structured key/value payload; detail mode renders msg.data.*
id number auto Monotonically increasing
timestamp number / nil auto engine.time.now() if available

Channel Schema

Field Type Required Notes
id string yes Unique channel identifier
filter string[] yes Tag OR-list; {} = catch-all
display_mode string yes "log" / "toast" / "detail"
max_history number no Default: log=200, toast=20, detail=1
title string no Human-readable channel name

Filter Semantics

Routing uses an OR-list: a message is delivered to a channel if at least one of the message's tags appears in the channel's filter array.

An empty filter array ({}) is a catch-all — every message is delivered regardless of its tags.

channel filter = {"combat", "loot"}   message tags = {"loot", "vendor"}
→ match: "loot" in both lists

API

notify.create_channel(def)

Syntax: notify.create_channel(def: table) -> void

Example:

notify.create_channel{
    id           = "combat-log",
    filter       = {"combat", "damage"},
    display_mode = "log",
    max_history  = 100,
    title        = "Combat Log",
}

Registers a new channel. Loud-error on: non-table def, missing or empty def.id, duplicate id, non-table def.filter, unknown def.display_mode. max_history defaults to display-mode default (log=200, toast=20, detail=1).


notify.destroy_channel(channel_id)

Syntax: notify.destroy_channel(channel_id: string) -> void

Removes the channel and all its subscribers. Idempotent (no-op if channel does not exist).


notify.list_channels()

Syntax: notify.list_channels() -> table[]

Returns a shallow-copy array of all registered channel definitions (without internal history). Order is unspecified.


notify.post(msg)

Syntax: notify.post(msg: table) -> void

Example:

notify.post{ tags = {"combat", "damage"}, text = "You hit goblin for 12 damage.", severity = "info" }

Posts a message. Auto-fills id, timestamp, and severity (default "info"). Routes to all channels whose filter matches at least one tag. Appends to channel history (ring-buffered at max_history), invokes all registered subscriber callbacks in insertion order, and mirrors to engine.print if engine_log_mirror is enabled.

Loud-error on: non-table msg, missing or non-array msg.tags, empty msg.tags, non-string tag elements, non-string msg.text.


notify.info(tags, text)

Syntax: notify.info(tags: string[], text: string) -> void

Sugar for M.post{ tags=tags, text=text, severity="info" }.


notify.warn(tags, text)

Syntax: notify.warn(tags: string[], text: string) -> void

Sugar for M.post{ tags=tags, text=text, severity="warn" }.


notify.error(tags, text)

Syntax: notify.error(tags: string[], text: string) -> void

Sugar for M.post{ tags=tags, text=text, severity="error" }.


notify.get_history(channel_id, n)

Syntax: notify.get_history(channel_id: string, n?: number) -> table[]

Example:

local last5 = notify.get_history("combat-log", 5)

Returns a shallow-copy array of the last n messages delivered to the channel, oldest-first within the returned slice. If n is nil or greater than the number of stored messages, returns all stored messages. Returns {} for unknown channels.


notify.subscribe(channel_id, callback)

Syntax: notify.subscribe(channel_id: string, callback: function) -> handle

Example:

local handle = notify.subscribe("combat-log", function(msg)
    hud.append(msg.text)
end)

Registers callback to be invoked whenever a message is routed to channel_id. Returns an opaque handle table required by unsubscribe. Loud-error on unknown channel or non-function callback.


notify.unsubscribe(handle)

Syntax: notify.unsubscribe(handle: table) -> void

Removes the subscription identified by handle. Silent if the subscription or channel no longer exists (idempotent).


notify.set_engine_log_mirror(enable)

Syntax: notify.set_engine_log_mirror(enable: bool) -> void

Enables or disables mirroring every posted message to engine.print. Only true (boolean) enables the mirror; any other value disables it. Enabled by default at module load.


Engine-Log Mirror

When engine_log_mirror is enabled (default), every call to notify.post also calls engine.print(msg.text) if engine.print is available. This provides zero-config visibility in the engine console during development.

Disable in production or when a channel subscriber handles its own output:

notify.set_engine_log_mirror(false)

Test Backdoors

The following functions are for test-only use. Do not call them from game code.

  • notify._test_clear_all() — resets all state to initial values
  • notify._test_get_subscribers(channel_id) — returns raw subscriber list
  • notify._test_get_log_mirror() — returns current engine_log_mirror bool
Description
No description provided
Readme 33 KiB
Languages
Lua 100%