refactor: move the backend-independent infrastructure into core - #21
Closed
ResurrectedTrader wants to merge 1 commit into
Closed
ResurrectedTrader wants to merge 1 commit into
ResurrectedTrader wants to merge 1 commit into
Conversation
ResurrectedTrader
force-pushed
the
shared-core
branch
5 times, most recently
from
September 15, 2026 14:53
70ee684 to
fa9b692
Compare
ResurrectedTrader
force-pushed
the
deps-baseline
branch
2 times, most recently
from
September 15, 2026 16:19
b5239dd to
60c3aea
Compare
ResurrectedTrader
force-pushed
the
shared-core
branch
from
September 15, 2026 16:21
fa9b692 to
8baca96
Compare
ResurrectedTrader
force-pushed
the
shared-core
branch
from
September 16, 2026 20:03
8baca96 to
38406a9
Compare
Everything here is either shared infrastructure lifted out of the 1.14d backend, or a fix to code that was already there. Hooking. `src/core/detour/` is one typed Detours API: `Target` says where a function lives, `Hook<Fn>` is a slot whose target and replacement are checked against each other at compile time and whose `Real()` reaches the original whether or not it is attached, and `Batch` is one transaction that de-duplicates exports sharing a body and aborts rather than committing an empty update. The call sites had each rolled their own, and two of them carried the same defect: a failed commit left the site believing it had attached, so the matching remove detached hooks that were never installed - eighteen of them in speedhack. Realms additionally dropped its detach error and nulled its real-function pointers while in-flight hook bodies could still be running. A failed `DetourTransactionBegin` is now aborted rather than left pending: one of its two failure modes claims the process-wide transaction slot before returning the error, and leaving that claimed wedges every later attach and detach in the process. Shared infrastructure. The `WH_GETMESSAGE` input hook and the game-window WndProc subclasses move to `src/core/input/`, taking their callbacks from the backend rather than reaching into it. Those callbacks are plain function pointers, like the ones in `GameCallbacks` they are fed from, and each dispatch loads its pointer once - `Remove()` clears the table from a different thread than the one dispatching, which under `std::function` destroyed a callable while it was executing. The SOCKS5 connect hook moves to `src/core/proxy/`. Command-line parsing moves to `src/core/config/OptionParser`, which also now removes the switches it parsed from the process command line, so a CD key passed as `-d2c` stops being readable through the PEB, through `GetCommandLineW` and through the ANSI copy once it has been consumed. Twenty-three tests cover the removal. Logging. `utils::GetLogger(name)` hands out named loggers over one shared sink, so where output goes is decided once by `AddLogSink` rather than by when a logger happened to be created - which also fixes loggers made before the sinks were installed being orphaned from them. Default volume is unchanged: the framework's own logger at debug, everything else at spdlog's default. Loggers flush themselves from warn, with a one-second periodic flush underneath, so a crash loses at most the last second rather than forcing an fflush on every line inside the shared sink's lock. Raw `spdlog::` calls are gone. Contract. `InstallHooks` returns bool, and the frontend aborts init when a backend cannot bring its hooks up rather than running on half-installed ones. `game::console::BackendPanel` lets a backend contribute console tabs as an interface instead of a list of function pointers. A rotating file log sits alongside the console sink, under logs/ next to the ini. Platform. `utils`, `contract`, `core` and `js` hold no game-version specific code, so they now build for x64 as well as Win32 and CI compiles both. The 1.14d backend, its glue and the tests stay Win32. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ResurrectedTrader
force-pushed
the
shared-core
branch
from
September 16, 2026 21:14
38406a9 to
53ac80d
Compare
Owner
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Shared infrastructure lifted out of the 1.14d backend, plus fixes to code that was already there. No behaviour change to the bot, and 1.14d keeps the same hooks in the same order at the same install points.
Hooking
Nine call sites each opened their own Detours transaction, kept their own "real function" pointer and their own installed flag, in three different shapes. They now share
d2bs::detourinsrc/core/detour/:Targetsays where a function lives, a named export of a module or an address the caller already has, resolved at attach time.Hook<Fn>is one typed slot. Its target and replacement are checked against each other at compile time, andReal()reaches the original whether or not the slot is attached, so a caller outside the replacement can use it as a plain handle.Batchis one transaction. It de-duplicates exports that are thunks into a shared body, aborts rather than committing an empty transaction, and only ever touches the slots it queued.That last property is what fixes a real defect. A failed commit used to leave a site believing it had attached, and the matching
Remove()then detached hooks that were never installed: eighteen of them in speedhack, two in the 1.14d hook manager. Realms had two more of its own, dropping the error from its detach commit and nulling its real-function pointers immediately after detaching, while in-flight hook bodies on the Battle.net connect worker could still be calling through them.InlinePatchand the VMT hook stay as they are; neither is a Detours transaction.Shared infrastructure moved into core
src/core/input/- theWH_GETMESSAGEhook and the game-window WndProc subclasses, taking their callbacks from the backend through a small struct rather than reaching into it.src/core/proxy/- the SOCKS5 connect hook.src/core/config/OptionParser- launch-option parsing.None of this is 1.14d-specific, and none of it belonged in a backend.
Command-line hygiene
OptionParsernow takes the switches it parsed back out of the process command line. A CD key passed as-d2cwas readable for the rest of the run by anything that can see the PEB: Task Manager, WMI'sWin32_Process.CommandLine, a remote read. The switch and its value are cut out and what follows shifts down over the gap, so neither the value nor its length survives and the line still parses as a well-formed command line holding only the arguments the game itself was given.The cut is skipped entirely when our tokeniser and
CommandLineToArgvWdisagree on the token count, so an input we do not model is left alone rather than guessed at.RemoveOptionsis the buffer-level half, split out so it can be tested; sixteen cases cover quoted values, escaped quotes, bare flags, repeats, a trailing switch with no value, and a program name that spells an option.It cannot reach the argv array the game's own CRT built at its startup from its own private copy. That is readable only by something already walking the process heap, which is a different threat from anything that reads a process's command line.
Logging
utils::GetLogger(name)hands out named loggers over one shared fan-out sink, so where output goes is decided once byAddLogSinkrather than depending on when a logger happened to be created. That also fixes a routing bug: loggers created before the sinks were installed used to copy the default logger's sink list at creation and were orphaned forever once the real sinks arrived. Rawspdlog::calls are gone.Two small accessors make the levels controllable later:
SetLogLevel(name, level)sets one logger by exact name, applying immediately if it exists and remembering the level if it does not yet, andLoggers()lists the loggers that exist with the level each is emitting at, growing as components log for the first time. Between them a settings panel can render the list and adjust an individual component. Nine tests cover the pair, including that a level set before a logger exists is applied when it is created, and that matching is exact rather than by prefix.Default volume is deliberately unchanged from before this branch: the framework's own
d2bslogger runs at debug, every other logger keeps spdlog's default of info. The only change to flushing is that a logger now flushes from debug rather than warn, so entries are on disk before a crash rather than sitting in a buffer.A rotating file log now sits alongside the console sink, capped and rolled, under
logs/next to the ini. Failing to open it is not fatal.Contract
InstallHooksreturns bool, and the frontend aborts initialisation when a backend cannot bring its hooks up, instead of running on half-installed ones.game::console::BackendPanellets a backend contribute console tabs as an abstract class rather than a list of function pointers.Platform
utils,contract,coreandjscontain no game-version-specific code, so they now build for x64 as well as Win32 and CI compiles both. The 1.14d backend, its glue and the tests stay Win32, and the solution maps that.Verification
build.ps1 Releasebuild.ps1 Release -Platform x64build.ps1 testbuild.ps1 check-formatbuild.ps1 lint