zzstat 0.5.0

A deterministic, hardcode-free stat calculation engine designed for MMORPGs
Documentation

zzstat - Hardcode-Free MMORPG Stat Engine

A highly optimized, completely data-driven, and deterministic stat calculation engine designed specifically for RPG and MMORPG servers.

zzstat provides everything you need to build complex game mechanics without writing a single line of hardcoded game logic. With zero I/O, no async primitives, and an incredibly fast O(1) copy-on-write fork() architecture, you can evaluate thousands of combat formulas and buffs concurrently with maximum performance.

🚀 Features at a Glance

  • JSON Data-Driven: Define your stat formulas, buffs, combat skills, and conditional abilities entirely in JSON. Let Game Designers balance the game without touching Rust code.
  • Dependency Graph Resolution: Automatically resolves complex dependency chains (ATK = STR * 2, DAMAGE = ATK - DEF) with topological sorting and cycle detection.
  • Resource Pools & DoT/HoT: Track stateful values like Current HP/MP, apply Damage over Time (Poison), and trigger custom Events (like DEATH).
  • Temporary Status Effects (Buffs & Debuffs): A StatusEffectManager that seamlessly applies temporary buffs using fork(), ensuring the character's base stats are never mutated. Supports stacking behaviors (Refresh, Accumulate, Independent).
  • Combat Engine (AST): A fully JSON-driven Combat Abstract Syntax Tree (AST). Calculate complex damages involving Chance nodes (Crit, Dodge, Block) deterministically.
  • AI-Ready Guidelines: Includes AI_PROMPT.md out-of-the-box so you can instantly generate perfectly valid JSON files using AI assistants like ChatGPT, Claude, or Copilot.
  • Modular & High Performance: Fully decoupled StatRegistry and StatResolver using rustc_hash::FxHashMap for blazingly fast O(1) operations.

🛠️ Core Modules

1. StatTemplate (Data-Driven Architecture)

Forget writing manual transforms in Rust. Load your entire class, monster, or item stat logic directly from a JSON string.

{
  "name": "Knight Class",
  "transforms": [
    {
      "stat": "DEF",
      "phase": "additive",
      "rule": "additive",
      "type": "Conditional",
      "condition": { "operator": "Equals", "key": "STANCE", "value": "DEFENSIVE" },
      "transform": { "type": "Additive", "value": 50.0 }
    }
  ]
}

2. ResourcePool (State Management)

RPG games need to track current states. ResourcePool handles Current HP/MP, bounds it to a StatResolver stat (like MAX_HP), processes ticks for DoTs, and emits events when thresholds are hit.

// Poison DoT (-20 HP/tick for 3 ticks)
hp_pool.add_effect(TimeEffect {
    name: "Poison".to_string(),
    amount_per_tick: -20.0,
    ticks_remaining: 3,
});

// Emits "DEATH" event if HP falls to 0
hp_pool.add_trigger(ThresholdTrigger {
    condition: TriggerCondition::Empty,
    event_name: "DEATH".to_string(),
});

3. StatusEffectManager (Temporary Buffs/Debuffs)

Adding a temporary buff shouldn't permanently alter the base stats. zzstat uses an O(1) copy-on-write fork() method to overlay stats dynamically.

let buff = StatusEffect {
    id: "WARCRY".to_string(),
    name: "Warcry".to_string(),
    bonuses: vec![Bonus::add_flat(atk_id, 50.0)],
    max_stacks: 1,
    stack_behavior: StackBehavior::Refresh,
};

// Add 50 ATK for 3 ticks
status_manager.add_status_effect(buff, Some(3), 1);

// Use the active resolver safely
let current_atk = status_manager.get_active_resolver(&base_resolver).resolve(&atk_id, &ctx);

4. CombatEngine (AST Formula Evaluation)

Evaluate complex attack commands defined entirely in JSON. The CombatEngine reads the AST and calculates the damage based on the Attacker and Defender's stats. Random chances (Crit/Dodge) rely on a closure, making tests 100% deterministic!

{
  "name": "Sword Slash",
  "expression": {
    "type": "Chance",
    "chance_expr": { "type": "Stat", "target": "defender", "stat": "DODGE" },
    "success_expr": { "type": "Constant", "value": 0.0 },
    "fail_expr": {
      "type": "Subtract",
      "left": { "type": "Stat", "target": "attacker", "stat": "ATK" },
      "right": { "type": "Stat", "target": "defender", "stat": "DEF" }
    }
  }
}

5. Graph Visualization (Mermaid.js)

Export your entire stat dependency tree, including all active bonuses and active transforms, into a graph TD Mermaid string natively. Perfect for generating live documentation for your game's mechanics!

let mermaid_code = resolver.export_mermaid();

Generates output that renders directly in GitHub, Notion, and Mermaid Live Editor:

graph TD
    WEAPON_MAX[("WEAPON_MAX")]
    WEAPON_MAX_src_b0>"ConstantSource(181.0)"]
    WEAPON_MAX_src_b0 -.-> WEAPON_MAX
    WEAPON_MIN[("WEAPON_MIN")]
    WEAPON_MIN_src_b0>"ConstantSource(159.0)"]
    WEAPON_MIN_src_b0 -.-> WEAPON_MIN
    MAIN_ATTACK[("MAIN_ATTACK")]
    MAIN_ATTACK_tr_b0[/"scale(LEVEL, 2.00)"/]
    MAIN_ATTACK_tr_b0 -.-> MAIN_ATTACK
    MAIN_ATTACK_tr_b1[/"scale(STR, 2.00)"/]
    MAIN_ATTACK_tr_b1 -.-> MAIN_ATTACK
    MONSTER_DEFENSE[("MONSTER_DEFENSE")]
    MONSTER_DEFENSE_src_b0>"ConstantSource(146.0)"]
    MONSTER_DEFENSE_src_b0 -.-> MONSTER_DEFENSE
    RAW_DAMAGE[("RAW_DAMAGE")]
    RAW_DAMAGE_tr_b0[/"scale(MAIN_ATTACK, 1.00)"/]
    RAW_DAMAGE_tr_b0 -.-> RAW_DAMAGE
    RAW_DAMAGE_tr_b1[/"scale(WEAPON_AVG, 2.00)"/]
    RAW_DAMAGE_tr_b1 -.-> RAW_DAMAGE
    BASE_DAMAGE[("BASE_DAMAGE")]
    BASE_DAMAGE_tr_b0[/"scale(RAW_DAMAGE, 1.00)"/]
    BASE_DAMAGE_tr_b0 -.-> BASE_DAMAGE
    BASE_DAMAGE_tr_b1[/"scale(MONSTER_DEFENSE, -1.00)"/]
    BASE_DAMAGE_tr_b1 -.-> BASE_DAMAGE
    LEVEL[("LEVEL")]
    LEVEL_src_b0>"ConstantSource(105.0)"]
    LEVEL_src_b0 -.-> LEVEL
    STR[("STR")]
    STR_src_b0>"ConstantSource(90.0)"]
    STR_src_b0 -.-> STR
    WEAPON_AVG[("WEAPON_AVG")]
    WEAPON_AVG_tr_b0[/"scale(WEAPON_MIN, 0.50)"/]
    WEAPON_AVG_tr_b0 -.-> WEAPON_AVG
    WEAPON_AVG_tr_b1[/"scale(WEAPON_MAX, 0.50)"/]
    WEAPON_AVG_tr_b1 -.-> WEAPON_AVG
    LEVEL ==> MAIN_ATTACK
    STR ==> MAIN_ATTACK
    MAIN_ATTACK ==> RAW_DAMAGE
    WEAPON_AVG ==> RAW_DAMAGE
    RAW_DAMAGE ==> BASE_DAMAGE
    MONSTER_DEFENSE ==> BASE_DAMAGE
    WEAPON_MIN ==> WEAPON_AVG
    WEAPON_MAX ==> WEAPON_AVG

⚡ Performance & Determinism

In MMORPG architecture, backend loops calculate stats for thousands of entities per second.

  1. Zero Allocations on Runtime Graph: Calculations trace predefined paths using robust caching (FxHashMap).
  2. Copy-on-Write Forks: Applying a buff creates an Arc overlay in nanoseconds.
  3. Fixed-Point Arithmetic (Optional): Ensure cross-platform floating-point precision issues never desync your lockstep gameplay.

📚 Examples

Explore the /examples directory for comprehensive guides on how to use zzstat:

  • 01_template_loader.rs
  • 02_resource_pool.rs
  • 03_status_effects.rs
  • 04_combat_engine.rs
  • 05_graph_export.rs
  • metin2/ (Full MMORPG Damage Simulator)

Run them with:

cargo run --example 01_template_loader
cargo run --example metin2

🤝 Using AI to Create Content

You can use ChatGPT, Claude, or any LLM to generate perfectly formatted stat and combat JSONs for zzstat. Simply copy the contents of AI_PROMPT.md and give it to your AI assistant.


📖 API Documentation

For comprehensive API documentation including all structs, methods, and types, you can generate and open the Rustdoc HTML directly on your machine:

cargo doc --open

Additionally, check out the docs/ folder in the repository for detailed guides and architectural walkthroughs in both English and Turkish.


License

This project is licensed under the MIT License.