Skip to main content

Phases and events

Every element in the descriptor (a bean, an auto-invoked function) carries the phase at which the runtime acts on it. The runtime walks a plugin's descriptor at lifecycle events and dispatches only the elements whose phase applies.

Phases​

Phase in dev.versatia:

PhaseApplies to the events
PLUGIN_START_ONLYSTART
PLUGIN_RELOAD_ONLYRELOAD
PLUGIN_RELOAD_OR_STARTSTART, RELOAD
PLUGIN_STOPSTOP
UNSETnone; a technical "not given", never present in a descriptor

Lifecycle events​

LifecycleEventTriggered by
STARTthe plugin's onEnable
RELOAD/versatia reload, plugin.versatiaReload() or VersatiaEngine.reload(name); see Reloading plugins
STOPthe plugin's onDisable

The bean scopes run at PLUGIN_START_ONLY: beans are created once and kept. The main class's @PluginBean runs at PLUGIN_RELOAD_OR_START: at a reload it reloads the plugin's Reloadable beans instead. @Value properties are set at PLUGIN_RELOAD_OR_START, before the reloadable beans and the functions. Functions pick their phase per occurrence; put what reads configuration at PLUGIN_RELOAD_OR_START, so that a reload re-reads it, and keep PLUGIN_START_ONLY for what the server allows once. A BackgroundTask needs no phase: it begins at the tick after the start and is restarted by every reload.

What happens at stop​

When a plugin is disabled, the STOP event goes in a fixed order:

  1. Every coroutine of the plugin, its background tasks and what it launched from its PluginScope, is cancelled; the runtime runs them on the server thread until they have finished or five seconds have passed, so their finally blocks are done.
  2. The PLUGIN_STOP functions run, best-effort: one that fails does not keep the others from running.
  3. The plugin's state is dropped: its beans are closed, last created first, and the beans it exposed disappear. Bukkit then unregisters its listeners and cancels its Bukkit tasks.

A stop function can therefore use every bean, and no coroutine of the plugin runs alongside it. With @DisableVersatiaFeatures(runBackgroundTasks = true) step 1 is left out, and the scope is cancelled only in step 3, when the beans are closed.

How an annotation declares its phase​

Authors of plain plugins never touch this; it matters when you define your own enhancement. @VersatiaEnhancement must say when its processor runs, in exactly one of two ways:

// Fixed for every occurrence
@VersatiaEnhancement(BeanProcessors.LOCAL, enhancementPhase = Phase.PLUGIN_START_ONLY)
annotation class LocalBean

// Chosen per occurrence through a property of the annotation
@VersatiaEnhancement(LifecycleProcessors.AUTO_INVOKE, enhancementPhaseParamRefName = "phase")
annotation class AutoInvoke(val phase: Phase)

With enhancementPhaseParamRefName, the named property must exist on the annotation, be of type Phase, and every occurrence must set it to something other than UNSET. Setting both forms or neither is a compile error.

When a scope is inherited (@Service inherits @LocalBean), the phase is read from the occurrence that links the two, that is from the @LocalBean written on the declaration of Service.