crabsmadethis/d2r-horadric-tools
Diablo II Resurrected modding toolkit — character builder, mod build/deploy pipeline, save inspector, CASC reader, and MCP server for Claude/Codex/AI integration. Linux/Steam Deck.
README
d2r-tools
D2R modding toolkit for Linux and Steam Deck (Proton). YAML-driven character builder, data modding pipeline, and an MCP server that exposes the tools to agentic clients (Claude Code, Codex, Cursor, etc.).
Features
- YAML character builder - declare characters in YAML, build
.d2ssave files - Data mod pipeline - YAML overlays applied to game tables, built and deployed
- JSON string patches - rename or add item / UI strings via YAML, no string-table surgery
- CASC read/write - pure Python, no external tools needed
- Integrated validation - scanner catches structural problems before loading the game
- Reign of the Warlock expansion support (8 classes including Warlock)
Quickstart
git clone <repo-url>
cd d2r-tools
pip3 install -e .
d2r-mod extract # extracts + generates data from your D2R install
d2r-chargen build MyChar
(or pip install -e . if pip points to Python 3 on your system)
Three commands from clone to working. The extract step generates Python data modules from your D2R installation (required for legal reasons - no game data is distributed).
# If D2R is not auto-detected:
d2r-mod extract --game-dir /path/to/Diablo\ II\ Resurrected
# Or set the environment variable:
export D2R_GAME_DIR="/path/to/Diablo II Resurrected"
An example character YAML is included in chars/ExamplePaladin.yaml.
Create your own characters in the chars/ directory.
D2R install path is auto-detected on Linux/Steam Deck and Windows. Use --game-dir to override.
Character Definition (YAML)
Characters are defined in YAML files placed in a chars/ directory. Here's a complete example:
schema_version: 1
name: MyPaladin
class: paladin
level: 85
stats:
strength: 156
dexterity: 100
vitality: 250
energy: 15
skills:
Holy Shield: 20
Smite: 20
Fanaticism: 20
Charge: 1
Salvation: 1
Cleansing: 1
equipment:
# Unique items - resolved automatically from game data
- slot: helm
unique: Guillaume's Face
# Runeword items - specify runeword name and base item code
- slot: body
runeword: Fortitude
base: utp # Archon Plate
# Rare/crafted items with explicit properties
- slot: hands
rare: true
base: uvg # Vampirebone Gloves
ilvl: 90
properties:
fire_res: 30
cold_res: 30
light_res: 30
life: 20
- slot: weapon
unique: Grief
base: 7cr # Phase Blade
- slot: shield
unique: Herald of Zakarum
- slot: belt
unique: Verdungo's Hearty Cord
- slot: feet
unique: Gore Rider
- slot: neck
rare: true
ilvl: 90
properties:
class_skills: [2, paladin]
fcr: 10
fire_res: 30
light_res: 25
- slot: ring_right
unique: Bul Katho's Wedding Band
- slot: ring_left
unique: Ravenfrost
inventory:
charms:
# Unique charms
- unique: Annihilus
properties:
all_skills: 1
strength: 20
dexterity: 20
fire_res: 20
cold_res: 20
light_res: 20
poison_res: 20
add_exp: 10
# Repeatable magic charms
- magic_grand_charm:
count: 8
properties:
skill_tab: [1, 15] # Combat skills (paladin)
life: 40
- magic_small_charm:
count: 5
properties:
life: 20
light_res: 11
merc:
equipment:
- slot: weapon
unique: The Reaper's Toll
ethereal: true
Slot Names
helm, body, weapon, shield, hands, belt, feet, neck, ring_right, ring_left, weapon_switch, shield_switch
Item Types
| Field | Description |
|---|---|
unique: "Name" |
Unique item - stats auto-resolved from game data |
set_item: "Name" |
Set item |
runeword: "Name" |
Runeword - requires base code |
rare: true |
Rare item - requires base and properties |
magic_grand_charm |
Magic grand charm with count and properties |
magic_small_charm |
Magic small charm with count and properties |
Classes
amazon, sorceress, necromancer, paladin, barbarian, druid, assassin, warlock
CLI Reference
d2r-chargen
d2r-chargen build <name> [--phase N] [--force] # Build character (phases 1-4)
d2r-chargen validate <name> [--yaml-only] # Validate YAML definition
d2r-chargen list # List defined characters
d2r-chargen scan <name> # Run scanner diagnostics
d2r-chargen import <name> [--force] # Import .d2s -> YAML
d2r-chargen diff <file1> <file2> # Compare .d2s files
d2r-mod
d2r-mod extract [--game-dir PATH] # Extract game data + generate Python modules
d2r-mod build [--no-regen] # Build mod from vanilla + overlays
d2r-mod deploy [--force] [--no-casc] # Deploy mod to game directory
d2r-mod undeploy # Remove mod from game
d2r-mod diff [--summary] # Compare vanilla vs modded tables
d2r-mod inject [--from-dir PATH] # Inject files into CASC archive
d2r-mod audit [--skills] [--items] # Audit game data
d2r-mod clean # Remove build/ and reset data
d2r-mod update # Re-extract after game update
Data Mod Overlays
Overlays modify D2R data tables declaratively using YAML. Place overlay files in an overlays/ directory and run d2r-mod build.
Create an overlays/ directory in your project root to add custom overlays.
If no overlays directory exists, d2r-mod build will proceed with vanilla data only.
target: data/global/excel/UniqueItems.txt
changes:
- row: {index: "The Gnasher"}
set:
prop4: "dmg%"
min4: "50"
max4: "50"
comment: "Buff The Gnasher with +50% Enhanced Damage"
Each overlay targets a specific game table (TSV file) and specifies row matches and column changes. See examples/sample_overlay.yaml for a complete example.
JSON String Patches
D2R reads item names, mercenary names, and most UI strings from JSON files in data/local/lng/strings/ (not from the older .tbl files). To add a new string or override a vanilla one, drop a YAML spec into patches/json_strings/ and rebuild.
# patches/json_strings/my_renames.yaml
description: "Rename a few potions"
target: item-names.json
entries:
- key: "vps" # vanilla string key (super healing potion)
value: "Wild Rice Cake" # what D2R should display instead
- key: "MyCustomItem" # new key — auto-allocated string ID
value: "Heart of the Mountain"
If the key already exists in the target JSON, its enUS value is overridden. If it doesn't, a new entry is appended with the next free string ID (read from data/local/lng/next_string_id.txt). Targets currently used: item-names.json, mercenaries.json, ui.json.
After d2r-mod build, the patched JSONs land in build/data/local/lng/strings/; d2r-mod deploy injects them into CASC. D2R caches strings at startup, so fully exit and relaunch to see changes.
If no patches/json_strings/ directory exists, the build skips this step.
Architecture
d2r_chargen/ YAML character builder
build_lib.py Binary item encoder (Huffman, stat encoding, checksums)
character.py Build orchestrator (YAML -> items -> .d2s)
resolve.py Name-to-ID resolution (uniques, runewords, stats, skills)
save.py Save file operations (stats, skills, waypoints, items)
scanner.py Diagnostic validator
data/ Game data (generated via extract, not distributed)
d2r_mod/ Data modding pipeline
casc.py Pure Python CASC reader (TBL, TXT, JSON)
casc_write.py CASC archive builder + ekey-hijack injection
overlay.py YAML overlay loader and applier
build.py Build orchestrator (vanilla + overlays -> build/)
build_steps/ Specialized build steps:
register_custom_uniques.py Auto-register custom unique names in TBL
build_string_registry.py Diff built TBLs vs vanilla into a registry
patch_json_strings.py Apply patches/json_strings/*.yaml to JSON
deploy.py Deploy/undeploy mod files
regen.py Generate chargen data from extracted tables
tools/ Standalone diagnostics
audit_string_registry.py Categorize string_registry entries vs JSON
Platform Support
| Platform | Status |
|---|---|
| Linux / Steam Deck (Proton) | Supported, tested |
| Windows | Untested — path detection included, contributions welcome |
| macOS | Unsupported |
Requirements
- Python 3.10+
- PyYAML
- Diablo II: Resurrected (for data extraction)
mcp(optional, only for the MCP server —pip install -e ".[mcp]")
License
MIT
MCP Server (Claude Code, Codex, Cursor, …)
d2r-tools ships an MCP server that exposes
game-data lookups, save inspection, the chargen pipeline, and the mod pipeline
as typed tools to any MCP-compatible agent. See d2r_mcp/README.md for the
full tool catalog (23 tools across lookup / save / chargen / mod).
Install
pip install -e ".[mcp]" # installs the `mcp` SDK alongside d2r-tools
Launch
python3 -m d2r_mcp # stdio transport
Client configuration
Claude Code (user-scoped):
claude mcp add d2r-tools --transport stdio --scope user -- python3 -m d2r_mcp
Codex / Cursor / any other MCP client — add to the client's MCP config:
{
"mcpServers": {
"d2r-tools": {
"command": "python3",
"args": ["-m", "d2r_mcp"],
"env": {}
}
}
}
Mutation tools (d2r_chargen_build, d2r_mod_deploy) enforce the project's
safety rules structurally: they back up the live save, build to a staging
file, run the scanner on the staging, and only promote to the live save if
the scanner passes.
Claude Code Plugin (optional)
The plugin/ directory is a Claude Code plugin that adds slash commands and
skills on top of the MCP server. Point Claude Code at the repo or symlink
plugin/ into your Claude Code plugins directory.
Slash commands
| Command | Description |
|---|---|
/d2r-build <name> |
Safe build cycle (backup → build → scan → verify) |
/d2r-validate <name> |
Validate character YAML without building |
/d2r-scan <name> |
Run diagnostic scanner |
/d2r-lookup <query> |
Look up items, stats, skills from game data |
Skills
- d2r-safe-edit — enforces backup→edit→scan→verify on every save file change
- d2r-parallel-agents — guides parallel-agent dispatch for multi-character work
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.