The Publicizer
Much of what a mod needs is private or internal — GameSessionOrchestrator.Draw,
MapDrawer.SubDrawers, half the fields on the HUD parts. The sample projects solve this
with Krafs.Publicizer, which rewrites the
reference assemblies at build time so every member appears public to your compiler.
Setup
<PropertyGroup>
<PublicizeAll>true</PublicizeAll>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Publicizer" Version="2.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
PublicizeAll publicizes every referenced assembly. You can instead opt in per
assembly with <Publicize Include="SPZGameAssembly" />.
Useful extras the samples set:
<PropertyGroup>
<PublicizerClearCacheOnClean>true</PublicizerClearCacheOnClean>
<PublicizerLogFilePath>logs/krafs.log</PublicizerLogFilePath>
</PropertyGroup>
The log is worth keeping — it is how you confirm publicization actually ran.
What it does and does not do
It changes compile-time visibility only. At runtime the members are still private,
and the CLR would normally refuse the access. That works because the publicizer also
emits IgnoresAccessChecksToAttribute for the target assemblies, which tells the
runtime to skip the check for your assembly.
Two consequences:
- Access to a private member is a normal, fast field or method access — no reflection cost.
- If publicization silently fails, you get a
MethodAccessExceptionorFieldAccessExceptionat runtime rather than a compile error.
DoNotPublicize
The sample .csproj files exclude a handful of types:
<ItemGroup>
<DoNotPublicize Include="SPZGameAssembly:BeltLaneRenderingDefinition"
IncludeCompilerGeneratedMembers="false" IncludeVirtualMembers="false"/>
<DoNotPublicize Include="Game.Core.Coordinates"
IncludeCompilerGeneratedMembers="false" IncludeVirtualMembers="false"/>
<DoNotPublicize Include="SPZGameAssembly:BuildingDefinition" … />
<DoNotPublicize Include="Game.Orchestration:ParentToolbarElementData" … />
<DoNotPublicize Include="Game.Orchestration:RootToolbarElementData" … />
</ItemGroup>
The reason is that publicizing everything can break code that was compiling fine.
Making a private virtual member public changes overload resolution and override
requirements; making the whole of Game.Core.Coordinates public exposes internal
constructors that then compete with the intended factory methods. When you hit a
mysterious compile error in code you did not change, adding the offending type to
DoNotPublicize is usually the fix.
The syntax is Assembly for a whole assembly or Assembly:TypeName for one type.
When it bites
- A build warning that nothing was publicized —
"Assembly is marked for publicization, but no members were publicized"— means your references are not resolving. CheckSPZ2_PATH. Privatematters. Game references must be<Private>False</Private>; copying a publicized game assembly into your mod folder would shadow the real one.- Private members are not API. Anything you reach this way can be renamed or deleted by an update without notice. The same discipline as hooking applies: use them, but keep the uses few and in one place.