Skip to content

1.0.16 - #18

Merged
viktor-ferenczi merged 4 commits into
mainfrom
1.0.16
Jul 31, 2026
Merged

1.0.16#18
viktor-ferenczi merged 4 commits into
mainfrom
1.0.16

Conversation

@viktor-ferenczi

Copy link
Copy Markdown
Contributor
  • Fixed DotNetCompat assembly discovery in the mod rewriter registration (client and server): the previous lookup matched on assembly name (DotNetCompat / DotNetCompat_*), which missed GitHub-sourced installs where Pulsar names the assembly dotnet_compat_<random>. The scan now identifies DotNetCompat by the presence of the CompilerHookExtensions extension type (excluding our own assembly, which shares the root namespace), making it independent of the install path and of Pulsar's naming scheme.
  • Rewriter registration failures are now fatal: a missing or incompatible extension point, or a failed shim-reference plumb, throws instead of logging and continuing. Without the rewriter, mods compile but apply Windows path semantics to native paths and break in ways that are near impossible to trace back — refusing to start is safer than a subtly broken game/server. Errors are logged (also to MyLog on the server) before rethrowing, since the host does not reliably surface init/preloader exceptions.
  • Corrected the load-order documentation in the rewriter registration and server preloader comments: DotNetCompat's assembly presence is guaranteed by the loader (core plugin, all assemblies loaded up front), not by plugin/hook order, and its init/preloader may run after ours — the registration deliberately depends only on assembly presence.
  • Bumped version to 1.0.16.

… regardless of it was built from downloaded sources or from a local dev folder
A silently skipped rewriter is not a graceful degradation: mods compile
fine but then get Windows path semantics applied to native paths and
fail much later, with nothing pointing back at this registration. Refuse
to start instead.

- Every log-and-return in RewriterRegistration.Register now throws
  InvalidOperationException, and the outer catch logs and rethrows
  instead of swallowing (mirroring Preloader's PatchCategory handler).
  Logging before rethrow is deliberate: the host does not reliably
  surface plugin-init/preloader exceptions, so that line is the only
  clue the user gets.

- Dropped the DotNetCompat assembly-name filter. Enumerating Pulsar's
  naming schemes ("DotNetCompat", "DotNetCompat_<random>",
  "dotnet_compat_<random>") needs a new prefix every time Pulsar grows
  an install path, which matters more now that a miss is fatal. Match on
  the CompilerHookExtensions type instead — the only identity we care
  about, stable across every install path — excluding our own assembly
  since LinuxCompat shares DotNetCompat's root namespace per side.

- Removed dead code: the old "incompatible version?" branch was
  unreachable (asm != null implied extType != null), so a version
  mismatch was reported as "assembly not loaded". The not-found message
  now lists every loaded assembly name, and the success line names the
  matched assembly.

- PlumbRewriterShimReferences also hard-fails. Swallowing there left the
  rewriter registered but non-functional, so every rewritten mod failed
  to compile with a "namespace could not be found" error pointing
  nowhere near the cause.
Both claims were wrong, and both were harmless only by accident.

- "DotNetCompat is always loaded earlier than LinuxCompat (Pulsar loads
  it first)" conflated assembly loading with Init. Assembly loading is
  guaranteed — the Loader constructor Assembly.Loads every enabled
  plugin before the Preloader exists or any Init runs, and
  se-dotnet-compat is a force-enabled core plugin the launcher
  Environment.Exit(1)s without. Init order is the opposite of the claim:
  PluginLoader.Init iterates the plugin list in reverse and the core
  plugins sit at its head, so LinuxCompat.Init runs first.

- The server preloader claimed DotNetCompat's Finish "runs before ours
  (Pulsar profile order)". postHooks is a HashSet<MethodInfo> iterated
  with foreach, so that order is unspecified.

Neither matters: RewriterFactories is a static field initializer, so
reading the field constructs the list, and DotNetCompat's patched
CreateCompilation re-reads it per call. Documented that reasoning
explicitly, with a note not to invert the dependency.

@mkaito mkaito left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified working on my repro world.

@SpaceGT SpaceGT left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If registration fails, Pulsar will Dispose the plugin and this will cause a hang when it tries to focus the (compat-managed) SDL window.
This is technically a regression since the previous version would allow launching but without mod support.

This seems like a niche case, however, and I plan to move the rewriter into Pulsar at some point so it's fine for now.

@viktor-ferenczi
viktor-ferenczi merged commit 0d7204b into main Jul 31, 2026
@viktor-ferenczi
viktor-ferenczi deleted the 1.0.16 branch July 31, 2026 14:53
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.

3 participants