feat: real WorldGen + SimDLL physics for dedicated server#385
feat: real WorldGen + SimDLL physics for dedicated server#385zed-assistant[bot] wants to merge 26 commits intomain-aifrom
Conversation
Port the mod to compile against the latest ONI game DLLs (build 21518087). API changes addressed: - KObject.GetEventSystem() now returns bool with out parameter - AccessControl.DefaultPermission → SetDefaultPermission - SkillListable removed, replaced with IListableOption - Chore.GetSMI() moved to protected StandardChoreBase, use reflection - SafeCellMonitor type hierarchy changed (Def instead of object) - ScheduleGroup constructor gained Color parameter - PauseScreen.OnQuitConfirm() requires bool saveFirst - Chore.addToDailyReport/reportType → GetReportType() - DevTool/DevPanel/DevToolManager not available, excluded via csproj
Adapt test code to ONI API changes: - PathProber is now static, remove AddComponent<PathProber>() - Worker renamed to StandardWorker - MinionConfig.MINION_NAV_GRID_NAME moved to TUNING.DUPLICANTSTATS
…t.Sdk) Enable dotnet test execution by adding required NuGet packages and IsTestProject property. Tests now run: 70 pass, 335 fail due to HarmonyLib mprotect EACCES on macOS ARM64 (W^X security restriction).
Bump OniMinimumSupportedBuild from 577063 to 21518087 to match the game version this PR was compiled against.
- SensorsPatch: prevent Sensors.Add() from calling sensor.Update() immediately, which crashed MingleCellSensor, ClosestEdibleSensor etc. during minion setup - ScheduleManager: add to test setup so MingleCellSensor.IsAllowed() doesn't NPE - ChoreConsumerStatePatch: null-safe Schedule resolution in constructor (game code bug: GetSchedule() can return null but ctor doesn't check) - MinionIdentityPatch: suppress Debug.LogError during OnSpawn when Personality DB is empty (test environment has no personality data) - AbstractChoreTest DI: inject ChoreExtensions dependency (was missing, causing NPE in chore.Register()) - PlayableGameTest TearDown: comprehensive singleton cleanup (~30 singletons) to prevent cross-test contamination - Unity mock patches: SystemInfo.processorCount, MonoBehaviour stubs, Object companion registration - ChoresPatcher: fix Chore constructor access (became protected), handle abstract Cleanup method
…ssion
- Add TestPersonalitiesCsv constant with valid personality entry ("TestDupe")
- Set MinionIdentity.personalityResourceId to match test personality
- Remove MinionIdentityPatch (SuppressErrors hack no longer needed)
- Revert DebugLogHandlerPatch to original behavior (always throw on LogError)
- Revert unnecessary whitespace change in BehaviourPatch
- .NET 4.8 console app with HttpListener web server - Mock world state: 64x64 grid with ONI elements, temperature, mass - React + TypeScript + Vite frontend with Canvas 2D renderer - Three overlay modes: elements, temperature, mass - Zoom, pan, cell tooltips, entity markers - API endpoints: /api/world, /api/entities, /api/state - Vite proxies /api to backend in dev, builds to wwwroot for production - Skip AssemblyExposure for DedicatedServer project - Add /decompiled/ to .gitignore (game IP protection)
…world boundary outline - Add LibNoiseDotNet, ImGui, Ionic.Zip references (fixes assembly load failure) - Fix RealWorldState to safely read Grid data (Grid.Mass crashes process) - Add world boundary outline to visualizer (vacuum was invisible against background) - Rebuild web client with boundary fix
- Register 11 elements (Vacuum, Oxygen, CO2, Water, Granite, etc.) matching frontend's hardcoded indices in constants.ts - Populate Grid cells with procedural world: rock floor, resource zone, habitable area with oxygen, water pool, cold zone, vacuum top - Fix Grid memory: use GCHandle.Alloc(Pinned) instead of fixed() blocks that lose pinning after scope (caused garbage data in long-running server) - Add /api/elements endpoint returning element registry (id, name, state) - RealWorldState now returns element mass from defaults
- GameLoader now loads all 191 elements from game's YAML files (bypass SubstanceTable, create stub Substances) - Patches Application.streamingAssetsPath to real game path - Initializes SimDLL: SIM_Initialize → CreateSimElementsTable → SimDataInitializeFromCells → Start (Grid points to DLL memory) - Simulation tick loop runs at 200ms intervals - WorldGen attempted but falls back to procedural world (noise generation needs CustomGameSettings fix - TODO) - Frontend: dynamic element loading from /api/elements (supports 191+ elements with auto-generated colors) - RealWorldState reads Grid.mass from SimDLL-owned memory
- Fix WorldGen: call SetWorldSize() before GenerateOffline() (matches Cluster.BeginGeneration flow) - Fix DebugLogHandlerPatch: test patch throws on LogError, but WorldGen uses Debug.Assert for non-fatal warnings. Added SoftDebugLogHandler with Priority.First to log instead - Fix ElementLoader: populate elementTagTable (needed by EnsureEnoughElementsInStartingBiome) - Remove procedural fallback — WorldGen is the only path now - SandstoneDefault 256x384 generates real biomes + SimDLL physics
- Initialize GenericGameSettings._instance via reflection (prevents NPE in WorldGen error reporting) - Patch ReportWorldGenError to skip KCrashReporter calls - Remove CustomGameSettings.Awake() from InstallPatches (KMonoBehaviour needs full game init first)
- Remove compiled wwwroot/assets/ from git (add to .gitignore) - Update web-client README with actual build/run instructions - Assets are embedded as resources in DedicatedServer.csproj
| /// <summary> | ||
| /// Generate a real ONI world using the game's WorldGen pipeline. | ||
| /// </summary> | ||
| private WorldGenResult? GenerateWorld() { |
There was a problem hiding this comment.
That seems to be a lot of code. Can we somehow just call game library to generate it? Without repeating their logic?
There was a problem hiding this comment.
Fixed — now calls ElementLoader.Load() directly with stub SubstanceTables. All the manual YAML parsing and element creation code has been removed.
src/MultiplayerMod.Test/Core/Patch/Compatibility/PatchesCompatibility.cs
Outdated
Show resolved
Hide resolved
- Remove MockWorldState — no longer needed with real WorldGen - Use game's ElementLoader.Load() directly instead of reimplementing YAML parsing and element creation logic - Make StreamingAssets path configurable: auto-detect from common Steam paths (macOS/Linux/Windows) or ONI_STREAMING_ASSETS env var - Simplify ServerBootTest to thin wrapper - WebServer returns 503 when world not loaded instead of mock data
- All paths via env variables only (ONI_STREAMING_ASSETS required) - Remove hardcoded macOS/Linux/Windows Steam paths from GameLoader - Remove hardcoded Steam path from Program.cs assembly resolver - Add ServerLauncher.cs — loads DedicatedServer via reflection to work around Harmony self-test deadlock on standalone Mono - Update run-server.sh to use ServerLauncher instead of dotnet test
- Add StartupObject to resolve duplicate entry point (Program vs TestHost) - Add missing System.Collections.Generic using - Fix ElementLoader.path via Harmony prefix on CollectElementsFromYAML - FileSystem.Initialize() for game YAML loading - StubManifestSubstance via Harmony (bypass Unity SubstanceTable)
- Fix MISSING.STRINGS element names — use tag name (elementId) when localization strings not loaded - Add App.config with useLegacyV2RuntimeActivationPolicy (needed for Harmony on standalone Mono)
- Remove ServerBootTest.cs — use ServerLauncher as entry point - Remove DefaultWidth/Height constants — WorldGen sets size from settings - Revert PatchesCompatibility.cs to main-ai state - Remove useLegacyV2RuntimeActivationPolicy from App.config - Fix grid rendering gaps between cells in visualizer
- Fix sub-pixel grid artifacts (Math.round instead of +0.5 overlap) - Element names use tag.Name fallback (localization not available headless) - Move width/height init to Boot() with clear comment - Re-add ServerBootTest as temporary entry point until Unity Mono ready
MonoMod's DeterminePlatform() blocks forever on macOS Rosetta because it spawns `uname` and StreamReader.ReadLine() hangs. Pre-setting PlatformHelper.Current = MacOS via reflection before any Harmony use skips the broken auto-detection. Server now runs with plain `mono DedicatedServer.exe` — no test runner needed.
|
If the simulation is suppose to be running with no clients connected in a headless manner would this not mean a colony would just die out when no players are connected? Also what's the plan with things such as colonists, AI, critters etc. |
It should probably auto-pause if clients aren't on the server (could be a option) |
Summary
Phase 2 of dedicated server (#383): real game world generation + SimDLL physics.
Screenshot
Real SandstoneDefault world (256x384) generated by the game's WorldGen pipeline, with SimDLL physics running at 5 ticks/sec. Web visualizer shows element overlay with 191 game elements.
What changed
ElementLoader.Load()Environment variables
ONI_STREAMING_ASSETS— required, path to game's StreamingAssets directoryHow to run
Next steps
Phase 3: multiplayer — connect clients via LAN transport.