c7487196ec
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c7487196ec |
Phase 8: 20+ new config keys + 14 admin commands (/playersync)
Config (JdbcConfig.java completely restructured into sections):
connection
host, port, use_ssl, user_name, password, db_name, table_prefix, Server_id
general
sync_world, sync_advancements, kick_when_already_online,
kick_message, kick_grace_period_ms, use_legacy_serialization,
item_placeholder_title_override, item_placeholder_description_override
save_triggers
auto_save_interval_minutes (0-1440, default 10)
save_on_dimension_change (default false)
save_on_death (default true)
save_on_respawn (default true)
sync_toggles
sync_inventory, sync_ender_chest, sync_xp, sync_effects,
sync_health_food, sync_curios, sync_accessories, sync_backpacks,
sync_cosmetic_armor, sync_refined_storage (all default true)
performance
heartbeat_interval_seconds (5-600, default 30)
peer_stale_threshold_seconds (10-3600, default 60)
join_poll_max_attempts (10-600, default 120)
join_poll_interval_ms (100-5000, default 500)
pool_stats_interval_minutes (0-1440, default 5)
hikari_pool_max_size (1-200, default 15)
hikari_leak_threshold_ms (2000-600000, default 25000)
safety
refuse_empty_inventory_write (default true) — enforced in writeSnapshotToDB
max_inventory_size_bytes (default 10 MB)
skip_saves_when_tps_below (0-20, default 0 = never)
observability
log_structured_json (future use)
log_rotation_size_mb (default 10)
log_rotation_max_files (default 5)
Wiring
- HeartbeatService reads heartbeat_interval_seconds at start.
- PoolStatsReporter reads pool_stats_interval_minutes (0 disables).
- doPlayerJoin poll uses join_poll_max_attempts + join_poll_interval_ms +
peer_stale_threshold_seconds.
- writeSnapshotToDB: refuse_empty guard + max_inventory_size_bytes guard
before core UPDATE. Both log via SyncLogger.dataLoss / .nbtAnomaly.
- Restore-side toggles: applyCuriosFromData, applyAccessoriesFromData,
applyCosmeticArmorFromData, doBackPackRestore, restoreRefinedStorageDisks
all short-circuit when their toggle is false.
Commands — new /playersync tree (perm level 2 required):
status — server id + heartbeat age + exec/Hikari stats + online
poolstats — log current stats immediately
flush [player] — force save all / one
info <player> — DB row metadata
dump <player> — dump full DB row to server log
resync <player> — clear synced tag + kick to force re-restore
wipe <player> confirm — DELETE all rows (DANGER, double-keyword required)
orphans — list stuck online=1 rows on dead peers
clearorphans [id] — clear orphans (global or by server_id)
peers — list peer servers with ALIVE/STALE/STOPPED tag
peerkill <id> — force-disable a zombie peer
cleanup — orphans + stale peers in one shot
reload — note about runtime reload scope
help — in-chat command reference
Every command logs to SyncLogger as ADMIN_<OP> for audit trail.
Infrastructure
- JDBCsetUp.executePreparedUpdateRet(String, Object...) returns rows-affected
for commands that need meaningful counts.
- VanillaSync.getExecutor() exposes the thread pool for read-only stats access
from admin commands (replaces reflection use in PoolStatsReporter eventually).
|
||
|
|
44178e020e |
Phase 7: server-perf hardening (hash-skip + batch + heartbeat tuning)
Based on a fresh audit against the Arcadia V2 modpack (444 mods, including
Curios + Accessories + SophisticatedBackpacks/Storage + RS2 + Cosmetic
Armor Reworked). Three perf wins + two opportunistic fixes.
Perf
- Heartbeat period 10s -> 30s. Paired with the 60s staleness threshold
this keeps failure-detection latency unchanged while cutting 3x the
server_info UPDATE traffic per server.
- Per-player hash-skip for unchanged snapshots (SaveToFile + staggered
auto-save). computeSnapshotHash() rolls over inventory/equipment/
enderchest/effects/xp/health/food/mod-data; when an auto-save produces
the same hash as the last successful write, the BG task returns early
and no UPDATE hits MySQL. Idle-server reduction is >95%. Logout /
shutdown / death never use the skip and refresh the hash on success
so post-logout rejoin doesn't wrongly skip.
- Batched backpack + SS saves. saveBackpackSnapshots / saveSSSnapshots
now build one transaction via executeBatchTransaction instead of
N sequential REPLACE INTO calls. A player with 3 backpacks + 2
shulkers drops from 5 network round-trips to 1 per logout save.
Per-entry fallback preserved on transaction failure.
- Periodic-save tick short-circuits when the player list is empty —
no main-thread hop, no log line, no DB heartbeat on empty servers.
Compat notes (no code change needed)
- CosmeticArmours (modid=cosmeticarmoursmod) items are worn in vanilla
armor slots (Helmet / Chestplate / Leggings / Boots inner classes) —
already captured by the core armor[] serialization. No handler needed.
- CosmeticWeapons uses the same pattern via main hand / offhand — also
already covered by core inventory serialization.
Cleanup
- removePlayerLock now also clears the hash cache so a player who
fully logged out doesn't leave a stale hash behind.
|
||
|
|
746cb56275 |
Phase 3: anti-loss infrastructure (shutdown hook + heartbeat + crash recovery)
Adds three utilities to harden PlayerSync against ungraceful server exits:
CrashRecovery.java
- installShutdownHook: registers a non-daemon JVM shutdown hook that calls
VanillaSync.emergencyFlushAll() synchronously when the process is killed
(SIGTERM, kill, OOM, host reboot). Covers the case where the normal
ServerStoppingEvent path never runs.
- clearOrphanedOnlineFlags: on startup, clears any online=1 player_data
rows pointing to this server_id (left by a previous crash). Reports the
count via SyncLogger so admins can see recovery activity.
- reportZombiePeers: logs peer server_ids whose heartbeat is missing or
stale (>60s), exposing the root of doPlayerJoin poll timeouts.
HeartbeatService.java
- Single-thread daemon scheduler pinging server_info.last_update every 10s.
- Lets peer servers distinguish live from dead via isPeerServerStale().
- Stopped explicitly in VanillaSync.onServerShutdown before pool close.
VanillaSync.emergencyFlushAll()
- Synchronous best-effort flush for every online player. No executor, no
locks — the server is dying, we just want data on disk. Writes player_data,
backpacks, SS, RS2 directly; logs SAVE/SKIPPED/FAILED per player via
SyncLogger so post-mortem analysis is possible.
PlayerSync.onServerStarting wires the four new calls after table init.
Fixes the production issue where players remained online=1 forever after
kill -9 and the 30s poll timeouts waiting for zombie server_ids.
|