Skip to content

v0.3.0

Choose a tag to compare

@Zaldaryon Zaldaryon released this 30 Jul 22:40
· 5 commits to main since this release
v0.3.0
447a765

New

  • Optimum is now a launcher, not a pre-patched game. 0.2.x shipped complete optimized assemblies. 0.3.0 ships Optimum.exe (the launcher), Optimum.Patcher.dll (Cecil), and runtime "donor" DLLs, and patches copies of the game's own assemblies at launch. Four shipping paths carry the changes: an engine Cecil transplant into VintagestoryLib.dll (4 injected types, 52 method transplants, 1 IL hook), an ABI-safe API patch into VintagestoryAPI.dll, runtime donor manifests for VSEssentials.dll and VSSurvivalMod.dll, and an asset overlay for the optimized shaders and the Optimum language strings. Vanilla files on disk are never modified.
  • Patch results are cached and validated per launch. First launch (or the first launch after a Vintage Story or Optimum update) applies the patches and writes them to .optimum/cache/; every launch after that validates the cache and loads it. If any patch step fails, the launcher falls back to a vanilla launch instead of leaving a half-patched install.
  • Branded patch splash screen. The cache-miss path now shows a small window styled after the vanilla GuiScreenLoadingGame (same font family, colour, and size), updated per patch target, so the first launch reads as one continuous loading sequence instead of a console flash. It is a core-profile GL 3.3 renderer with a logging GLFW error callback, so a headless host, a Wayland compositor rejecting window positioning, or an unsupported driver degrades to log-only and never blocks patching.
  • No more console window. The launcher builds as WinExe and writes every diagnostic line to {dataPath}/Logs/optimum-launcher.log (truncated per run) instead of holding a console open for the whole session.
  • Optimum window identity. The client window title is Optimum, the window icon is set from the Optimum-branded gameicon.png at window creation (rather than relying on WM_CLASS resolution, which OpenTK's bundled build cannot set), and GameVersion.LongGameVersion carries a + Optimum v0.3.0 suffix on Windows, Linux and macOS.
  • Vintage Story 1.22.5. All patches were regenerated against the 1.22.5 baseline and the fork refs in forks.json were moved to the 1.22.5 source release.

Performance

  • Map page cache. ChunkMapLayer's per-chunk upload pipeline is replaced by an 8x8-chunk page cache (256x256 px per page). Pages persist to ModData/optimum-map/{world-id}/ as GZip-compressed RGBA written by a background coalescing thread, upload into a GL_TEXTURE_2D_ARRAY (128 layers, free-list allocator, LRU eviction) with BC7 Mode 6 compression when GL_ARB_texture_compression_bptc is present (64KB vs 256KB per page), and draw through a single GL.DrawArraysInstanced call against the optimum-map shader instead of one draw per map component. 2x and 4x box-filtered LOD tiers serve zoomed-out views, and a min-heap priority queue keyed on squared distance from the viewport centre loads near pages first. Falls back to the vanilla per-component path when no pages are resident.
  • Optional map terrain pre-generation. With MapPageCachePregen enabled in .optimum/optimum.json (default off), unexplored chunks entering the viewport are filled on the background map thread with biome-approximated colour derived from the world seed plus the client-side climate, ocean and forest maps, shaded by replaying GenTerra's noise (9 octaves, freq 1/3267, persistence 0.9) over a 34x34 padded height grid for NW-illuminated slope brightness. Pregen pixels are desaturated 35% so they stay visually distinct from explored terrain, and explored data always wins.
  • Wind and rain volume deadzone now actually ships. WeatherSimulationSound.updateSounds skipped OpenAL SetVolume calls below a 1% delta in the source tree since 0.2.x, but updateSounds was never a transplant target, so the shipped game never ran it. It is now in the Essentials runtime manifest along with its four tracking fields.

Fixes

  • Native 0xc0000005 crash when switching settings tabs. _AddOptimumTab widened the settings background bounds without widening the composer's own surface bounds. GuiElementDialogBackground blurs up to Bounds.OuterWidth through raw unclamped pointers, so a background wider than the Cairo surface overran the native pixel buffer — a stack-trace-less access violation on every in-game tab switch, previously misattributed to widening c.Bounds and reverted twice without being fixed. Every fixed-width ancestor bounds, up to and including the composer's own, now grows by the same delta so background and surface stay in lockstep on both the main-menu and in-game paths.
  • A patch-resolution failure could silently drop the whole install back to vanilla. The Cecil assembly resolver for the --mod runs searched only the vanilla input directory, its Lib folder, the donor directory and the output directory — none of which hold VintagestoryAPI.dll for the mod pass (input comes from .optimum/vanilla/Mods, donors from .optimum/donors). It stayed invisible until Cecil had to resolve a type while writing output, at which point AssemblyDefinition.Write threw AssemblyResolutionException, the patcher process died, and the launcher fell back to a fully vanilla launch — discarding even assemblies that had patched cleanly. The resolver now walks up from both the vanilla and compiled paths to the first ancestor directory containing VintagestoryAPI.dll and adds it plus its Lib folder.
  • Chiseled blocks cast full-cube shadows at any distance. Two separate bugs. The shadow-cull path (CullInstantShadowPassNear/Far) had no chisel-distance awareness in either the source tree or the shipped runtime, so vanilla's plain LodLevel >= 1 check let the cheap full-cube LOD 3 proxy into the depth map even right next to the camera. Separately, OptimumApiBridge.InFrustumAndRange ignored its lodLevel parameter for chisel-flagged locations and returned one shared boolean for both LOD 2 and LOD 3 — up close both meshes drew together (z-fighting), and past the chisel distance the block vanished entirely instead of falling back to the proxy. Fixed with a new InFrustumShadowPass bridge method plus a PatchChiselLodShadowHook IL hook, a shared OptimumChiselLodVisible helper for the source tree, and per-lodLevel branching in InFrustumAndRange.
  • A self-reference in a transplanted method broke every patch on install. updateSounds contains .All(s => s.IsReady) LINQ predicates that the C# compiler caches as static delegates on a generated <>c class. Cecil member injection clones only the named method, not that nested type, so the transplanted IL referenced a method that did not exist in the output. SelfConsistencyVerifier correctly refused to write, but the resulting ModPatcher.Patch exception was unhandled in Optimum.exe and killed the launcher mid-sequence, leaving VSEssentials.dll, VintagestoryLib.dll and VintagestoryAPI.dll all fully vanilla — no Optimum tab, no version suffix. Both .All(...) calls are now explicit loops, with a regression test.
  • Four crashes from Cecil-injected fields whose initializers never ran. Member injection adds a field slot but does not run constructor IL, so any C# field initializer on an injected field silently vanishes. WeatherSystemClient.optimumWindSpeed/optimumSurfaceWindSpeed (null Vec3d, NRE on the first render frame after joining, now plain locals), AStar.optimumNodePool (NRE per pathfinding attempt, swallowed by vanilla's try/catch, leaving every pathing entity unable to move; now lazily allocated), MechanicalPowerMod.optimumTickNetworks (NRE on every server tick, ~16,000/s, producing a 73MB log and an auto-shutdown at 100,000 errors; now lazily allocated) and TreeGen.vineScratchPos/positionStack.
  • NRE on joining a world. WeatherSystemClient.OnRenderFrame dereferenced capi.World.Player.Entity before the local player existed, and then WeatherDataAtPlayer before the server's LevelFinalize packet had populated it. Most reproducible in singleplayer, where render frames tick faster than the integrated server's join sequence. Both are now guarded with an early return.
  • The Optimum settings tab disappeared, and the Back button overflowed the panel. _AddOptimumTab widened c.Bounds, the dialog composer's own on-screen extent, which is a different object from what the tab buttons are parented to — so the dialog re-centred without moving the buttons, pushing the Optimum tab and Back button outside the panel background. Fixed by widening oButtonBounds.ParentBounds (the real layout parent) on both paths, and additionally c.Bounds in-game only. Inter-tab gaps are 10px and the Back gap 15px so the row fits narrow windows, and the Extra tab's switches are now centred on their label rows.
  • Crucible rendered pitch black above 900°C (#9718 regression). CrucibleInFirepitRenderer added incandescence colour straight into RgbaLightIn, saturating the light vector above 1.0, which some Nvidia drivers render as black. Glow now goes through the dedicated RgbaGlowIn uniform with alpha = extraGlow/255f, matching the ForgeContentsRenderer path that always worked.
  • FSR at native resolution no longer differs from vanilla. The LOD-bias SamplerParameter/TexParameter calls are skipped when Render Scale is Native (1.0x); vanilla never sets them, so native-resolution output is now bit-identical to vanilla instead of subtly different.
  • The version label was appended twice. Both ApiPatcher.PatchGameVersionLabel (rewriting GameVersion..cctor) and a source patch in ClientProgram.Main suffixed LongGameVersion, producing v1.22.5 (Stable) + Optimum v0.3.0 + Optimum v0.3.0. The Cecil patch is now the single source of truth.
  • Three patches were out of sync with build/. The settings-tab spacing fix and the FSR native LOD-bias skip were edited directly in build/VintagestoryLib without regenerating patches/, so any fresh bootstrap silently reverted them. All patches under patches/VintagestoryLib and patches/Vintagestory now apply-and-diff back to build/ exactly.
  • Linux and macOS packages launched unpatched. Both scripts copied Optimum.Api.Contracts.dll to the stage root instead of populating .optimum/donors/ and .optimum/vanilla/Mods/ with the names the launcher expects, so the launcher fell straight through to vanilla. Both now stage all four donors, and both patch VintagestoryAPI.dll at build time for the version label.
  • install-linux.sh prerequisite check. Reading ilspycmd's version through a pipe into head -n1 could deadlock; the script now reads the full output with a 5s timeout backstop. dotnet tool update --version also refuses NuGet-considered downgrades, so the update call passes --allow-downgrade and the fallback uninstalls first.

Verification

  • All four real patch operations run end to end against a vanilla 1.22.5 install and exit 0 with zero self-reference errors: VintagestoryLib (default), VintagestoryAPI (--api), VSEssentials (--mod), VSSurvivalMod (--mod). 52/52 engine method transplants applied.
  • Full VintageStory.slnx solution build clean; 400+ test cases across Optimum.Tests and Optimum.Launcher.Tests passing, including new regression tests for each Cecil field-injection null, the lambda-transplant self-reference, the launcher's WinExe/logging contract, and the splash screen's native asset packaging.
  • The new chisel-LOD shadow hook was verified against real vanilla IL via a live --api patch run and IL dump, not only against the decompiled source tree.
  • check-patches.sh reports 0 conflicts, 0 pending, 0 orphans.

Notes

  • This is an architectural rewrite. Delete any previous Optimum installation before installing 0.3.0.
  • Not every source patch in the repository reaches the shipped game. A patch ships only when a Cecil target, an API rule or a runtime donor manifest owns it; the complete inventory, including the patches that currently do not ship, is in docs/patch-shipping-audit-0.3.0.md.
  • Targets Vintage Story 1.22.5.
  • Source-only release. See the wiki for install instructions.