Hooks & API

Available in: Server & Client

This concept applies to both environments with behavior adapted for each side.

Every hook is wrapped in pcall individually if one Addon's OnServiceRegistered throws, it's logged as a warning and every other Addon's hooks keep firing normally. One misbehaving Addon can't take down the rest.

The six hooks

Hook Signature Fires
Init (self, owl: AddonAPI) Once per Addon, in dependency order, before any Service/Controller starts OwlInit
OnServiceRegistered (self, service: RegisteredService) Once per Service, as each one is registered via Owl.CreateService
OnControllerRegistered (self, controller: RegisteredController) Once per Controller, as each one is registered
OnFrameworkStarting (self) Right before Owl begins the OwlInit phase for Services/Controllers
OnFrameworkStarted (self) Once every Service/Controller has completed OwlStart
OnFrameworkDestroying (self) When Owl.Destroy() is called

Only Init receives a second argument. Every other hook only needs self by the time OnServiceRegistered fires, your Addon already has everything Init gave it stored on self.

Hooks = {
    Init = function(self, owl)
        self.startedAt = os.clock()
    end,
 
    OnFrameworkStarted = function(self)
        self.Logger.info("Framework fully started, %.2fs after Addon Init.", os.clock() - self.startedAt)
    end,
}

The AddonAPI passed to Init

owl in Init(self, owl) isn't the global Owl table it's a scoped view of it, with two members unique to your Addon layered on top of everything else Owl normally exposes:

Member Notes
owl.Trove A Trove instance unique to this Addon not the framework-wide one. Clean up connections/instances here, not on the global Owl.
owl.Logger Pre-prefixed with "Addon.<Name>", self.Logger.info(...) already tags its own output, no need to repeat the Addon's name in every message.
everything else CreateService, CreateController, GetService, GetAddon, Reflection, Metrics, Config, Util... identical to calling them on Owl directly.
Hooks = {
    Init = function(self, owl)
        self.Trove = owl.Trove
        self.Logger = owl.Logger
 
        self.Logger.info("Registered as an Owl Addon.")
    end,
}

owl.GetProvider appears in the AddonAPI type but isn't implemented anywhere in the framework yet calling it will fail. Don't rely on it until it's actually wired up.

Looking up registered addons

-- > // By name
local analytics = Owl.GetAddon("AnalyticsAddon")
 
-- > // Every registered addon's name
for _, name in ipairs(Owl.Reflection.GetAddons()) do
    print("Loaded addon:", name)
end
 
-- > // Diagnostics snapshot for one addon
local info = Owl.Reflection.GetAddonInfo("AnalyticsAddon")
if info then
    print(string.format("%s v%s started: %s", info.Name, info.Version, tostring(info.Started)))
 
    if info.InitError then
        warn("Init failed:", info.InitError)
    end
end

Owl.Reflection.GetAddonInfo is the fastest way to check why an Addon didn't come up correctly InitError holds the caught error message if its Init hook threw, without you needing to go dig through server logs for the exact warning.

Where to go next

  • Addons - Overview - creating and registering an Addon, Priority, and Dependencies.
  • Automatic Hooks - the equivalent hook system for Services/Controllers, if you haven't seen that pattern yet.