Class ShutdownGate

java.lang.Object
com.ultikits.ultitools.utils.ShutdownGate

final class ShutdownGate extends Object
The shutdown window of the inbound UltiCloud requests that change server state (22-23 shutdown-window round, maintainer decision of 2026-10-26); internal, not for module use.

One signal. ShutdownGate.Signal.enter() is the only question every state-changing path asks: in production it is PluginInitiationUtils.enterPanelChange(), which is true only while PluginInitiationUtils.isAcceptingPanelMessages() holds (the accepting flag UltiTools#onDisable clears as its first step, and Bukkit's isEnabled(), which turns false before onDisable runs). A path that entered holds a shared permit until ShutdownGate.Signal.exit(); stopAcceptingPanelMessages waits for every permit to be returned before onDisable goes on, so a change that started before the disable finishes, and writes its action-log entry, before the action log closes later in onDisable.

Consumers. AgentMessageRouter.dispatch(String, CloudChannel) (every covered type, before the switch gate and the handler, for the whole handler call) and SerialRequestWorker (the covered file and property types again when the worker runs them, since a request queued before the disable may run after it). A request that hands its change to another thread takes a permit of its own for that work and returns it when the work ends, from whichever thread ends it (22-23 second verification): ModuleRequestHandler for the module responder's worker, PlayerLinkChannel for the link handler thread. The command handler reads the same flag through Bindings#disabling on the server thread; work handed to the server thread is not waited for, since it can only run in a later tick and Bukkit cancels it when UltiTools disables.

Covered types and their refusal (every other request type keeps working until the connection closes):

  • command.execute, server.power: acknowledged first through the shared ledger (so a redelivery after a restart is acknowledged and never run), then command.result / server.power.result internal_error;
  • files.write, files.delete, properties.update, config.update, player.action, module.request: their result type with internal_error;
  • credential.rotate: its acknowledgement has no failure form, so the generic error internal_error with replyTo; the gateway sends the rotation again after the next welcome;
  • player.link.completed: an at-least-once event, not a request. It is left unanswered, so the gateway delivers it again after the next welcome (contract SEMANTICS 19) and the account is provisioned then; a failure receipt would end the delivery with the account not created.
The message of every refusal says the server is shutting down. No refusal changes server state or writes an operator file; the ledger records an acknowledged id as it does for every ack-first request.
Since:
6.3.1