clientmod: build without a local Rec Room install

The mod resolves every game-side type at runtime via
AccessTools.TypeByName, so the IL2CPP interop assemblies
(Assembly-CSharp, Photon3Unity3D, UnityEngine.*) in the csproj were
dead references. Dropped them entirely — only MelonLoader's own
DLLs are actually needed at compile time.

tools/build-patcher-zip.ps1 now downloads MelonLoader BEFORE the
dotnet build and points MelonLoaderDir at the extracted contents,
so a fresh contributor checkout builds the patcher zip cold with
no Rec Room install required. (Previously the csproj defaulted
RecRoomDir to a hardcoded user path and failed for anyone else.)

Verified by deleting bin/obj and running the script from scratch
on a machine without RecRoomDir set — produces a 20 MB zip with
the same manifest/DLL/MelonLoader.zip layout as before.
This commit is contained in:
Alexa
2026-05-27 11:47:25 +03:00
parent d471b418b9
commit 1f407ef7b3
2 changed files with 48 additions and 51 deletions
+13 -37
View File
@@ -12,21 +12,22 @@
</PropertyGroup> </PropertyGroup>
<!-- <!--
MelonLoader 0.6.x build of the DorkNet client patches. This is the MelonLoader 0.6.x build of the DorkNet client patches. Resolves
MelonLoader-targeted twin of DorkNet.ClientPatch (which is a BepInEx every game-side type at runtime via AccessTools.TypeByName, so
plugin). Both apply the same Harmony patches needed for the watch Rec Room's IL2CPP-derived interop assemblies (Assembly-CSharp,
to point at a DorkNet server — Photon AppId override, .rec.net → Photon3Unity3D, UnityEngine.*) are NOT needed at compile time —
custom-host URI rewrite, Photon CustomAuth identity injection, TLS they would be derivative works of the game anyway, so we never
trust bypass, Photon region shortcut. reference them. Only MelonLoader's own DLLs are needed.
Set MelonLoaderDir to <RecRoomInstall>\MelonLoader. Override on the tools/build-patcher-zip.ps1 downloads the MelonLoader release zip
command line: and points MelonLoaderDir at its extracted contents, so a fresh
dotnet build -p:MelonLoaderDir=C:\... -p:Il2CppAssembliesDir=C:\... contributor checkout builds without a local Rec Room install.
To build outside the script (manual dev loop):
dotnet build -p:MelonLoaderDir=<path-to-extracted-MelonLoader>
--> -->
<PropertyGroup> <PropertyGroup>
<RecRoomDir Condition="'$(RecRoomDir)' == ''">C:\Users\you\Documents\Recnet\dist\RecRoom-Clean-2020.03.10</RecRoomDir> <MelonLoaderDir Condition="'$(MelonLoaderDir)' == ''">$(MSBuildThisFileDirectory)..\build\melonloader</MelonLoaderDir>
<MelonLoaderDir Condition="'$(MelonLoaderDir)' == ''">$(RecRoomDir)\MelonLoader</MelonLoaderDir>
<Il2CppAssembliesDir Condition="'$(Il2CppAssembliesDir)' == ''">$(MelonLoaderDir)\Il2CppAssemblies</Il2CppAssembliesDir>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -46,31 +47,6 @@
<HintPath>$(MelonLoaderDir)\net6\Il2CppInterop.Common.dll</HintPath> <HintPath>$(MelonLoaderDir)\net6\Il2CppInterop.Common.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Il2Cppmscorlib">
<HintPath>$(Il2CppAssembliesDir)\Il2Cppmscorlib.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Il2CppSystem">
<HintPath>$(Il2CppAssembliesDir)\Il2CppSystem.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>$(Il2CppAssembliesDir)\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>$(Il2CppAssembliesDir)\Assembly-CSharp-firstpass.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Photon3Unity3D">
<HintPath>$(Il2CppAssembliesDir)\Photon3Unity3D.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(Il2CppAssembliesDir)\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>
+35 -14
View File
@@ -16,6 +16,11 @@
user's Rec Room install root, copies the DLL into Mods/, then user's Rec Room install root, copies the DLL into Mods/, then
renders the config template into UserData/. renders the config template into UserData/.
Self-contained: downloads MelonLoader before the dotnet build and
feeds its DLLs into the build as references, so no local Rec Room
install is required to compile DorkNet.ClientMod. (The mod resolves
every game-side type at runtime via AccessTools.TypeByName.)
.PARAMETER VersionKey .PARAMETER VersionKey
The version key matching versions.json on the public main branch The version key matching versions.json on the public main branch
(e.g. december_2020_12_18 or march_2020_03_10). (e.g. december_2020_12_18 or march_2020_03_10).
@@ -45,10 +50,35 @@ New-Item -ItemType Directory -Force $OutputDir | Out-Null
$stage = Join-Path $env:TEMP "dorknet-patcher-$VersionKey-$([guid]::NewGuid().Guid.Substring(0,8))" $stage = Join-Path $env:TEMP "dorknet-patcher-$VersionKey-$([guid]::NewGuid().Guid.Substring(0,8))"
New-Item -ItemType Directory -Force $stage | Out-Null New-Item -ItemType Directory -Force $stage | Out-Null
try { try {
# ── 1. Build DorkNet.ClientMod ─────────────────────────────────────── # ── 1. Fetch MelonLoader from upstream releases ──────────────────────
# We download it first so the DLLs inside can serve as compile-time
# references for DorkNet.ClientMod — keeps the build self-contained
# (no local Rec Room install required to compile the mod).
# x64 .NET 6 build targets 2020-era Rec Room. The asset name pattern
# is stable across 0.6.x: MelonLoader.x64.zip.
$mlAsset = "MelonLoader.x64.zip"
$mlUrl = "https://github.com/LavaGang/MelonLoader/releases/download/$MelonLoaderVersion/$mlAsset"
$mlZip = "$stage\MelonLoader.zip"
$mlExtract = "$stage\melonloader-extracted"
Write-Host "[fetch] downloading $mlUrl"
Invoke-WebRequest -Uri $mlUrl -OutFile $mlZip
if (-not (Test-Path $mlZip) -or (Get-Item $mlZip).Length -lt 1MB) {
throw "MelonLoader download failed or produced an unexpectedly small file"
}
Expand-Archive -Path $mlZip -DestinationPath $mlExtract -Force
# The 0.6.x release zip layout has a top-level `MelonLoader` folder
# containing net6, net35, Dependencies, Documentation. Point the
# csproj at that inner folder.
$mlRefRoot = Join-Path $mlExtract 'MelonLoader'
if (-not (Test-Path "$mlRefRoot\net6\MelonLoader.dll")) {
throw "Extracted MelonLoader is missing net6\MelonLoader.dll — release layout may have changed"
}
# ── 2. Build DorkNet.ClientMod (no Rec Room install needed) ─────────
Write-Host "[build] dotnet build DorkNet.ClientMod (Release)" Write-Host "[build] dotnet build DorkNet.ClientMod (Release)"
dotnet build "$repoRoot\DorkNet.ClientMod\DorkNet.ClientMod.csproj" ` dotnet build "$repoRoot\DorkNet.ClientMod\DorkNet.ClientMod.csproj" `
-c Release --nologo | Out-Null -c Release --nologo `
"-p:MelonLoaderDir=$mlRefRoot" | Out-Null
if ($LASTEXITCODE -ne 0) { throw "ClientMod build failed" } if ($LASTEXITCODE -ne 0) { throw "ClientMod build failed" }
$dll = Get-ChildItem "$repoRoot\DorkNet.ClientMod\bin\Release" -Recurse ` $dll = Get-ChildItem "$repoRoot\DorkNet.ClientMod\bin\Release" -Recurse `
-Filter "DorkNet.ClientMod.dll" | -Filter "DorkNet.ClientMod.dll" |
@@ -58,18 +88,6 @@ try {
Copy-Item $dll.FullName "$stage\DorkNet.ClientMod.dll" Copy-Item $dll.FullName "$stage\DorkNet.ClientMod.dll"
Write-Host "[build] copied $($dll.FullName)" Write-Host "[build] copied $($dll.FullName)"
# ── 2. Fetch MelonLoader from upstream releases ──────────────────────
# The x64 .NET 6 build is what targets 2020-era Rec Room. The asset
# name pattern is stable across 0.6.x: MelonLoader.x64.zip.
$mlAsset = "MelonLoader.x64.zip"
$mlUrl = "https://github.com/LavaGang/MelonLoader/releases/download/$MelonLoaderVersion/$mlAsset"
$mlOut = "$stage\MelonLoader.zip"
Write-Host "[build] downloading $mlUrl"
Invoke-WebRequest -Uri $mlUrl -OutFile $mlOut
if (-not (Test-Path $mlOut) -or (Get-Item $mlOut).Length -lt 1MB) {
throw "MelonLoader download failed or produced an unexpectedly small file"
}
# ── 3. Copy the config template ────────────────────────────────────── # ── 3. Copy the config template ──────────────────────────────────────
$template = "$repoRoot\DorkNet.ClientMod\dorknet-clientmod.json.template" $template = "$repoRoot\DorkNet.ClientMod\dorknet-clientmod.json.template"
if (-not (Test-Path $template)) { if (-not (Test-Path $template)) {
@@ -92,6 +110,9 @@ try {
Set-Content -LiteralPath "$stage\manifest.json" -Value $manifest Set-Content -LiteralPath "$stage\manifest.json" -Value $manifest
# ── 5. Bundle into the final zip ──────────────────────────────────── # ── 5. Bundle into the final zip ────────────────────────────────────
# Strip the extracted MelonLoader scratch dir before zipping so it
# doesn't bloat the artifact. The MelonLoader.zip itself stays.
Remove-Item -Recurse -Force $mlExtract
$outZip = Join-Path $OutputDir "dorknet-clientpatch-$VersionKey.zip" $outZip = Join-Path $OutputDir "dorknet-clientpatch-$VersionKey.zip"
if (Test-Path $outZip) { Remove-Item $outZip -Force } if (Test-Path $outZip) { Remove-Item $outZip -Force }
Compress-Archive -Path "$stage\*" -DestinationPath $outZip Compress-Archive -Path "$stage\*" -DestinationPath $outZip