Class CloudFeatures
Two lifetimes. The objects a module or a redelivery depends on live as long as the plugin is enabled: the
CloudPlayerLinks API and the PlayerLinkChannel behind it (so a handler a module set at startup
survives every reconnect), and the one SessionDedupeLedger every handler shares (so the ledger monitor is
the same across connections). Each upgraded connection gets its own AgentProtocol; CloudSession
calls register(AgentMessageRouter, CloudChannel, AgentProtocol, CredentialRotationHandler.CredentialPersister, ScheduledExecutorService)
once for it, which registers the request handlers on that connection's router and points the lifetime objects at
that connection. A credential.rotate is persisted through the session that owns the connection, never
through the store directly, because that session is the one owner of the current credential.
Threads. The player-link handler of a module runs on one daemon thread, UltiTools-CloudLinks; the
create and handler deadlines run on a second, UltiTools-CloudLinks-Timer. Both stop in shutdown().
The file and server.properties handlers do blocking disk I/O, so each connection gets one
SerialRequestWorker (UltiTools-CloudFiles, daemon, bounded queue, overflow answered
rate_limited) that runs them in arrival order; heartbeats, acks and every other request never wait behind a
slow listing. The worker closes with its connection (on close, on the next connection, and in shutdown()).
The router dispatches every other request on the agent thread, which is never the server thread; the handlers that
call the Bukkit API hand that work to the server thread through CloudFeatures.Bindings.mainThread.
Registered request types (each only when its port is bound; CloudFeatures.Bindings.forPlugin(com.ultikits.ultitools.UltiTools) binds every one):
command.execute (22-09), server.power (22-10), player.action (22-11), files.* and
properties.* (22-14), config.* (22-15), module.request (22-16), and
player.link.created/completed/expired and credential.rotate (22-17).
Capabilities. AgentCapabilities decides the hello list and the router's per-request switch check
(S-65, S-66). A module.request is also filtered by the module capabilities the gateway accepted: a request
for a module whose modules:<id>@<X.Y.Z> name was not accepted is answered capability_not_accepted
(contract SEMANTICS 8) and never reaches the module.
Outbound (plan 22-22). One AgentOutboundFeeds lives as long as the plugin is enabled. On every
established connection it gets the live channel; the connection's agent scheduler then runs its once-a-second send
(log lines) until the close, and a main-thread task every 20 ticks, started at the first connection, samples TPS,
CPU and the server state and advances the telemetry cadence. snapshot.request is registered on every
connection. A /ul reload that changes an operator setting sends settings.changed once
(onReload()).
Shutdown (22-23 shutdown-window round). Every connection's router and file worker share one
ShutdownGate built from shutdown(): from the first step of UltiTools#onDisable every
state-changing request is answered with its shutting-down failure and runs nothing, and a change already running
holds the gate's permit until it is answered, so onDisable closes the action log only after it.
- Since:
- 6.3.1
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static final classThe ports the inbound handlers are built from. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final CloudFeatures.Bindingsprivate final AgentCapabilitiesprivate SerialRequestWorkerThe file-request worker of the current connection, ornull.(package private) static final StringThe name of the thread the module's player-link handler runs on.private final ExecutorServiceprivate static CloudFeaturesprivate final SessionDedupeLedgerprivate RunnableCancels the main-thread sampling task, ornullbefore the first connection.private static final Stringprivate static final Stringprivate final AgentOutboundFeedsprivate static final Stringprivate final PlayerLinkChannelprivate final CloudPlayerLinksprivate AgentProtocolThe protocol of the connection the lifetime objects currently use, ornull.(package private) static final longHow often the agent thread sends the buffered log lines (contract: every second, or when a batch is full).private final AgentSettingsprivate static final Stringprivate static final Stringprivate final ScheduledExecutorService -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate static booleanprivate boolean(package private) CloudFeatures.Bindingsbindings()(package private) AgentCapabilitiesprivate static ThreadFactoryprivate long(package private) com.google.gson.JsonObjectThe hello's operator settings (powerActionsEnabled), remembered as reported so a later reload reports only a change (settings.changed, plan 22-22).(package private) static CloudFeatures(package private) DedupeLedgerledger()private CloudChannelprivate static StringmoduleOf(com.google.gson.JsonObject payload) voidonReload()Reports a changed operator setting on the open connection (plan 22-22, S-53): called by/ul reloadafter the framework configuration was read again.private voidThe power handler's pre-shutdown hook: the outbox and the ids reach the disk before the server stops.private static voidreconnectForNewCapabilities(AgentProtocol connection) private voidOne action-log entry; a failing log never reaches the handler, so the request still gets its reply.private voidrecordSwitchRefusal(CloudRequest request, String capability, String message) Records a request the capability gate refused as exactly one DENIED entry (FEATURES.mdultitools.remote.file-refusal-audit, as 6.3.0 did): the switch that decides the capability, the request type as action, itscommandorpathas target when it carries one (the type otherwise), the panel as actor and the refusal sentence naming the configuration key as reason.private static StringrefusalTarget(CloudRequest request) (package private) voidregister(AgentMessageRouter router, CloudChannel channel, AgentProtocol protocol, CredentialRotationHandler.CredentialPersister rotation, ScheduledExecutorService agent) Registers the inbound handlers on one connection's router and points the lifetime objects at that connection.private static voidregisterAll(AgentMessageRouter router, List<String> types, Supplier<CloudRequestHandler> factory, SerialRequestWorker worker) private voidregisterCommands(AgentMessageRouter router) private voidregisterConfig(AgentMessageRouter router) private voidregisterModules(AgentMessageRouter router, AgentProtocol connection) private voidprivate voidregisterPower(AgentMessageRouter router) private voidregisterSnapshots(AgentMessageRouter router) Registerssnapshot.requeston every connection; the telemetry behind it is looked up per request.private voidreportSettingsIfChanged(CloudChannel channel) The comparison and the mark happen under one monitor, with the hello's own mark, so a reload racing a hello can neither repeat a value nor lose one.(package private) voidsendPlayerCommand(String commandLine) Sends oneplayer.commandcarrying only the command's normalised name (D-20), while a connection is established and theevents:commandsswitch is on; never buffered (D-14).(package private) AgentSettingssettings()voidshutdown()Stops the player-link threads and answers every pending create as not connected.static CloudFeaturesCreates the features of the enabling plugin and makes them the ones every new connection registers.(package private) static CloudFeaturesstart(CloudFeatures.Bindings bindings) private voidStarts the main-thread sampling task once; it runs untilshutdown(), so TPS history has no gaps.
-
Field Details
-
HANDLER_THREAD_NAME
The name of the thread the module's player-link handler runs on.- See Also:
-
PANEL_ACTOR
- See Also:
-
MODULE_REQUEST
- See Also:
-
MODULE_PREFIX
- See Also:
-
SEND_PERIOD_MS
static final long SEND_PERIOD_MSHow often the agent thread sends the buffered log lines (contract: every second, or when a batch is full).- See Also:
-
SETTINGS_CHANGED
- See Also:
-
TELEMETRY
- See Also:
-
installed
-
bindings
-
capabilities
-
settings
-
handlerThread
-
timers
-
ledger
-
playerLinkChannel
-
playerLinks
-
protocol
The protocol of the connection the lifetime objects currently use, ornull. -
fileWorker
The file-request worker of the current connection, ornull. -
outbound
-
mainTimerCancel
Cancels the main-thread sampling task, ornullbefore the first connection. Guarded bythis.
-
-
Constructor Details
-
CloudFeatures
-
-
Method Details
-
start
Creates the features of the enabling plugin and makes them the ones every new connection registers. Called once per enable, before any module loads, so a module can set its player-link handler while it enables.- Parameters:
plugin- the enabling framework plugin- Returns:
- the features, until
shutdown()
-
start
- Parameters:
bindings- the ports the handlers are built from- Returns:
- new features, installed for every later connection
-
installed
- Returns:
- the features new connections register, or
nullbefore the plugin enabled or after it stopped
-
playerLinks
- Returns:
- the player-link API; the same object for the whole enabled lifetime, never
null
-
shutdown
public void shutdown()Stops the player-link threads and answers every pending create as not connected. New connections no longer register these features. Never waits for a thread. -
register
void register(AgentMessageRouter router, CloudChannel channel, AgentProtocol protocol, CredentialRotationHandler.CredentialPersister rotation, ScheduledExecutorService agent) Registers the inbound handlers on one connection's router and points the lifetime objects at that connection. Called byCloudSessiononce for each upgraded socket, before itshello.- Parameters:
router- the connection's routerchannel- the connection's live channel (open between welcome and close)protocol- the connection's protocol layerrotation- persists acredential.rotatethrough the session that owns this connectionagent- the connection's agent scheduler: runs the once-a-second send while it is established
-
registerSnapshots
Registerssnapshot.requeston every connection; the telemetry behind it is looked up per request. While it is unavailable the hello does not declaretelemetryand the router refuses the request before it gets here (available(String)); the warning says why, once per connection. -
available
- Returns:
- whether the feature behind a contract capability can run now (only telemetry can be missing)
-
startMainTimer
private void startMainTimer()Starts the main-thread sampling task once; it runs untilshutdown(), so TPS history has no gaps. -
sendPlayerCommand
Sends oneplayer.commandcarrying only the command's normalised name (D-20), while a connection is established and theevents:commandsswitch is on; never buffered (D-14).- Parameters:
commandLine- the command line as the player typed it
-
capabilities
AgentCapabilities capabilities()- Returns:
- the hello list and the per-request switch check of every connection
-
helloSettings
com.google.gson.JsonObject helloSettings()The hello's operator settings (powerActionsEnabled), remembered as reported so a later reload reports only a change (settings.changed, plan 22-22).- Returns:
- the settings the next hello carries
-
onReload
public void onReload()Reports a changed operator setting on the open connection (plan 22-22, S-53): called by/ul reloadafter the framework configuration was read again. Exactly onesettings.changed { powerActionsEnabled }is sent when the value differs from the last one the panel was told (by a hello or an earliersettings.changed); nothing when it is unchanged or no connection is established, in which case the next hello carries the value.A reload that adds or removes a declared capability (an operator switch) closes the connection normally (1000) instead, and the connection reconnects after its normal short backoff: capabilities are negotiated only at the handshake (backend decision k), so the new hello declares the new set and carries the settings; no
settings.changedis sent then. A change made while the hello waits for its welcome is caught when the welcome arrives. Reads the configuration only; never writes an operator file (D-31). -
reconnectForNewCapabilities
-
reportSettingsIfChanged
The comparison and the mark happen under one monitor, with the hello's own mark, so a reload racing a hello can neither repeat a value nor lose one. Also run when a connection is established, which reports a change made between its hello and its welcome. -
settings
AgentSettings settings()- Returns:
- the operator settings reporter shared by every connection
-
registerCommands
-
registerPower
-
registerPlayerActions
-
registerAll
private static void registerAll(AgentMessageRouter router, List<String> types, Supplier<CloudRequestHandler> factory, SerialRequestWorker worker) -
registerConfig
-
registerModules
-
moduleOf
-
accepted
-
moduleNames
-
persistBeforeShutdown
private void persistBeforeShutdown()The power handler's pre-shutdown hook: the outbox and the ids reach the disk before the server stops. -
recordSwitchRefusal
Records a request the capability gate refused as exactly one DENIED entry (FEATURES.mdultitools.remote.file-refusal-audit, as 6.3.0 did): the switch that decides the capability, the request type as action, itscommandorpathas target when it carries one (the type otherwise), the panel as actor and the refusal sentence naming the configuration key as reason. A capability noCapabilityswitch decides records nothing. Writes only the action log, never an operator file. -
refusalTarget
-
recordAction
One action-log entry; a failing log never reaches the handler, so the request still gets its reply. -
ledger
DedupeLedger ledger()- Returns:
- the ledger every handler of every connection shares
-
bindings
CloudFeatures.Bindings bindings()- Returns:
- the bindings these features were built from
-
liveChannel
-
gatewayClockOffsetMillis
private long gatewayClockOffsetMillis() -
daemon
-