Server & storage
General Settings
The main configuration file is located at /plugins/SimpleClaimSystem/config.yml. Reload after editing with /scs reload.
# Logger verbosity
logger: NORMAL # NORMAL | DEBUG
# Language file (from /langs)
lang: "en_US.yml"
# Update checks
update:
check: true # poll for new versions on startup
notifications: true # notify in-game on join (requires scs.update.notifications)
# Command aliases — add extra command names that invoke the same handler
command-aliases:
claim: []
parea: []
unclaim: []
claims: []
scs: []
cc: []
requnclaim: []
viewrequnclaim: []
# Base commands to unregister entirely (see the key reference below)
disabled-commands: []
Key reference
| Key | Description |
|---|---|
logger | NORMAL = standard info/warn/error. DEBUG = verbose (cache hits, Redis SETEX, DAO inserts). Only turn on when troubleshooting; produces a lot of console noise. |
lang | Filename of the language file inside /langs. Shipped with en_US.yml and fr_FR.yml. Drop your own file in the folder to add a translation. |
update.check | On startup, the plugin contacts the release feed to detect newer versions. |
update.notifications | When a newer version is detected, notify players who have scs.update.notifications on join. |
command-aliases.* | Extra command names mapped to each built-in command. Example: claim: [c, protect] makes /c and /protect behave like /claim. Since 2.7.3 an alias enforces the same permission as its base command, and the three extra commands (cc, requnclaim, viewrequnclaim) can be aliased too. |
disabled-commands | Base commands to remove from the server entirely (claim, claims, unclaim, parea, scs, cc, requnclaim, viewrequnclaim). Frees the name for another plugin. If the disabled command has command-aliases entries, the first alias becomes its replacement command — e.g. disable claims and set claims: [aclaims] to turn /claims into /aclaims while your own plugin takes over /claims. Restart required. (2.7.3) Changes apply on /scs reload when the server lets plugins unregister commands at runtime; otherwise the command stays hidden by the plugin and a restart applies the change fully. (2.8) |
Database
SCS2 uses SQLite by default but supports MySQL / MariaDB for larger servers and multi-server setups:
database:
enabled: false # false = SQLite (local), true = use the MySQL block below
hostname: localhost
port: 3306
name: database_name # schema (must already exist on the server)
username: root
password: pass
ssl-mode: PREFERRED # MySQL TLS: DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY
hikari:
# Connection-leak threshold (ms). Hikari logs a stack trace identifying the borrower
# if a connection is held longer than this without being returned. Useful to surface
# accidental synchronous DB calls on hot paths. 0 disables the check.
leak-detection-ms: 30000
Key reference
| Key | Description |
|---|---|
enabled | false = SQLite at /plugins/SimpleClaimSystem/storage.db. true = connect to MySQL using the fields below. Switch is safe to flip — use the transfer commands to migrate data. |
hostname / port | MySQL host/port. Defaults to localhost:3306. |
name | Database (schema) name. Must already exist and the user below must have DDL rights on it (the plugin creates its own tables). |
username / password | Credentials for the account the plugin connects with. Change the default root/pass before deploying. |
ssl-mode | MySQL TLS mode: DISABLED, PREFERRED (default), REQUIRED, VERIFY_CA or VERIFY_IDENTITY. Ignored for SQLite. The connection URL is also hardened (allowLoadLocalInfile=false, utf8mb4). (2.7.3) |
hikari.leak-detection-ms | If a borrowed connection isn't returned within this many milliseconds, Hikari logs a stack trace pointing at the borrower. Catch synchronous DB calls on hot paths quickly. Default 30000; set 0 to disable. The pool is auto-sized to the engine (MySQL 20 / SQLite 4) with a 10s connection timeout. (2.7.3) |
Database transfer
You can transfer data between local (SQLite) and distant (MySQL) databases at any time:
/scs transferLocalToDistant— Copy all SQLite data to MySQL/scs transferDistantToLocal— Copy all MySQL data to SQLite
The connection pool is managed by HikariCP internally. For large servers MySQL is strongly recommended over SQLite — concurrent writes are faster.
Redis (Optional Cache Layer)
Redis is an optional third caching layer between the in-memory cache (Caffeine) and the SQL database. It does not replace MySQL, but since 2.8 it also doubles as a real-time cross-server sync bus (see Multi-server setup below): several servers sharing one Redis + one MySQL now see each other's claim changes instantly.
How the cache stack works
Every claim / player read follows this order:
- Caffeine (in-memory, per-server) — hit: returned instantly.
- Redis (if enabled) — miss in Caffeine ⇒ check Redis. Hit: the value is deserialised and cached in Caffeine. Miss: fall through.
- MySQL / SQLite — miss everywhere ⇒ query the database. The result is written to Redis (if enabled) and Caffeine for subsequent reads.
Writes go to the database first, then to Redis, then to Caffeine, and will invalidate both caches if any step fails to avoid stale reads.
redis:
enabled: false
hostname: localhost
port: 6379
username: "" # Redis 6+ ACL user; empty = default user
password: "" # empty = no auth; set on any internet-facing Redis
ssl: false # enable TLS to the Redis server
cross-server-sync: true # publish cache invalidations so other servers see changes live
database: 0 # Redis logical db (0-15 by default)
command-timeout-seconds: 30 # bump on slow/remote Redis; 0 = no client-side timeout
Key reference
| Key | Description |
|---|---|
enabled | When true, the plugin opens a Redis connection at startup and treats it as cache layer 2. Leaving this off is fine — Caffeine alone handles most workloads. |
hostname / port | Redis host/port. Defaults are the vanilla Redis install. |
username | Redis 6+ ACL username. Leave empty to authenticate as the default user (password-only). (2.7.3) |
password | Optional AUTH password. Leave empty for unauthenticated Redis (only safe on localhost). |
ssl | When true, connect to Redis over TLS. Keep Redis on a private network regardless. (2.7.3) |
cross-server-sync | When true (default), each server publishes a cache invalidation to the others on every claim mutation, so a change on one server appears on the rest in real time. Set false for a single server that wants zero pub/sub overhead. (2.8) |
database | Logical database number. Use distinct numbers if you're sharing one Redis between several plugins. |
command-timeout-seconds | Per-command timeout for Lettuce. Bump if you see "Command timed out" on joins with many visible claimed chunks, or on a slow/remote Redis. Set to 0 to disable the client-side timeout entirely. |
Storage schema
Since v2.2.4 the plugin stores claims under a two-key layout: one scs:claim:<id> key holds the JSON for the entire claim (chunks, members, bans, perms, flags), and every chunk gets a lightweight scs:chunk:<worldUuid>;<x>;<z> pointer containing just the claim id. This makes a radius claim O(chunks) on the wire instead of O(chunks²) and avoids the SETEX timeouts that earlier layouts could trigger.
When should I enable it?
Redis is worth enabling when:
- Caffeine evicts frequently (very large claim counts, a lot of Caffeine misses). Redis keeps deserialised claims close to the server.
- You want claim reloads after a
/scs reloadto be fast (Redis survives the restart; Caffeine does not).
For small/medium servers, Caffeine alone is enough. Redis adds operational overhead (another service to run) without meaningful gains.
Use /scs clearRedis to flush the plugin's Redis keys (e.g., after a manual MySQL edit). It does not touch other plugins' keys in the same logical database.
Multi-server setup (2.8)
Point several servers at the same MySQL database and the same Redis, with redis.enabled: true and redis.cross-server-sync: true (the default). From then on the servers stay coherent in real time:
- Every claim mutation (create, unclaim, add/remove chunk, rename, description, spawn, icon, members, roles, bans, permissions, flags, sale, ownership transfer) writes to MySQL + Redis, then publishes an invalidation on the
scs:invalidateRedis channel. - Each other server is subscribed to that channel and, on receipt, drops the affected chunks from its local Caffeine cache, reads the fresh claim back from Redis and applies it: holograms, map markers, bossbars, the public-warp and spawn indexes and the per-player claim lists are all refreshed on the spot, so the change shows up within milliseconds, without a reload or a reconnect.
- Per-player data travels on the same channel: every write to a player's data (limits, settings, purge grace, tax meter, favourites) publishes an invalidation, and a player's data is always re-read at login, so a change made on one server is visible on the others right away — including for a player currently connected elsewhere.
- Unclaim requests, template names and claim-name lists are kept in step the same way, and a bulk rewrite (config migration, custom flag backfill, admin reset) sends a single flush that makes every server reload its cached claims.
- The recurring jobs that must run once per network (tax collector, auto-purge, temporary-member sweep, unclaim-request sweep) take a short Redis lock before each run, so only one server executes them per interval and nobody is billed or purged twice.
- A server ignores its own messages (each instance has a random id), so there is no echo loop, and cache warm-up loads (login,
/scs reload) do not broadcast, so the channel stays quiet.
# Same config on every server:
database:
enabled: true # one shared MySQL
# ...same host/name/credentials everywhere...
redis:
enabled: true # one shared Redis
cross-server-sync: true
# ...same host everywhere...
What syncs instantly: everything about claims (the map, protections, members, permissions, flags, sales, ownership, holograms, public warps) and everything about players (limits, settings, purge grace, tax meter, favourites), plus unclaim requests and claim templates. What stays local by design: pending invitations, claim chat and the anti-abuse timers of the /claim auto-* commands, which only make sense on the server the player is connected to.
Cross-server sync needs Redis. Without it (redis.enabled: false), each server only has its local Caffeine cache and will not see another server's claim changes until the cache expires — so a multi-server deployment should always run a shared Redis.
Cache Thread Pool
Both the claim cache and the player cache share a tunable executor. It controls how many concurrent DB/Redis lookups the cache layer can fire when something misses Caffeine. Defaults are usually fine — only change this if you see "executor saturated" warnings or if your server is particularly large.
cache:
thread-pool:
# automatic = suitable for the machine (max(2, CPU cores / 2)).
# manual = use core-size below.
mode: automatic
core-size: 4
queue-size: 5000
Key reference
| Key | Description |
|---|---|
mode | automatic scales the pool against the host's CPU count. manual uses the configured core-size verbatim. |
core-size | Always-alive threads per cache (claim + player). Only read in manual mode. Range 2–8 is typical. |
queue-size | How many pending lookups the executor buffers before overflow. Overflow runs on the caller thread — visible as a brief stall rather than a lost task. |
The claim cache and the player cache use independent executors but read the same config, so both stay sized consistently. Change it once, both are affected on next reload.