Server Admin

Scuffed Combat Stats

Requirements, the /scs operator commands, the SQLite fight database and per-raid JSON, config keys and guided experiment runs.

Last updated

Install, command, configuration and data reference for server operators. Scuffed Combat Stats is an operator-only balancing tool: it records every Endless Leveling damage-meter fight into a local SQLite database, writes a detailed JSON file for each raid, and includes a guided experiment mode for measuring one variable at a time. It adds nothing that normal players see or use, so there is no gameplay guide for it.

  • Plugin version: 0.8.2-pre.1 (plugin name scuffed-combat-stats)

Requirements

PluginVersionType
EndlessLevelingCore (Airijko:EndlessLevelingCore)≥ 10.6.0Required

The manifest declares ServerVersion >=0.7.0-pre.0 <0.8.0. If Endless Leveling is not present at startup, the plugin logs a warning and does nothing.

Capture depth depends on the Endless Leveling build you run. Each level is detected at startup and switches on automatically; an older Endless Leveling simply skips the levels it does not support.

LevelNeeds from Endless LevelingWhat you get
Base10.6.0 or newerOne database row per player per fight: totals, class, race, level, augment loadout, stat line, damage-source split, held weapons
Combat telemetryA build that carries the combat telemetry API (first shipped in 11.10.1)Per-augment, per-class-ability, per-weapon and per-victim damage tables, plus the ability and swing damage totals
Damage breakdownA build that carries the per-hit damage breakdown APIStage-by-stage damage decomposition in the per-raid JSON

The startup log states which levels are active ("combat telemetry detected" and "damage-breakdown API detected" lines).

The guided experiment mode (/scs collectdata) additionally needs the preset-build and augment-slot methods in Endless Leveling's API. When they are missing, the command refuses to start and names what it could not find.


Install notes

  • Ship the jar whose name ends in -all (for example ScuffedCombatStats-0.8.2-pre.1-all.jar). It is the only build that bundles the SQLite driver. The plain jar contains no org.sqlite classes and fails with a NoClassDefFoundError on the first database write.
  • Keep only one Scuffed Combat Stats jar in mods/. Two jars with the same plugin name are refused at boot.
  • The -all jar is trimmed to the Windows and Linux SQLite native libraries. The macOS, FreeBSD, Android and Musl natives are excluded.
  • Capture is on by default. A fresh install starts recording the first fight that Endless Leveling freezes.

Admin & Staff Commands

All commands live under /scs. They are operator-only: the plugin registers no permission nodes and never opens its commands to non-operators, so Hytale's default operator check is the only gate.

Every subcommand must be run by a player in game. They cannot be run from the server console.

CommandDescription
/scs statusShows capture ON or OFF, number of stored rows, time since the last fight and how many players it held, the current balance tag, whether an experiment run is active, and the database path
/scs onResume capture. Takes effect immediately and persists across restarts
/scs offStop all capture immediately. Damage listeners, the hotbar sampler, raid windows and fight writes all become no-ops, including for the fight in progress. Persists across restarts
/scs tag <name>Set the balance tag stamped on future fights (see below). While an experiment run is active, this retargets the tag the server returns to when the run ends instead
/scs summaryPer-primary-class aggregate over all stored fights of at least 1 second: sample count, average damage share, damage per minute and mitigation percent. Shows up to 12 classes, highest average damage share first
/scs best [--min <n>]Ranks (class, race, weapon) combinations by median damage per hit. --min is the minimum number of rows per combination (default 3, minimum 1). Shows the top 15 and also prints median damage per second at freeze and average crit rate
/scs raid statusShows whether raid capture is enabled, whether the damage-breakdown API is present, and every open raid window with its player count, hits, credited damage, stage-detail state and age. Also shows the last file written
/scs raid lastPath and headline numbers of the most recent per-raid JSON written since the last restart
/scs collectdataStart a guided experiment run, or report on the one already going (see below)
/scs collectdata nextFinish the current step and set up the next one, or finish the run after the last step
/scs collectdata stopAbandon the run and restore the previous balance tag

/scs summary and /scs best run on the database thread, not the world thread, so they do not stall a tick. When there is nothing to show, /scs summary says no fights are captured yet and /scs best suggests lowering --min.


Permissions

None. The plugin defines no permission nodes. Access follows Hytale's default rule for registered commands: operators only.


Configuration

Settings live in mods/ScuffedCombatStats/config.json, created with defaults on first start.

KeyDefaultEffect
enabledtrueMaster capture switch. Changed with /scs on and /scs off
balanceTag"baseline"Label stamped on every captured fight. Changed with /scs tag
minFightTopDamage1.0A fight whose top player dealt less than this damage is dropped as noise. 0 keeps everything
raidCaptureEnabledtrueOpen a capture window when an Endless Leveling raid starts and write a per-raid JSON when it ends. Independent of enabled, but a raid window only opens while enabled is also true
raidSampleHitsPerPlayer200Fully expanded per-hit breakdowns kept per player in the raid JSON, sampled evenly across the whole raid. 0 keeps only the sums
raidJsonDir"raids"Folder under mods/ScuffedCombatStats/ that raid JSON files are written to
raidIncludeTestRaidstrueAlso capture raids that Endless Leveling marks as test raids. They are written with "testRaid": true
raidSessionMaxMs7200000Safety limit for one raid window (2 hours). A window open longer than this is force-closed and written with endedBy set to timeout. The check runs when another raid window opens. Builds after 0.8.2-pre.1 also run it on the next hit recorded for the stale window. 0 disables the limit

The file is read once at startup. Edit it while the server is stopped; there is no reload command. enabled and balanceTag are rewritten by their commands.

Balance tags

The balance tag is a free-text label copied onto every row of a fight. Change it with /scs tag patch-1.2 whenever you push a balance pass, then compare WHERE balance_tag = '...' groups in the database to see the before and after.


Data & storage

Everything the plugin owns sits in mods/ScuffedCombatStats/.

PathContents
config.jsonSettings above
scuffedcombatstats.dbSQLite database of captured fights (WAL mode, so -wal and -shm files appear beside it while the server runs)
raids/<eventId>.jsonOne file per captured raid (folder name set by raidJsonDir)
experiment-protocol.jsonEditable experiment steps, written the first time a run starts
experiment-run.jsonState of the active experiment run. Removed when the run ends

There is no automatic pruning. The database and the raid folder grow until you delete rows or files yourself.

What a fight row records

When Endless Leveling freezes a damage-meter leaderboard, the plugin writes one row per player into the fights table. Totals, kills, healing, class, race and level are copied from Endless Leveling's own leaderboard entry. Nothing is re-derived except damage share, crit rate and mitigation percent.

Column groupContents
Fightcaptured_at, balance_tag, event_id, world_id, world_name, content_type (RAID, DUNGEON or OTHER), content_name, started_at, stopped_at, duration_ms, party_size, ally_classes (the other party members' primary classes)
Playeruuid, name, primary_class, secondary_class, race, level, prestige, temp_profile, profile_index, augments_json (loadout at freeze time)
Damagetotal_damage, dps_at_freeze, damage_share, hit_count, crit_count, crit_rate, kill_count, best_kill_streak
Defence and supporttotal_taken, total_mitigated, mitigation_pct, total_healing, total_shielding, total_buff
Damage by sourcesrc_direct, src_bonus, src_dot, src_true, src_summon, plus unattributed, src_overflow and sources_balanced
Ability and swingability_damage_total, swing_damage_total (needs combat telemetry)
Weaponweapon_item_id and weapon_source
Timingfirst_damage_ms, last_damage_ms
Provenanceaddon_version, el_version, server_mod_fingerprint

Notes on specific values:

  • dps_at_freeze is Endless Leveling's rolling 5-second damage rate at the moment the fight froze, not a fight average. A fight that ended in a lull reads near zero. Divide total_damage by duration_ms for an average.
  • The five src_* columns are the plugin's own observation of the damage stream. Whatever is missing against Endless Leveling's total goes in unattributed, and any excess goes in src_overflow. Nothing is scaled to fit. sources_balanced is 1 when the gap is within 2 percent of the fight total.
  • weapon_source says how the weapon was chosen: PHASE2_TOP (largest damage in the per-weapon table), HELD_TOP (item held longest) or HELD_AT_FREEZE (item in hand when the fight froze).
  • server_mod_fingerprint is a short hash of the sorted jar file names in mods/. Equal values mean the same mod set. Rows written in the first moments after startup may carry empty provenance values.
  • Leaderboards with no entries, or where the top player's damage is below minFightTopDamage, are not stored.
TableOne row perColumns
augment_damagefight row and augmentfight_id, augment, damage
ability_damagefight row and class abilityfight_id, ability, damage
weapon_damagefight row and weaponfight_id, weapon, damage
victim_damagefight row and victim typefight_id, victim, damage, max_health, overkill
fight_attributesfight row and attributefight_id, attribute, race_base, skill_bonus, external_bonus, total
fight_itemsfight row and held itemfight_id, item_id, swings, held_ms

fight_id joins to fights.id. Augment and ability damage are kept in separate tables because the two id spaces overlap. The first four tables fill only when combat telemetry is available. fight_attributes covers Sorcery, Precision, Ferocity, Strength, Haste, Defense, Life Force and Flow. Augment, ability and victim tables fold ids past 256 per bucket into an (overflow) row so totals stay complete.

The schema upgrades itself on startup. Older databases keep all their rows and gain NULL in any new columns.

Reading the data

The database is a normal SQLite file. Open it with DB Browser for SQLite or DBeaver, or export to CSV. The in-game /scs status output prints its absolute path. To copy it while the server runs, copy the -wal and -shm files together with the main file, or stop the server first.

Per-raid JSON

When an Endless Leveling raid starts, the plugin opens a capture window keyed by the raid's shared world name. When Endless Leveling freezes the raid's leaderboard (completion, admin stop, world removal or shutdown), the plugin writes raids/<eventId>.json. A window that never gets a leaderboard is closed by the raidSessionMaxMs limit (see the config table for when that check runs), and any window still open at shutdown is flushed to disk. Files are written to a temporary file and moved into place, so a reader never sees a half-written file. Windows with no recorded damage write nothing.

The file contains:

  • raid: event id and name, import id, world names and ids, shard count, total waves, start, stop and duration, testRaid, party size, and endedBy (snapshot, timeout or shutdown). Anything other than snapshot can mean partial totals.
  • provenance: add-on version, Endless Leveling version, mod fingerprint, balance tag, whether the breakdown API was available, and the per-player sample size.
  • totals: hits, credited and computed damage, weapon base damage, true damage, rounding loss and overall amplification.
  • players: for each player, identity, leaderboard figures, capture counters, an amplification block (how the raw weapon roll became credited damage, with per-stage factors), stageTotals, stageAverages, damage by source, weapon, ability and augment, and sampleHits (up to raidSampleHitsPerPlayer fully expanded hits).

Without the damage-breakdown API in Endless Leveling, the stage figures are absent (breakdownAvailable is false) and the file carries credited totals and the per-source, weapon, ability and augment splits only. Players who left before the raid ended keep the identity captured on their first hit. For sharded raids spanning several worlds, damage is attributed to the matching window; when two or more raids run at once and a hit arrives from an unseen world, the hit is dropped rather than booked to the wrong raid.


Guided experiment runs

Passive capture cannot answer a class or weapon question when every player differs in gear, augments and skill. An experiment run fixes this by putting one operator into the same preset build at the start of every step so that steps differ in exactly one thing.

Starting a run

  1. Put at least one preset build in Endless Leveling's builds folder. The first build id Endless Leveling lists is the run's primary build (*), the second is the comparison build (*alt).
  2. Move to the admin temp profile with /lvl temp grant. A run rebuilds the operator's character on every step, so it is only allowed on the throwaway admin sandbox profile, never on a real character.
  3. Run /scs collectdata. It starts the run, applies the first step and prints the instruction, the target, and the tag.

The server has one run at a time. Typing /scs collectdata again reports the status: run id, protocol name, step number, operator, step time, rows recorded on this step against its target, and the instruction. The exception is a step that is not set up (see Tags below): for the operator who started the run, the bare command retries the setup instead.

During a run

  • Do what the step says, then run /scs collectdata next. Each next re-applies the step's build and augments and takes over the balance tag.
  • The duration and fight targets in a step are guidance shown to the operator. Nothing advances automatically.
  • Only the operator who started the run can advance or stop it.
  • /scs off pauses a run without cancelling it. The run keeps its step and tag, and records nothing until /scs on. The status output warns when a run is active with capture off.
  • After the last step, next ends the run. stop abandons it at any time. Both restore the balance tag to what it was before the run and delete experiment-run.json.

Tags

Each step tags its fights run:<runId>/step:<n>/<tagSuffix>, where <runId> is the start time as yyyyMMdd-HHmmss. If Endless Leveling refuses the build or an augment, the step is marked not set up and its tag gets /SETUPFAIL appended, so analysis can skip rows that did not get the build they asked for. Run /scs collectdata again to retry the setup.

A run survives a restart. On the next boot it comes back on the same step but is held as not set up, because the admin temp profile does not survive a restart. The operator runs /scs collectdata to re-apply the step. Rows captured before that are tagged SETUPFAIL.

Protocol file

The steps are data, in mods/ScuffedCombatStats/experiment-protocol.json. The built-in default is written there the first time a run starts. The file is read when a run starts and the resolved steps are copied into the run, so editing it mid-run does not change a run in progress.

KeyMeaning
name, notesProtocol label and free text
steps[].enabledfalse skips the step (default true)
steps[].buildIdPreset build id, * for the primary build, *alt for the second build, or - to leave the character as the previous step left it
steps[].augmentsMap of augment slot key (for example ELITE#1) to augment id, applied after the build. Use none to empty a slot
steps[].tagSuffixDistinguishing part of the tag. Lowercased; anything other than letters, digits, -, _ and . becomes -. Defaults to step<n>
steps[].durationSecondsTime target shown to the operator, 0 for none
steps[].targetFightsFight-count target shown to the operator, 0 for none
steps[].instructionText shown to the operator

A step that asks for *alt is dropped when only one preset build exists. If the file is missing, unreadable or has no enabled steps, the built-in protocol is used.

The shipped protocol is weapon-isolation-v1. Four enabled steps, each 300 seconds or 3 fights, hold the same build and change only the weapon: weapon-a, weapon-b, weapon-c, then weapon-a-repeat as a drift control. Two further steps ship disabled as worked examples: one ELITE augment (weapon-a-elite-augment) and the second preset build (alt-build-weapon-a).


Integrations

PluginTypeUse
EndlessLevelingCoreRequiredDamage events, leaderboard snapshots, attribute and augment lookups, raid start and end hooks, preset builds for experiment runs

The plugin only reads from Endless Leveling and from the player's hotbar. It does not change gameplay, damage numbers or player data, apart from the experiment run rebuilding the operator's admin temp profile. It has no documented API for other mods.


Performance

Capture runs on the damage path, so it is built to stay cheap. Fight inserts and every admin query run on a dedicated database thread, raid and config files are written off-thread, and the held-weapon sampler touches the clock only when a player swaps items. /scs off is the quickest way to rule the plugin out when troubleshooting a lag report.


Troubleshooting

SymptomLikely cause
NoClassDefFoundError for org.sqlite on the first writeThe plain jar was deployed. Use the -all jar
Plugin logs "Endless Leveling not detected - plugin will do nothing"EndlessLevelingCore is not installed or not loaded
/scs status shows capture OFFSomeone ran /scs off. The setting persists across restarts, so run /scs on
fights stored stays 0Capture is off, no leaderboard has frozen yet, or every fight stayed under minFightTopDamage
Augment, ability, weapon or victim tables are emptyThe running Endless Leveling build has no combat telemetry API. Check the startup log
Raid JSON has no stage detailThe running Endless Leveling build has no damage-breakdown API. breakdownAvailable is false in the file
No raid file after a raidraidCaptureEnabled is false, capture was off, or the raid recorded no damage. /scs raid status shows open windows and the last file
/scs collectdata says you are not on the admin temp profileRun /lvl temp grant, then try again
/scs collectdata says no preset builds are loadedAdd a build JSON to Endless Leveling's builds folder and reload
A step shows REFUSED and SETUPFAILEndless Leveling rejected the build or an augment the class cannot take. Fix the protocol, then rerun /scs collectdata