-
-
Notifications
You must be signed in to change notification settings - Fork 2
Building from Source
This page is for developers who want to modify Optimum. End users should use the Installation page instead — no build step is needed to use Optimum.
- .NET 10 SDK
- ilspycmd from
10.1.0.8386through10.1.1.8388, inclusive; the build prefers10.1.1.8388and rejects versions outside the range - bash (Linux/WSL/Git Bash)
- python3, git, curl, perl
git clone https://github.com/Zaldaryon/Optimum.git
cd Optimum
make build # downloads ~570MB client, decompiles, clones forks, patches, compiles
make test # runs the Optimum.Tests and Optimum.Launcher.Tests suites (400+ cases)
make run # deploys DLLs + shaders to .vanilla/ and launches the clientThe first make build takes 2-5 minutes depending on download speed. Subsequent builds take 5-10 seconds.
git clone https://github.com/Zaldaryon/Optimum.git
cd Optimum
.\scripts\bootstrap.ps1
dotnet build VintageStory.slnx -c ReleaseThe bootstrap script downloads the official Windows installer, extracts it with Inno Setup 6, decompiles with ILSpy, clones the open-source forks at pinned refs, and applies patches.
- Downloads the official Vintage Story 1.22.5 client archive for your platform.
- Extracts it into
.vanilla/. - Decompiles
VintagestoryLib.dllandVintagestory.dllwith ilspycmd intobuild/. - Runs post-decompile fixup regex (ref-casts, GeneratedRegex, ErrorCallback alias, ambiguous calls).
- Creates
.baseline/snapshot (unmodified decompiled source for diff generation). - Clones the open-source forks (vsapi, Cairo, vsessentialsmod, vssurvivalmod, vscreativemod) at the refs pinned in
forks.json. - Applies the patches in
patches/over both the decompiled and fork sources (111 patch files, of which 20 underpatches/runtime/target the ILSpy-decompiled layout the launcher transplants at runtime). - Copies Optimum-original sources from
sources/into the working tree.
Optimum/
├── Optimum.Launcher/ ← Runtime launcher (outputs Optimum.exe): cache, splash, assembly loading
├── Optimum.Launcher.Tests/ ← Launcher tests (cache, packaging, solution integrity)
├── Optimum.Patcher/ ← Cecil IL patcher: engine (Program.cs), API (api-patcher.cs), mods (mod-patcher.cs)
├── Optimum.Tests/ ← xunit test suite
├── optimum-api-contracts/ ← Optimum bridge methods called from patched vanilla API code
├── optimum-game-content/ ← Optimum runtime mod system (status command, diagnostics)
├── patches/ ← 111 patch files (generated by extract-patches.sh)
│ ├── runtime/ ← Patches against the ILSpy-decompiled layout, applied at launch
│ └── cecil-owned.list ← Patches that ship via Cecil transplant
├── sources/ ← Optimum-original source files, shaders and lang strings
├── scripts/ ← Build, package, install scripts
├── build/ ← Decompiled VintagestoryLib + Vintagestory (gitignored)
├── .baseline/ ← Clean decompiled snapshot for diff (gitignored)
├── .vanilla/ ← Vanilla client archives (gitignored)
├── VintagestoryApi/ ← Forked API project (gitignored, populated by bootstrap)
├── VSEssentials/ ← Forked essentials mod (gitignored)
├── VSSurvivalMod/ ← Forked survival mod (gitignored)
└── VSCreativeMod/ ← Forked creative mod (gitignored)
The patcher has three entry points, one per shipping path:
Optimum.Patcher/Program.cs — the engine transplant into VintagestoryLib.dll:
- 4 types to inject (OptimumInfo, OptimumUpdateChecker, EntityLightBatchBuffer, OptimumGreedyMeshEmitter)
- 112 member entries to inject into existing types (fields, methods, properties)
- 52 methods to transplant (replace a vanilla method body with the optimized version)
-
1 IL hook (inserts
_AddOptimumTabintoGuiCompositeSettings.ComposerHeader)
Optimum.Patcher/api-patcher.cs — ABI-safe rules against VintagestoryAPI.dll: two inventory dirty hooks, the chisel LOD hook, the chisel LOD shadow-pass hook, the logger static-initializer rewrite, and the GameVersion.LongGameVersion label rewrite.
Optimum.Patcher/mod-patcher.cs — EssentialsManifest() and SurvivalManifest(), naming the individual types, members and methods injected into VSEssentials.dll and VSSurvivalMod.dll.
A method containing a lambda or LINQ predicate cannot be transplanted. The C# compiler caches those as static delegates on a generated <>c type; injection clones only the named method, so the transplanted IL references something that does not exist and SelfConsistencyVerifier refuses to write output. Rewrite as an explicit loop first. Likewise, a Cecil-injected field never runs its C# initializer, because injection adds the field slot without constructor IL — use a local, or lazily allocate with ??= at the use site.
make deploy runs the Cecil IL patcher and copies results into .vanilla/:
make deploy # patch + copy
make run # deploy + launchmake testRuns the xunit suites (400+ cases across Optimum.Tests and Optimum.Launcher.Tests). Tests verify patch logic, data structures, and IL compatibility without requiring a bootstrapped working tree.
To add a new optimization:
- Make the change in the appropriate project (VintagestoryApi/, build/VintagestoryLib/, etc.).
-
make build— verify compilation. -
make test— verify tests pass. - If it's a VintagestoryLib change: add the method to
Optimum.Patcher/Program.cstargets. -
scripts/extract-patches.sh— regenerate patches from working tree vs baseline. -
scripts/check-patches.sh— verify 0 conflicts, 0 pending, 0 orphans. -
make run— verify in-game.
build/VintagestoryLibandbuild/Vintagestoryare regenerated from the cached decompile pluspatches/on every bootstrap. Editing them directly without re-runningscripts/extract-patches.shis invisible until the next fresh bootstrap, which silently reverts the edit.
| Target | Description |
|---|---|
make check |
Report installed tools (installs nothing) |
make bootstrap |
Download, decompile, clone, patch |
make build |
Bootstrap if needed, then compile |
make clean |
Remove obj/ and bin/ from project dirs |
make refresh |
Force full re-bootstrap |
make patches |
Regenerate patches/ from the working tree |
make deploy |
Run Cecil patcher, copy DLLs + shaders into .vanilla/ |
make run |
Build, deploy, launch client |
make test |
Run unit tests |
make package |
Build all release archives |
make package-linux |
Linux tar.gz |
make package-appimage |
Linux AppImage |
make package-macos |
macOS .dmg |
make package-win |
Windows zip |