d2rAI-toolkit
Health Uyari
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 7 GitHub stars
Code Gecti
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Build and port Diablo II: Resurrected mods with AI agents: engine-level plugins, data patches, game-copy testing, releases. Made by a clanker.
made by a clanker
Change how Diablo II: Resurrected works, not just its tables.
Describe the system you want. An AI agent splits it into data and engine work, builds it from D2R's own files and
engine, tests it in real game copies, and helps you ship it.
Quick start · First prompt · Skills · Knowledge · AI workflow · All docs · FAQ
What is this?
d2rAI-toolkit lets you change Diablo II: Resurrected at the engine level by talking to an AI coding agent
such as Claude Code. New game systems, new skill functions, raised
hard limits and new UI panels: the agent plans the work, writes the data and the engine plugin, finds the game code
with Ghidra, and tests the result in copies of the game. A new skill or a bigger stash works the same way, just
smaller.
It gives the agent everything it needs to work safely on D2R:
- an engine plugin template for D2RLoader, an address registry for D2R build
93847, and Ghidra scripts to find what isn't in it yet; - a Python toolkit (
modkit) that turns small JSON patches into a complete mod folder, built fresh from the game's
own files every time and validated; - a knowledge base of what the engine hard-codes, how every table and HD file works, how to plan a new game
system, and how to debug crashes; - skills: step-by-step guides the agent follows for each kind of job;
- the tools around a real project: testing in copies of the game, releases, distribution (your own host, GitHub or
Nexus Mods) and a player downloader.
Everything here was learned the hard way, by porting a large classic overhaul mod to D2R with AI agents. The toolkit
works only with D2R's own files and engine. Blizzard's files are read from your install and never stored in the
repo.
What you can do
⚙️ Engine-level changesChange what the game does. New systems (a mapping system, a new crafting bench), new skill functions, |
🗡️ Data mods from prompts"A frost Whirlwind for the Barbarian." "A 12 × 12 stash." "A new unique bow that drops from Andariel." The agent |
📜 Port a classic modWith its creators' permission, bring a 1.10 / 1.13c-era mod to D2R: take stock, convert each table, keep the |
🧪 Test in real game copiesGame slots are frozen copies of your install, queued with locks. The agent controls them without your mouse or |
📦 Ship releases, anywherePackage a 7z plus a manifest of content-addressed files, upload only what changed, and create release pages from |
🤖 Run many agents in parallel, safelyA coordinator plus short "lanes": one job each, with its own branch, worktree and PR. The coordinator reviews and |
🧹 Keep your repo cleanA pre-commit scan for e-mails, IPs, user paths, keys, PNG metadata and your own private-words list. The same |
🩹 Triage crashes from playersTurn a player's crash report into a D2R function and a fix: RVA from RIP, stack return addresses, Ghidra, and which |
Quick start (5 minutes)
You need Windows, an installed D2R 3.3, Python 3.12+, git, and Visual Studio with the C++ workload
(full list). CMake must be on PATH. Visual Studio's bundled CMake works: add<Visual Studio>\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin to PATH. Commands use py -3, becausepython on Windows may be the Microsoft Store stub. If D2R is not in the default folder
(C:\Program Files (x86)\Diablo II Resurrected), set MODKIT_GAME to its folder first:$env:MODKIT_GAME = "D:\Games\Diablo II Resurrected".
# 1. Get the toolkit (with its CascLib and PluginSDK submodules)
git clone --recursive https://github.com/Nugwa628/d2rAI-toolkit.git
cd d2rAI-toolkit
# 2. Build CascLib, which reads the game's storage (needs cmake on PATH)
powershell -ExecutionPolicy Bypass -File tools\build_casclib.ps1
# 3. Copy the tables, strings and layouts out of YOUR install (into d2r-data/, which is git-ignored)
py -3 -m modkit fetch
# 4. Make a mod project next to the toolkit, build it, install it (with the game closed)
py -3 -m modkit new ..\mymod --name MyMod
py -3 -m modkit build ..\mymod
py -3 -m modkit install ..\mymod
# 5. Turn on the privacy scan for your commits
py -3 tools\install_hooks.py
Then start the game with D2RLoader.exe and pick MyMod, or run D2R.exe -mod MyMod -txt. Want a bigger
example? Build examples/showcase with py -3 -m modkit build examples\showcase. It builds
with 0 errors. The warnings it shows are in D2R's own data (vanilla formulas and seqnum values), plus one rename note.
Now open the folder in your agent (claude in a terminal) and start asking.
Your first prompt
Start big. This is the kind of change the toolkit is for: a new game system that D2R's tables alone can't express.
Add a Path of Exile-style mapping system to
..\mymod. Maps drop in Hell with a tier from 1 to 10 and rolled
modifiers (monsters deal extra fire damage, more monster life, extra packs). Transmuting a map in the cube in the
Rogue Encampment opens a portal to a map area. The area level comes from the tier, the modifiers apply to every
monster there, and the map is used up. Better tiers give better drops.
This is a multi-lane project, not one prompt and done. A good agent says so, and plans it like this.
1. Reads the rules and the limits. CLAUDE.md says data before code. The request matches thed2r-engine skill and new-game-systems.md.
engine-limits.md shapes the design before a line is written:
| Limit | What it means for maps |
|---|---|
| The cube's "portal" output opens only the Cow Level | The portal needs a plugin hook on that output |
| Portals link levels within one act only | Map areas are Act 1 levels; the device is the cube in the Rogue Encampment |
| Portal links store the level as a byte in many places | Map areas need level ids of 255 or below: a pool of a few areas, reused, not one per map |
levels.txt Layer below 100; clear Position on new maze levels |
Set both on every cloned level, or entering it crashes |
| Levels come from levels.txt, loaded at startup | A map area is a pre-made level from the pool. Its layout is generated once per game, so a reused area keeps its layout until the next game |
2. Splits the work into lanes (AI workflow):
| Lane | Work | Tools |
|---|---|---|
| RE spike | Confirm each hook: the cube's portal output (and the checks it makes for the Cow Level), the portal maker, the monster spawn path the level populator uses, room to level. Record the signatures and entry bytes | tools/ghidra_query.ps1 with Decompile.java, Disasm.java, Refs.java; engine-re.md; the 93847 address registry |
| Data | Item type map, 10 map bases, map affixes and the stats they roll, a pool of map levels cloned from a vanilla cave (with their lvlmaze rows), area levels per tier, treasure classes, the cube recipe, strings, stand-in HD art |
modkit patches (modkit-ops.md), skills d2r-items, d2r-monsters, d2r-cube |
| Plugin | Three parts, each with its own off-switch word. mapportal: the cube output opens a portal to a free pool level and remembers the map's tier and modifiers. mapmods: monsters spawned in that level get the modifiers. maplevel: picks the area level for the tier and frees the level when the run ends |
plugin-template |
| Tests | Named checks in a game slot: a map drops, the recipe consumes it, the portal leads to a map level, the area level matches the tier, a monster has the modifier's stats, the portal closes | testing |
| Release | Version bump, player changelog, publish; a --nexus archive if it goes on Nexus Mods |
release, distribution |
3. Writes the data in ..\mymod\patches\, for example 040-maps.json (shortened):
{"ops": [
{"op": "add_row", "table": "itemtypes",
"values": {"ItemType": "Map", "Code": "map", "Equiv1": "misc", "Magic": 1, "Rare": 1, "StorePage": "misc"}},
{"op": "clone_row", "table": "misc", "from": "Jewel", "key": "Map Tier 1",
"set": {"code": "mp1", "namestr": "MapTier1", "type": "map", "level": 68}},
{"op": "clone_row", "table": "levels", "from": "Act 1 - Cave 5", "key": "Act 1 - Map Area 1",
"set": {"LevelName": "MapArea1", "MonLvl(H)": 70, "MonLvlEx(H)": 70, "Waypoint": 255, "Position": 0}},
{"op": "clone_row", "table": "lvlmaze", "from": "Act 1 - Cave 5", "key": "Act 1 - Map Area 1"},
{"op": "add_row", "table": "cubemain",
"values": {"description": "map -> map portal", "enabled": 1, "version": 100, "numinputs": 1,
"input 1": "map", "output": "Cow Portal"}},
{"op": "string", "key": "MapTier1", "text": "Map (Tier 1)", "file": "item-names.json"}
]}
The real patch also sets Layer and the monster lists, the lvlmaze row's Level id, the other tiers and the
affixes. py -3 -m modkit build ..\mymod must end with 0 errors. The validator catches a maze level without a lvlmaze
row, broken references and bad ids.
4. Writes the plugin parts. Example: mapmods, a hook on the spawn-monster function from the registry. It is a
sketch in the template's style. The RE spike confirms the signature and the entry bytes first, as hook_example.cpp
shows.
// map_mods.cpp: monsters spawned in a map area get that map's modifiers.
// 0x492cf0 (game, room, x, y, monster, mode, search, flags) -> unit: spawn monster (d2r-93847-addresses.md)
using SpawnFn = d2r::Unit* (*)(d2r::Game*, void* room, int32_t x, int32_t y, int32_t monster, int32_t mode,
int32_t search, int32_t flags) noexcept;
SpawnFn g_spawn = nullptr; // trampoline, set by InstallInlineHook
std::atomic<uint32_t> g_count{0};
auto Spawn(d2r::Game* game, void* room, int32_t x, int32_t y, int32_t monster, int32_t mode, int32_t search,
int32_t flags) noexcept -> d2r::Unit* {
d2r::Unit* unit = g_spawn(game, room, x, y, monster, mode, search, flags);
if (!unit || !room) return unit;
const maps::Run* run = maps::RunFor(game, d2r::api.roomLevel(room)); // 0x2efc10 room -> level; mapportal set the run
if (!run) return unit; // not a map area: unchanged
for (const maps::Mod& mod : run->mods) // read from the map item's stats
maps::Apply(game, unit, mod); // stats and states on the monster
if (plog::Sample(g_count)) plog::Info("mapmods: %d mods on monster %d", run->count, monster);
return unit;
}
5. Tests and ships. Each lane runs its named checks in a game slot and opens a PR. The coordinator merges, runs
the full suite, and releases. Expect several rounds: the first map run always finds something.
A simple data change
Smaller jobs follow the same rules in one step:
Add a frost version of Whirlwind for the Barbarian in
..\mymod. It should replace Double Throw, do cold damage
and have its own icon.
The agent uses the d2r-skill skill. A class has exactly 30 skills, so it
replaces Double Throw (same row id, tree slot and save byte), clones Whirlwind and writes one op:
{"ops": [{"op": "skill", "name": "Frost Cyclone", "replace": "Double Throw", "clone": "Whirlwind",
"skills": {"EType": "cold", "EMin": 4, "EMax": 8, "ELen": 75},
"text": {"name": "Frost Cyclone"}, "icon": "assets/frost_cyclone.png",
"hdSequenceLike": "Whirlwind"}]}
That one op also creates the tooltip strings, the HD and classic icons, and the HD animation entry. Then it builds
with 0 errors, installs with the game closed, and adds a row to the project's TESTING.md.
Repo map
| Path | What's inside |
|---|---|
modkit/ |
The Python toolkit (py -3 -m modkit -h): CASC reading, tables, strings, UI layouts, sprites/PNG/DC6, HD maps, patch ops, the skill op, validator, installer, save editor |
knowledge/ |
How D2R works for modders: limits, new game systems, tables, HD art, layouts, strings, saves, reverse engineering, addresses, debugging, porting |
.claude/skills/ |
Step-by-step guides the agent follows (list below) |
CLAUDE.md |
The agent's standing instructions and rules |
docs/ |
Guides for running a project: AI workflow, testing, releases, distribution (own host, GitHub, Nexus Mods), downloader |
templates/ |
Fill-in files: coordinator prompt, lane rules, lane prompt, bug list, plan, changelog, owner update |
plugin-template/ |
D2RLoader plugin skeleton (C++): guards, off-switch, status command, a new skill function, an inline hook |
testing/ |
Game slots, game control without real input, a checks framework, release-like installs |
release/ |
Packaging (7z + manifest, --nexus archive), delta publish over SFTP, server ingest, CDN mirror, release pages, release.sh |
downloader/ |
The player-facing Win32 installer and updater, configured by one header file |
tools/ |
CascLib build, game image dump, Ghidra scripts, check_public.py and the git hooks |
examples/showcase/ |
A small mod that builds with 0 errors: a reworked class skill, an item skill with a new missile, a bigger stash |
tests/ |
Unit tests (py -3 -m unittest discover tests) |
Skills
Skills live in .claude/skills/. The agent picks the right one from your request; you can also
name it ("use d2r-port").
| Skill | Use it for |
|---|---|
d2r-mod-setup |
Set up the toolkit; create, build, install and run a mod; test characters |
d2r-skill |
New or reworked skills: functions, damage, tooltip, tree slot, icon, missiles, states, HD animation |
d2r-items |
Base items, uniques, sets, runewords, affixes, drops, vendors, item art |
d2r-monsters |
Monsters, bosses, monster skills, spawns, area levels, difficulty |
d2r-ui |
Inventory, stash and cube grids, panels, widget layouts, skill tree |
d2r-cube |
Horadric Cube recipes and their condition limits |
d2r-engine |
D2RLoader plugins, new engine functions, Ghidra reverse engineering |
d2r-debug |
Crashes, asserts, lost characters, wrong behaviour: logs, symptom to cause, bisecting |
d2r-crash |
A player's crash report: addresses to functions, who caused it, a fix with evidence |
d2r-port |
Port a classic 1.10 / 1.13c-era mod to D2R (with its creators' permission), table by table, with its DLL rules in a plugin |
d2r-coordinator |
Plan and run a project with several agents: lanes, models, game slots, bug list, review |
d2r-release |
Ship a version: bump, player changelog, fail-fast publish, verify, release pages |
Knowledge index
The knowledge base is the toolkit's memory. The agent reads the topic file before it works
in an area. It's also good reading for human modders.
| Page | What it covers |
|---|---|
| engine-limits.md | Every hard-coded limit a mod hits: the rule, the symptom when you break it, the fix |
| new-game-systems.md | Planning a new game system: data vs plugin, finding the hooks, where state lives, UI, lanes |
| tables.md | How D2R reads its .txt tables: indexed vs keyed, ids, adding rows safely, per-table notes |
| hd-graphics.md | .sprite files, item/skill/panel art, HD json maps, overlays, player and monster animation |
| ui-layouts.md | The HD layout json: widget tree, grids, panels, tabs, stash, inventory, cube, skill tree |
| strings.md | String files, whole-file replacement, safe new ids, the printf placeholder trap |
| saves.md | .d2s layout, checksum, retail and D2RLoader extended saves, safe and unsafe edits |
| item-format.md | The bit-packed item record in saves and item messages |
| engine-re.md | Finding and hooking D2R code: memory dump, headless Ghidra, query scripts, hook rules |
| engine-server.md | The server's game logic: tick, table sets and game edition, drops, inventory, vendors, quests, death |
| combat-formulas.md | Hit, block, damage and resist rules, experience, skills, hirelings |
| monsters-spawn.md | Monster spawning, packs, superuniques and the AI table |
| map-generation.md | How levels are generated, streamed and travelled |
| items-inventory.md | Death drops, item ids and seeds, level requirement, inventory requests, vendor stock and prices |
| skills-missiles.md | Skill start/do per slot, missile hits, timed states, mercenary AI |
| monster-ai.md | The AI tick, target search, common AIs and their aip values, monster init, player corpses |
| objects.md | Chests, shrines, wells, doors, traps and portals: populate, init and operate functions |
| quests-acts.md | Quests across the five acts: quest records and flags, the shared quest machinery, each quest's triggers and messages, the end of game |
| hirelings.md | Mercenaries outside combat: hire list and quote, hire, death, resurrect, items, save and messages |
| movement-modes.md | Collision, pathfinding, line of sight, speed, unit modes, animation timing, the event queue |
| units-stats.md | Unit header and ids, seed draw order, stat lists, states, party records, timing |
| network-messages.md | Game messages a plugin author sees when debugging: ids, join order, item actions, weapon swap |
| d2r-93847-addresses.md | Address registry for build 93847: functions, tables, record and unit offsets |
| debugging.md | Symptom → cause → fix, from real bugs, and where every log lives |
| crash-triage.md | From a player's crash report to a function and a fix, with a worked example |
| d2rloader.md | D2RLoader facts and traps: default mod, the first start, other plugins, version checks |
| porting-classic-mods.md | Porting a classic mod: inventory, table conversion, fixed ids, function numbers, art order |
| plugin-techniques.md | Plugin recipes: belt refill, buff HUD, weapon swap, waypoint UI, many clients on one machine |
| sdk-gaps.md | Where plugins still patch the game, and the D2RLoader SDK service that would replace each patch |
| modkit-ops.md | Every patch operation modkit build understands |
How the AI workflow works
A small mod is one person and one agent. A big one runs like a small studio:
flowchart LR
Owner([You, the owner]) -- goals, approvals --> Coord[Coordinator agent]
Coord -- one prompt per job --> Lanes[Lanes in parallel<br/>own branch + worktree]
Lanes -- need the game --> Slots{{Game slots<br/>queued}}
Lanes -- pull request --> Coord
Coord -- reviews, merges --> Main[(main)]
Main --> Release[Release] --> Players([Players])
Players -- bug reports --> Owner
- You decide, the coordinator plans, writes lane prompts, reviews and merges, and lanes each do one bug
or feature and open a PR. Lanes never merge. - Strong models where judgement matters (coordinator, engine work), cheaper ones for routine lanes.
- One numbered bug list, targeted tests per lane, and the full test suite at milestones.
- Every merged fix gets released. The agent asks you before anything public, paid or destructive.
The full method, with failure modes and a starter checklist, is in docs/ai-workflow.md.
Ready-made prompts and lists are in templates/.
Keeping your repo clean
Mods are often built in private and published later. The toolkit helps keep private things out of public git:
py -3 tools\install_hooks.py # pre-commit + commit-msg hooks run the scan for you
py -3 tools\check_public.py # scan files, PNG metadata, commit messages and authors
py -3 tools\check_public.py --no-history # files only
- It finds e-mails, IPv4 addresses,
C:\Users\<name>paths, keys and tokens, and PNG text chunks. - It also checks your own private-words list (other projects' names, your real name, server names). Keep that
list outside the repo, in~/.d2r-modkit/private_terms.txt(one word per line), or pointD2RAI_PRIVATE_TERMSat it. Hits are printed masked, so logs don't leak the words. - Generic false positives go in
check_public.allow. Private-word hits can't be allow-listed. - CI runs the tests and the generic scan on every push (
.github/workflows/ci.yml). - Commit with a no-reply identity. To publish a squashed public copy of a private repo, see
tools/publish_mirror.md.
Requirements
| What | Why |
|---|---|
| Windows 10 or 11 | D2R, D2RLoader and the native tools are Windows-only |
| Diablo II: Resurrected 3.3 (Battle.net or Steam) | The game files the mod is built from |
Python 3.12+ (py -3) |
modkit and every script (standard library only) |
git, Visual Studio with the C++ workload, CMake on PATH (3.20+, 3.29+ for plugins; VS's bundled CMake in Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin works) |
CascLib (tools\build_casclib.ps1), plugins (plugin-template\build.ps1) and the downloader |
| D2RLoader 1.3.1 (optional) | Plugins and extended saves |
| Ghidra (optional) | Reverse engineering for engine work |
| 7-Zip (optional) | Building release packages |
| An AI coding agent | Claude Code is the reference; see the FAQ for others |
Status and limitations
We'd rather tell you now than have you find out later:
- Target: D2R 3.3. Data-only mods follow the 3.3 file layout and aren't tied to a build number.
- Engine addresses are for build 93847 only (D2R image under D2RLoader 1.3.1, PE timestamp
0x6ab3782c). The
plugin template checks the image at load and turns its engine parts off on any other image. After a game update,
addresses must be found again (engine-re.md). - The plugin template is a starting point, not a framework. Engine work still needs care and Ghidra.
- No classic MPQ/DC6/DCC conversion pipeline: art goes in as PNGs and becomes D2R sprites.
- Windows only. Parts of
modkitrun elsewhere, but the game and the native tools don't.
FAQ
Does the repo contain any Blizzard files?No, and it never should. modkit fetch reads the game files from your own install into d2r-data/, which is
git-ignored, as are build outputs, dumps and saves. A built mod contains only what you changed. Rule 1 in
CLAUDE.md tells the agent the same.
Only for engine plugins or the extended save format (more than 30 skills per class, wide stat ids). A data-only mod
runs with D2R.exe -mod <Name> -txt. With D2RLoader, the first start after installing or updating it builds a cache:
start the game twice. See d2rloader.md.
Both. They ship the same program under different build numbers. The Steam 3.3 build seen so far runs the same
image as Battle.net's 93847. That's why requirement checks use the game version (3.3), never a build number, and
plugins check the image itself.
Yes. CLAUDE.md, the skills and the knowledge base are plain Markdown, and modkit is a normal CLI.
Point your agent's instruction file (for example AGENTS.md) at CLAUDE.md and tell it to read the matchingSKILL.md for each job. The workflow guide also covers mixing providers.
Yes, with permission from the mod creators. A mod is its creators' work: ask them before you start, and follow
their terms on credit and on whether and where the port may be published. If they say no, or you can't reach
them, don't port it. With permission, start with the d2r-port skill and
porting-classic-mods.md. That's where most of the knowledge comes from.
Any of them, or a mix. Your own host with the downloader gives one-click delta updates. GitHub Releases and Nexus Mods
host the files for free, and Nexus brings players. package_release.py --nexus writes an archive that is ready for
Nexus. See distribution.md for the comparison and the Nexus API's limits.
Modded games run offline. Keep mods and plugins away from Battle.net online play.
Contributing
Bug reports, knowledge fixes and new skills are welcome. Please read CONTRIBUTING.md first.
Facts about the engine are the most valuable thing you can add: say which build, and how you know. Security issues:
SECURITY.md. Everyone here follows the Code of Conduct.
Credits
- D2RLoader and its PluginSDK, which make D2R plugins and extended saves possible.
- CascLib, for reading the game's storage.
- The LZMA SDK (public domain), used by the downloader.
- Ghidra, the reverse engineering suite behind the engine work.
- Built from lessons learned porting a large overhaul mod to D2R, and from the modding community's decades of
table documentation.
License
MIT © Nugwa628 and contributors. Third-party code in submodules and downloader/third_party/ keeps its
own license.
d2rAI-toolkit is an unofficial fan project. It is not affiliated with, endorsed by or sponsored by Blizzard
Entertainment. Diablo and Diablo II: Resurrected are trademarks of Blizzard Entertainment, Inc. You need your own
legal copy of the game.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi