diff --git a/docs/capacity-placeholders.md b/docs/capacity-placeholders.md index 035058ff..16da6dab 100644 --- a/docs/capacity-placeholders.md +++ b/docs/capacity-placeholders.md @@ -1,6 +1,6 @@ # Capacity Snapshot Feed -> ProxyFeatures Capacity 1.3.0 · versioned Redis latest-value feed for ServerFeatures placeholders +> ProxyFeatures Capacity 1.4.0 · versioned Redis latest-value feed for ServerFeatures placeholders ## Purpose diff --git a/docs/features/capacity.md b/docs/features/capacity.md index 4f7374f9..b3bf40d9 100644 --- a/docs/features/capacity.md +++ b/docs/features/capacity.md @@ -2,11 +2,12 @@ > Velocity · Feature ID `Capacity` · disabled by default · authoritative admission control -Capacity decides whether a proxy login or backend connection may start. It applies proxy, -gameplay, group, server, reserved-slot and server-state rules in memory and creates a short-lived -lease before Velocity starts the connection. +Capacity is the single authority for proxy login and backend admission. It combines numeric limits, +reserved slots, server states and Queue integration before Velocity starts a connection. -Paper's local player limit remains an emergency resource ceiling, not the normal network limit. +The feature is intentionally small operationally: administrators can inspect the current state, +change live slot limits, change per-server queueability, set temporary server states and reload the +YAML policy. Topology and safety tuning remain configuration-only. ## Storage and authority @@ -16,91 +17,126 @@ Capacity has no database support and creates no tables or views. Its only persis plugins/proxyfeatures/features/Capacity/config.yml ``` -The YAML file is always authoritative: +The YAML file is authoritative: -- startup loads configuration and manual states directly from `config.yml`; -- `/capacity config ...` writes the changed configuration back to `config.yml`; -- `/capacity state ...` writes or removes a manual state under `manual_states`; -- `/capacity reload` reloads all Capacity settings and manual states from `config.yml`; -- Redis snapshots remain observational and do not store or authorize Capacity state. +- startup loads the complete policy and manual states from `config.yml`; +- slot and queueability commands save their result to `config.yml` before applying it; +- state commands save manual state claims under `manual_states`; +- `/capacity reload` atomically validates and applies topology, timings, integrations, messaging and + manual states from YAML; +- an invalid reload is rejected and the last valid in-memory policy remains active; +- Redis snapshots are observational and never authorize admissions. -There is no SystemData connection, ORM context, SQL migration, revision history, rollback, audit, -runtime table, metric table, database view or database fallback. +## Configuration model -## Requirements +An admission can be constrained by all applicable scopes: -- one Velocity proxy process as the Capacity authority; -- Queue loaded after Capacity when Queue is enabled; -- Redis messaging only when backend Capacity placeholders are wanted. - -DataProvider and MySQL are not required by Capacity itself. +```text +proxy +AND gameplay +AND group +AND exact server +``` -## Configuration structure +A capacity of `0` disables that numeric scope. Reserved slots remain inside the absolute capacity; +they never increase it. Negative values and `reserved_slots` values greater than `capacity` are +invalid rather than silently corrected. -The generated config contains the complete policy: +Groups add one shared constraint over a set of servers. The group owns its membership: ```yaml -enabled: false - -global: - proxy: - capacity: 600 - reserved_slots: 10 - gameplay: - capacity: 500 +groups: + survival: + capacity: 250 reserved_slots: 10 + servers: + - survival + - survival1 + - survival2 +``` -leases: - ttl_millis: 15000 - login_ttl_millis: 20000 +There is deliberately no `group` property below an individual server. A server may occur in at most +one `groups..servers` list. Duplicate membership, duplicate identifiers after normalization, +blank members and malformed lists are rejected during startup and reload. -reconciliation: - interval_seconds: 5 +Exact-server policy is independent from group membership: -integrations: - maintenance_sync_interval_millis: 500 +```yaml +servers: + survival: + capacity: 100 + reserved_slots: 5 + counts_toward_gameplay: true + queueable: true + initial_state: OPEN + + queue: + capacity: 0 + reserved_slots: 0 + counts_toward_gameplay: false + queueable: false + initial_state: OPEN +``` -queue: - on_full: true +A server can be listed only in a group, only in `servers`, or in both. Group-only servers inherit the +unknown-server exact policy while still counting against the group. Server-only entries have no +group constraint. Configured servers remain visible in Capacity snapshots and `/capacity info` even +while they are not currently registered or online in Velocity. -failure: - fail_open_on_internal_error: false +Configuration values are type-strict. Booleans must be YAML booleans, capacities and durations must +be integer values, and identifiers cannot contain whitespace, control characters or `|`. -restart_return: - use_reserved_slots: true +Topology and non-operational behavior are edited in YAML and applied with `/capacity reload`: -unknown_servers: - counts_toward_gameplay: true - queueable: false - capacity: 0 - reserved_slots: 0 +- group creation, deletion and membership; +- `counts_toward_gameplay` and `initial_state`; +- unknown-server policy; +- lease and reconciliation timings; +- Maintenance and Restart integration behavior; +- fail-open behavior and Redis snapshot channel, publisher and interval settings. + +## Commands -groups: {} -servers: {} -manual_states: {} +Aliases: + +```text +/capacity +/cap ``` -Groups and servers are normal YAML maps. A command that adds, changes or deletes one rewrites those -maps in the same file. Default group/server maps are injected only when the complete section is -missing, so an intentionally deleted entry is not recreated on the next startup. +Inspection: -## Capacity scopes +```text +/capacity status +/capacity status group +/capacity info +``` -An admission can be constrained by: +`status` shows the proxy and gameplay scopes. `status group` shows a shared group constraint. +`info` shows the exact-server scope, its group scope when applicable, queueability, gameplay +participation, configured initial state, effective state and any manual state claim. + +Operational changes: ```text -proxy hard limit -AND gameplay-global limit -AND optional group limit -AND optional exact-server limit +/capacity slots proxy [reserved] +/capacity slots gameplay [reserved] +/capacity slots group [reserved] +/capacity slots server [reserved] +/capacity queueable +/capacity state [duration] [reason] +/capacity reload ``` -A capacity of `0` disables only that numeric scope. Server states are independent of numeric -capacity: a server with no configured numeric limit is still blocked when its effective state is -not `OPEN`. +Command arity is strict: trailing or incomplete arguments are rejected instead of being ignored. +When `reserved` is omitted from a slots command, the current value is retained and clamped to the new +capacity. Lowering a limit never kicks players; it blocks new positive-scope admissions until usage +returns below the limit. Raising a limit or reloading a more permissive policy wakes affected queues +immediately. -Connected players, pending leases and restart restoration reservations are counted together. -Reserved slots stay inside the absolute capacity; they never increase it. +The command interface cannot create groups, move servers between groups, change timing internals, +change failure policy or mutate unknown-server behavior. Edit `config.yml` and reload for those +changes. ## Server states @@ -114,28 +150,12 @@ The most restrictive owner wins. Owners include the configured initial state, th Maintenance and Restart. - `OPEN`: new connections are allowed when numeric capacity permits. -- `DRAINING`: new normal connections are blocked; players already on the server stay connected. -- `CLOSED`: new normal connections are blocked; players already on the server stay connected. -- `OFFLINE`: new normal connections are blocked; players already on the server stay connected. - -Capacity is an admission controller and deliberately does not move or disconnect existing players. -Maintenance and Restart retain their controlled evacuation/reconnect workflows. A state-bypass -permission can enter restrictive states; existing prepared non-bypass leases are revoked when a -state becomes restrictive. - -Manual state commands persist readable YAML such as: - -```yaml -manual_states: - survival: - state: DRAINING - expires_at: '2026-08-04T12:00:00Z' - reason: Deploying update - actor: AdminName -``` +- `DRAINING`: new normal connections are blocked; connected players remain. +- `CLOSED`: new normal connections are blocked; connected players remain. +- `OFFLINE`: new normal connections are blocked; connected players remain. -For a permanent state, `expires_at` is `permanent`. Expired entries are removed from the config when -Capacity loads them or when the expiry task runs. +Capacity is an admission controller. It does not move or disconnect existing players. Maintenance +and Restart retain ownership of their controlled evacuation and reconnect workflows. Examples: @@ -146,87 +166,30 @@ Examples: /capacity state survival clear ``` -Using `open` is equivalent to `clear`: it removes only the manual owner. Another owner or a -restrictive configured initial state may therefore keep the effective state restrictive. - -## Commands - -Aliases: - -```text -/capacity -/cap -``` - -Inspection: - -```text -/capacity status -/capacity status group -/capacity info -/capacity config show -``` - -`info` reports occupancy, pending leases, restoration reservations, limits, effective state, state -behavior and the current manual claim. - -Configuration: - -```text -/capacity config proxy [--confirm] -/capacity config gameplay [--confirm] -/capacity config group limit [--confirm] -/capacity config group add-server [--confirm] -/capacity config group remove-server [--confirm] -/capacity config group delete --confirm -/capacity config server limit [--confirm] -/capacity config server group [--confirm] -/capacity config server gameplay [--confirm] -/capacity config server queueable -/capacity config server initial-state --confirm -/capacity config server delete --confirm -/capacity config unknown limit [--confirm] -/capacity config unknown gameplay -/capacity config unknown queueable -/capacity config option