Core Concepts
Previous: Quick Start · Next: Integration Overview
This page covers the architecture — for those who want to "understand it before configuring." After reading, you'll grasp: what exactly happens inside QS for a single keypress, why the state machine exists, why a skill has "two sets of fields," and what QS does and does not do.
🎯 The runtime pipeline: 7 stages of a single keypress
The core of QS is one fixed runtime pipeline. Whether the trigger comes from an item, a command, the API, or a passive event, it walks this same path:
① Input Player keypress / command / API / passive event
│ Normalized into a standard trigger signal
▼
② State machine What state is it in now? Idle? Combo window? Locked?
│ (State) Decides whether this input gets in, and which path it takes
▼
③ Graph resolution In the current state, which node does this key hit?
│ (Graph) + Combo Is it a link in some combo sequence?
▼
④ Execution plan Assembles "which MM skill to cast, who the target is, which parameters to carry"
│ (Plan)
▼
⑤ Gate Checks one by one: unlock? cooldown? charges? GCD? resource? blood sacrifice? conflict? conditions?
│ Any failure → blocked with a prompt, the pipeline stops here
▼
⑥ Execution (Exec) The MythicExecutor hands the plan to MythicMobs to perform
│
▼
⑦ Post-processing Start cooldown / change state / advance the combo / refresh the actionbar
│ (Post)💡 Why "one pipeline for all triggers"? This way, rules like cooldown, charges, combos, and targets are written only once and apply equally to "cast via item," "cast via command," and "cast via passive." You'll never hit the two-sets-of-logic conflict where "the command can cast but the item can't."
Remember the fixed order of this pipeline — the gate comes before execution. So "the MM skill is clearly written but won't cast" almost always gets stuck at step ⑤ the gate (not unlocked / on cooldown / not enough resources), not an MM problem.
🔄 The state machine: 6 states
QS maintains a skill state machine for each player. The point of its existence: to make time-sequenced things like "combos," "follow-ups," and "being silenced" decidable.
| State | Meaning |
|---|---|
IDLE | Default state; can normally open with a skill |
CASTING | A skill is being cast |
COMBO_WINDOW | After a successful opener, a brief window opens; input within it counts as a "follow-up" |
RECOVERY | Reserved state (recovery / stagger; no concrete logic enabled yet) |
LOCKED | Silenced; no skill can be cast |
INTERRUPTED | The channel / cast was interrupted |
When states transition
IDLE ──first keypress, opener succeeds──► CASTING
CASTING ──succeeds and the skill has a combo──► COMBO_WINDOW
COMBO_WINDOW ──combo_window_ms (default 800) elapses without a follow-up──► IDLE (combo breaks)
Any state ──/qs silence N──► LOCKED ──after N seconds──► IDLE
Channel/cast interrupted by movement or damage ──► INTERRUPTED ──► IDLEKey points:
- Opener vs. follow-up is distinguished by state. The same right-click takes the opener node in
IDLEand the follow-up node inCOMBO_WINDOW(graph nodes userequire_stateto mark which is which). This is the underlying mechanism that makes combos possible. - The combo window times out. If you don't press the next link within
combo_window_ms(default 800ms), the state returns toIDLEand the combo breaks. - Silenced =
LOCKED./qs silence 5puts the player intoLOCKEDfor 5 seconds, during which no skill can be cast;/qs silence 0clears it. This is the implementation entry point for skill-lock / silence debuffs.
See Graphs and Combos and Cast Modes and Channeling.
📑 A skill's "two sets of fields": ecosystem validation vs. runtime reading
Open any skill yml and you'll see the fields split into two halves. This is a deliberate design in QS, and understanding it saves you from big pitfalls.
| This set of fields | Purpose | Who reads it |
|---|---|---|
meta / trigger / state / graph / execution | Ecosystem info — categorization, consistency validation on reload, combo orchestration reference | schema validator |
type / cooldown / resource / cast_mode / levels / variables … | Actually read at runtime — truly decides behavior | runtime loader |
A side-by-side example (from fire_wave):
meta:
type: active # Ecosystem tag: for categorization
trigger:
primary: RIGHT_CLICK # Ecosystem: declares the primary trigger key (compared against the graph during validation)
state:
required: IDLE # Ecosystem: declares the required state (compared against the entry node during validation)
execution:
mythic_skill: fire_wave # Ecosystem: declares which MM skill to execute
type: active # ← What truly decides active/passive at runtime
mythic_skill: fire_wave # ← What's actually used at runtime (execution.mythic_skill takes priority)⚠️ The two sets must stay consistent. For example,
state.required: IDLEmust match the graph entry node'srequire_state: IDLE;execution.mythic_skillmust match the entry node'smythic_skill./qs reloadruns a consistency check and emits a schema warning if they don't match.
Why split into two sets? The upper set lets the ecosystem (the validator, combo orchestration, the future editor GUI) understand "what this skill looks like, which category it belongs to, whether it combos" without running the skill; the lower set is the config the engine actually consumes at runtime. One is for "understanding," the other for "executing."
🚪 The gate: ordered checkpoints before allowing a cast
The gate is step ⑤ of the pipeline and one of QS's most core values. It's a chain of checks executed in a fixed order; any failure blocks the skill and prompts the player:
| Checkpoint | What it checks |
|---|---|
| Unlock | Has this player unlocked this skill? |
| Cooldown / cooldown group | Is it on CD? Do skills in the same cooldown group share a CD? |
| Charges | Are there charge layers left (charge skills use layers instead of a binary cooldown)? |
| Global cooldown (GCD) | Did you just cast another skill and you're still in the global cooldown? |
| Resource | Is there enough mana (temporary placeholder, moving to QC later)? |
| Health / hunger (blood sacrifice) | Is there enough health/hunger to spend? Too low to cast? |
| Conflict group | Did you recently cast a skill in the same conflict group (gate.conflict_window_ms default 1000ms)? |
| Declarative conditions | Are all the conditions in conditions: satisfied? |
💡 The gate is the "decision layer," not the "presentation layer." It only answers whether a cast can happen, never draws anything. So when a skill won't cast, think gate first (which checkpoint failed) — don't dive straight into MM to debug. For detailed checkpoint config see Cooldown, Charges, GCD, and Conflicts and Costs, Conditions, and Variables.
🌉 The bridge: QS → MythicMobs
After the gate passes, QS must hand the execution plan to MM. This handoff seam is called the bridge.
Bridge mode (config.yml's mythic.bridge_mode) | Behavior |
|---|---|
AUTO (recommended / default) | Prefer registering the skill via the MM API → validate → on failure, fall back to writing YAML and loadSkills() |
API_MODE | API only, no fallback (for development / testing) |
YAML_STUB | Only write to the MM skills folder + go through the load lifecycle |
About the bridge, remember three things:
- It runs even without MM. The bridge enters placeholder mode, and triggering a skill →
[QinhSkills] <skill name>in chat. Seeing it proves the QS side is fully working. - Never overrides your MM skill of the same name. The placeholder only exists when "you haven't written a skill of the same name"; once you write a real skill, the bridge uses yours.
/qs bridgeshows the bridge status, diagnosing whether MM is present and whether registration succeeded.
🧭 Responsibility boundaries: what QS does / doesn't do
Finally, nail down the boundary — this is key to understanding the entire Qinhuai ecosystem.
QS does: cast logic / trigger normalization / target acquisition / gating (unlock · cooldown · charges · cooldown group · GCD · conflict · resource · blood sacrifice · conditions · cast_mode · channel cast bar and interruption) / passing variable parameters through to MM / JS script hooks (pre_js / post_js).
QS does not do:
| What it doesn't do | Whose job it is |
|---|---|
| Particles / movement / presentation / the mechanic itself | MythicMobs |
| Damage value settlement | Attribute plugins like AttributePlus (on the MM side) |
| Player class / player level / mana·stamina resource pools / cooldown reduction (CDR) / player attributes | QinhClass (QC) |
🔑 About mana: the
resource.manain a skill yml and theresources.default_mana/max_manainconfig.ymlare temporary placeholders before QC takes over. Resource spending goes through QS's existing player-profile seam (PlayerSkillProfile), not a separate pool; when QC takes over resources in the future, only this one spot changes — the skill yml doesn't need to.
This boundary follows directly from QS's first design philosophy: QS only manages logic — not presentation, not values, not player progression. Everyone does their own job.
Keep reading
- Wire skills to items / commands / the API → Integration Overview
- Why insert a QS layer between items and MM → Integrating MythicMobs
- Hands-on writing skill YAML (all fields) → Skill File Structure · Full Skill Definition Fields
- Server-owner operations → Server Owner Guide