Skip to content

Add Realistic ACS - #191

Merged
OwendB1 merged 2 commits into
StarCpt:mainfrom
KLoverTony:add-realistic-acs
Sep 14, 2026
Merged

OwendB1 merged 2 commits into
StarCpt:mainfrom
KLoverTony:add-realistic-acs

Conversation

@KLoverTony

Copy link
Copy Markdown
Contributor

Adds the Realistic ACS client plugin descriptor.

  • Source: https://github.com/KLoverTony/realistic-acs
  • Pinned to commit 09a902b7c57de24d1bac6801c82df25eca3116bf
  • Targets Pulsar Legacy / .NET Framework.
  • The RCS Blocks content mod is optional and linked in the descriptor; it is not a plugin dependency.
  • The plugin is documented as a client-side local-physics prototype and not multiplayer-safe.

@OwendB1 OwendB1 self-assigned this Sep 11, 2026
@OwendB1

OwendB1 commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Thank you for the nice PR comment layout! Reviewing this now.

@OwendB1

OwendB1 commented Sep 11, 2026 •

Copy link
Copy Markdown
Collaborator

Reviewed PluginHub PR head 391886d1de08b995080a074a17425c4c7ac5ac8e and KLoverTony/realistic-acs at pinned commit 09a902b7c57de24d1bac6801c82df25eca3116bf. This is a first registration, so I audited all 28 tracked repository paths at the pin rather than using a commit diff.

Summary: the manifest validator and Pulsar-real source compilation pass, the pin is the public repository's current main, and I found no network, process-launch, dynamic-code, committed executable, or concrete server-authority-bypass surface. Two physics/lifecycle issues should be addressed before listing: the allocators can inject torque from a force plan whose net force is discarded, and hydrogen-thruster overrides are not restored on every exit path. Performance, logging, unused build dependencies, and repository-level asset licensing are lower-severity follow-ups.

Manifest

python3 test.py Plugins at the hub PR head reports All files validated.

The GUID <Id>, <RepoId>KLoverTony/realistic-acs</RepoId>, author, NETFramework runtime, and commit pin are internally consistent. SourceDirectories limits Pulsar to acs-client-plugin/ClientPlugin, which contains 2,390 lines across three .cs files. The solution, build/deploy scripts, proposal, and optional companion mod are not compiled or loaded by the hub.

The manifest declares no NuGet packages, dependencies, alternate versions, or assets. That matches the loader-real compilation: the selected source needs only Pulsar's game dependency closure. The repository's own RealisticAcs.xml is a non-authoritative local-development copy and was not treated as the registry pin.

Repository and pin provenance

The target repository is public, owned by the PR author, and the full pin resolves exactly. It is the current tip of the default main branch: comparing the pin to main is identical, with no newer source drift. There are no tags or releases.

All 28 tracked paths were accounted for. Every text, source, XML, project, configuration, script, and documentation file was read. The six .mwm files have Space Engineers model headers/string tables, and the 128×128 .DDS has a valid DDS header; none is in the plugin's selected source or asset closure. The tree has no DLL/EXE/native library, archive, submodule, symlink, or Git LFS pointer.

Security

  • No network API or destination exists in compiled scope; no credentials, Steam IDs, machine identifiers, or local content are transmitted.
  • No process or shell invocation, runtime assembly loading, Roslyn/Emit path, crypto/base64/compression-to-execute path, or encoded executable payload exists.
  • The one reflective invocation calls the fixed private MyEntity.InstantiateSubpart member with plugin-created model data; neither the type nor member comes from external input.
  • The only file write is realistic-acs.log beside the compiled plugin assembly. That location and write pattern are a finding below, but there is no arbitrary path input.
  • There are no Harmony patches or AppDomain-wide hooks. The game chat event is removed in Dispose; angular damping records and restores its original value.
  • Direct Havok torque and angular-damping changes are local. I found no path by which a server accepts or rebroadcasts unauthorized state, exposes hidden server data, or otherwise loses authority. Client-only operation and the repository's multiplayer disclaimer are therefore not findings by themselves.

Project-reference audit: all 48 <Reference> hint paths resolve to the installed Space Engineers Bin64, with matching declared assembly names/versions and Private=False; there are no project, framework, COM, or analyzer references and no unresolved imported reference file. DirectShowLib 2.1.0.1599 (SHA-256 8402f26b3ca1cc4aebab94386b19c4201b463d9d5dc8510a1f8e13dd28efd45c) is an installed game binary referenced by VRage.Platform.Windows. EmptyKeys.UserInterface 3.2.0.0 (c6d36cdfb422aa575df6d5820eb87c8547a71c57cdf851fbedcbe233a6e35f12) and .Core 3.2.0.0 (f0699f6cc3fe04f34254fae5162aa789617f5d4da783c1393b1edfae11528201) are installed game UI binaries referenced by Keen assemblies. The selected plugin source directly uses none of those three.

ClientPlugin.csproj also declares Lib.Harmony 2.4.2 and Mono.Cecil 0.11.6. Neither is used by selected source or consumed by PluginHub's source compiler; this is covered as a low-severity build-provenance finding below.

From-source build

I reproduced the registry path with Pulsar's RoslynCompiler at 8aa6a4be6adf0d5975637cb63356b48706ebb623, compiling exactly the three manifest-selected source files with TRACE, NETFRAMEWORK, and PULSAR against the game dependency closure: compilation succeeded and returned no failure diagnostics. A normal pinned-tree net10.0 project build also succeeds with 0 errors; its only warning says the optional local Pulsar deployment folder is absent.

Game-member behavior was checked against Space Engineers 1.210.014 decompiled source. In particular, MyThrust.ThrustOverridePercentage is a Sync<float, SyncDirection.BothWays> value and is serialized into the thrust object builder, which makes incomplete restoration persistent rather than merely cosmetic.

Findings

1. High: force residual is computed but discarded while its torque is applied

Both live paths calculate a physical wrench from candidate forces: the RCS allocator accumulates TranslationResidual with AchievedTorque (Plugin.cs lines 894–907), and the ordinary-thruster allocator does the same (lines 1883–1893). The live controller checks only torque alignment and injects AchievedTorque; it never checks or applies the associated net force (lines 365–393).

On an unbalanced or authority-limited layout, this permits rotational authority from a force plan that is not a realizable pure couple: the ship receives the torque while the plan's translation vanishes. Please define an absolute/normalized force-residual tolerance and withhold or scale allocations that exceed it, or apply a physically matching full wrench. The first option preserves the plugin's attitude-only architecture but reduces authority on poor layouts; the second changes translation and needs broader flight-control testing.

2. Medium: hydrogen override ownership is not restored on grid loss or unload

UpdateHydrogenRcsFuelDemand writes ThrustOverridePercentage every visual update (Plugin.cs lines 604–615). When the controlled grid disappears, the code clears only dictionaries (lines 445–454); Dispose() restores the experimental pulse and angular damping but does not restore RCS thrusters (lines 131–150). Because this game property is synchronized and saved, the last duty-cycle value can outlive the plugin or grid-control session.

Track each touched thruster's original override and restore it on grid switch, world loss, disarm/setup failure, and plugin unload. Preserve pre-existing user values rather than assuming zero.

3. Medium: active control performs several grid scans and bounded brute-force solves per simulation frame

Every Update() calls visuals, attitude control, and rotational dampening (Plugin.cs lines 153–162). Visuals scan every grid block each frame; active attitude control scans again for RCS pods and ordinary thrusters. The ordinary allocator allows up to 800 full coordinate-descent sweeps over all thrusters (lines 1896–1940). Each active pod's visual pose also tests 73 azimuth × 25 elevation samples—1,825 transforms/dot products per pod per frame (lines 1045–1083).

Cache grid membership and actuator geometry behind topology/working-state changes, solve at a bounded cadence or only when inputs/state materially change, and replace the visual grid search with an analytic two-axis inverse or cached direction table. Please profile a large grid with many thrusters and pods before release.

4. Low: synchronous unbounded diagnostics use the loader-cache directory

WriteLifecycleLog resolves the executing assembly directory and calls File.AppendAllText for every message (Plugin.cs lines 2344–2355). The five-second diagnostic walks the grid and emits per-thruster records, so a long session repeatedly opens an unbounded file on the simulation thread. It also puts persistent user data beside Pulsar's compiled output instead of the game's configured user-data root.

Use a plugin-owned directory beneath MyFileSystem.UserDataPath, buffer/rate-limit verbose diagnostics, and rotate or cap the file. Keep game-object reads on the simulation thread if writes are queued.

5. Low: two unused NuGet packages remain in the IDE build graph

ClientPlugin.csproj lines 239–248 restore Lib.Harmony and Mono.Cecil, but no selected source uses either and the hub manifest declares neither. Pulsar does not consume these project package declarations when compiling registry source. Remove both until compiled code needs them; this reduces local restore trust and keeps the project aligned with what users actually compile.

6. Low: companion-mod asset licensing/provenance is not explicit at repository level

GitHub detects no repository-level license. The only license file is acs-client-plugin/LICENSE, whose copyright holder is blank. The separate companion mod contains six committed models and a texture outside that directory, with no explicit authorship/source or license covering those assets.

Add a root license that states its holder and scope, and document the origin/license of the .mwm and .DDS assets. This does not place executable code in Pulsar's closure, but it removes ambiguity for redistribution through the linked companion mod.

@KLoverTony

Copy link
Copy Markdown
Contributor Author

Updated the descriptor to pin Realistic ACS to d746fc45df274ec39b2ad05e65e32e1c5bcfdd42.

This revision addresses the review findings:

  • rejects unbalanced virtual force plans;
  • restores hydrogen RCS overrides on all exit paths;
  • caches allocator/visual work and bounds solve cadence;
  • moves buffered, capped logs to the SE user-data directory;
  • removes unused Harmony/Mono.Cecil packages;
  • adds root MIT licensing and asset provenance.

I also completed local in-game testing for attitude control, override torque/counter-control, AlwaysOn registration cleanup, and restoration behavior. The optional RCS content mod remains separate and is not a PluginHub dependency.

Note: the local machine did not have Python available to rerun test.py; the descriptor itself was reviewed and the source net48 build passed.

@KLoverTony

Copy link
Copy Markdown
Contributor Author

Follow-up validation: Python 3.13.15 is now installed locally and python test.py Plugins was run against the updated add-realistic-acs branch. Result: All files validated.

@KLoverTony

KLoverTony commented Sep 13, 2026 via email

Copy link
Copy Markdown
Contributor Author

@OwendB1

OwendB1 commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Re-reviewed PluginHub PR head fead23364cb018a5d05db21bee051d9b75bfc6d4. The manifest is absent from the hub base, but the same PR previously pinned KLoverTony/realistic-acs at 09a902b7c57de24d1bac6801c82df25eca3116bf in hub commit 391886d1de08b995080a074a17425c4c7ac5ac8e; the current pin is d746fc45df274ec39b2ad05e65e32e1c5bcfdd42. I therefore reviewed the exact direct source diff 09a902b7c57de24d1bac6801c82df25eca3116bf..d746fc45df274ec39b2ad05e65e32e1c5bcfdd42: 6 changed paths, 627 insertions, and 85 deletions.

Summary: five prior findings are fixed. The performance finding is partially fixed, but the new AlwaysOn path adds a per-frame grid scan/log on ordinary unconfigured grids. Two new runtime correctness issues remain: master disarm does not stop manual-override torque, and hydrogen demand is disconnected from some accepted RCS torque allocations.

Prior-finding disposition

  1. Force-residual gate — fixed. Attitude and rotational-dampening allocations now require torque alignment and a translation residual at or below 2% before torque is applied (Plugin.cs lines 402–410, 1325–1342).
  2. Hydrogen override restoration — fixed. Original percentages are tracked and restored on disarm, grid switch/loss, and unload (Plugin.cs lines 678–717).
  3. Hot-path performance — partially fixed. Actuator membership, solver results, and pose candidates are now cached and solve cadence is bounded; finding 3 below covers the remaining regression.
  4. Diagnostic persistence — fixed. Logging is buffered, stored under MyFileSystem.UserDataPath, and rotated at 1 MiB (Plugin.cs lines 2777–2847).
  5. Unused NuGet dependencies — fixed. Harmony and Mono.Cecil were removed.
  6. Asset licensing/provenance — fixed. The root MIT license names KLoverTony and ASSET_PROVENANCE.md covers the companion assets.

Rechecked gates affected by the delta

The exact hub head passes python3 test.py Plugins. Pulsar RoslynCompiler at 8aa6a4be6adf0d5975637cb63356b48706ebb623 compiles the three selected files with TRACE, NETFRAMEWORK, and PULSAR with zero diagnostics; the pinned net10.0 project build succeeds with 0 errors and only the absent optional deployment-folder warning.

The changed project file removes both package references. Its 48 game Bin64 references remain Private=False and resolve locally; DirectShowLib and the two EmptyKeys references remain unused by selected plugin source and outside the PluginHub loader closure. The six-file delta adds no executable/native binary or hub dependency. Its new sensitive surfaces are limited to the reviewed user-data log, fixed-member subpart reflection, Custom Data read, synchronized hydrogen override, angular-damping changes, and direct local torque; no network, process-launch, credential, dynamic-code, or concrete server-authority-bypass path was added.

Findings

1. High: master disarm still applies manual-override torque

/acs disarm changes attitude and inertia state but sets no master-off state (Plugin.cs lines 260–266). Update() continues calling UpdateManualOverrideTorque for the controlled grid and every unattended registered grid (lines 185–197, 1387–1419); that method has no arm/master guard and still reaches ApplyTorque (lines 1521–1562).

This contradicts the documented master enable/disable behavior and leaves physics mutation active after an explicit off command. Add a distinct master-enabled state and gate every ACS physics or block-property write with it, including controlled-grid and AlwaysOn manual-override processing. Keep the separate attitude and inertia settings beneath that master state.

2. High: hydrogen consumption follows visuals rather than applied RCS torque

Without pilot rotation input, the visual path clears rcsVisualAllocatedForces, and hydrogen demand is then derived only from that dictionary (Plugin.cs lines 599–602, 678–695). Rotational dampening can subsequently apply an independent RCS allocation (lines 1301–1342); manual-override counter-control can do likewise (lines 1564–1604). Conversely, visual demand can be written before the live controller rejects that plan on residual/alignment grounds.

Hydrogen pods can therefore provide dampening/counter-torque at zero proportional fuel demand, or consume fuel for withheld torque. Drive demand from the accepted RCS allocations actually applied, aggregate simultaneous paths once per frame, and clear it only when no accepted hydrogen allocation remains. Otherwise exclude hydrogen authority from paths that cannot account for its demand.

3. Medium: ordinary grids incur a full-grid AlwaysOn scan and duplicate log every frame

Every update calls RegisterAlwaysOnGridIfConfigured; until registration succeeds, it runs TryGetAlwaysOnCockpit and logs every failure (Plugin.cs lines 185–192, 1365–1374). The check allocates a cockpit list and scans every block before the normal unconfigured case fails (lines 1472–1516).

Cache or throttle failed registration checks to the slow-validation cadence, and log only when the result changes. Parse the documented [RealisticACS]/AlwaysOn setting as an exact key while doing so; the current substring check also accepts comments, longer keys, and unrelated sections.

@KLoverTony

KLoverTony commented Sep 14, 2026 via email

Copy link
Copy Markdown
Contributor Author

@OwendB1

OwendB1 commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Merging this first version. Thanks!

@OwendB1
OwendB1 merged commit 2e1fce9 into StarCpt:main Sep 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants