2026-07-10 03:34:49 +01:00
param (
[ string ] $Configuration = " Release " ,
2026-07-14 01:35:18 +01:00
[ string ] $OutputDirectory = " artifacts\unity\org.gamecult.cultlib " ,
2026-08-18 17:39:59 +02:00
[ string ] $NuGetConfig = " " ,
2026-08-18 23:44:51 +02:00
[ switch ] $NoRestore ,
2026-07-14 01:35:18 +01:00
[ switch ] $UpdateTemplate
2026-07-10 03:34:49 +01:00
)
$ErrorActionPreference = " Stop "
$repoRoot = Split-Path -Parent $PSScriptRoot
$projectPath = Join-Path $repoRoot " src\GameCult.Mesh\GameCult.Mesh.csproj "
2026-08-18 20:09:44 +02:00
$webSocketProjectPath = Join-Path $repoRoot " src\GameCult.Networking.WebSockets\GameCult.Networking.WebSockets.csproj "
2026-07-19 22:55:06 +01:00
$quicProjectPath = Join-Path $repoRoot " src\GameCult.Mesh.Quic.Native\GameCult.Mesh.Quic.Native.csproj "
2026-07-10 03:34:49 +01:00
$templateRoot = Join-Path $repoRoot " unity\org.gamecult.cultlib "
$publishRoot = Join-Path $repoRoot " artifacts\unity-publish "
2026-08-18 20:09:44 +02:00
$webSocketPublishRoot = Join-Path $repoRoot " artifacts\unity-websocket-publish "
2026-07-19 22:55:06 +01:00
$quicPublishRoot = Join-Path $repoRoot " artifacts\unity-quic-publish "
$quicNativeRoot = Join-Path $repoRoot " artifacts\unity-quic-native "
2026-07-10 03:34:49 +01:00
$outputRoot = if ( [ System.IO.Path ] :: IsPathRooted ( $OutputDirectory ) ) {
$OutputDirectory
} else {
Join-Path $repoRoot $OutputDirectory
}
$pluginRoot = Join-Path $outputRoot " Runtime\Plugins "
if ( Test-Path -LiteralPath $publishRoot ) {
Remove-Item -LiteralPath $publishRoot -Recurse -Force
}
2026-07-19 22:55:06 +01:00
if ( Test-Path -LiteralPath $quicPublishRoot ) {
Remove-Item -LiteralPath $quicPublishRoot -Recurse -Force
}
2026-08-18 20:09:44 +02:00
if ( Test-Path -LiteralPath $webSocketPublishRoot ) {
Remove-Item -LiteralPath $webSocketPublishRoot -Recurse -Force
}
2026-07-10 03:34:49 +01:00
if ( Test-Path -LiteralPath $outputRoot ) {
Remove-Item -LiteralPath $outputRoot -Recurse -Force
}
2026-08-18 20:09:44 +02:00
$unityPackageVersion = ( Get-Content -LiteralPath ( Join-Path $templateRoot " package.json " ) -Raw | ConvertFrom-Json ) . version
Write and enforce the semver policy, per operator ruling on 1.0.60
1.0.60 removed public API (CultInspectorAssetPath) under a patch bump; its
own changelog says "Breaking" and the version number lied. Operator ruling:
semver is policy, strictly, for every published package family.
docs/semver-policy.md states what counts as breaking per surface (managed
API, Unity inspector attributes, CultCache wire format/store compatibility,
CultNet protocol, native bridge exports), the 0.y.z pre-1.0 convention, tag
naming, and required changelog shape. Records the 1.0.60 scar as history;
does not renumber it.
scripts/check-changelog-semver.mjs is the mechanical check: refuses a
changelog "### Breaking" section under too small a bump, and refuses a
version that skips, reverses, or repeats the previous published tag. Skips
re-litigating a version that is already tagged (rebuilding immutable
history is not a release action). Wired into publish-packages.yml for the
four npm/PyPI packages, and into build-unity-package.ps1 (cultlib),
packages/cultmath/scripts/build-unity-package.ps1 (cultmath), and a new
scripts/verify-caching-unity-release.ps1 (caching.unity has no build script
of its own to hook into).
18 node:test cases cover the required table (breaking+major/minor/patch,
non-breaking, 0.y.z, skip, downgrade) plus CLI-level git-tag resolution.
Hand-mutated each rule (bump classification, pre-1.0 lane requirement,
breaking-heading detection, breaking early-return, already-published skip)
and confirmed each kill, then restored byte-exact. No mutation harness is
committed; CultLib has none today.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:03:22 +02:00
# docs/semver-policy.md: refuse to build a release whose CHANGELOG.md entry
# claims a breaking change under too small a bump, or whose version skips or
# reverses the previous published tag. Runs before the (slow) build below so
# a bad version number fails fast.
& node ( Join-Path $repoRoot " scripts\check-changelog-semver.mjs " ) `
- -package " org.gamecult.cultlib " `
- -changelog ( Join-Path $templateRoot " CHANGELOG.md " ) `
- -version $unityPackageVersion `
- -tag -prefix " cultlib-unity " `
- -cwd $repoRoot
if ( $LASTEXITCODE -ne 0 ) {
throw " CultLib Unity package failed the semver policy check (see docs/semver-policy.md). "
}
2026-09-29 17:20:17 +02:00
# Release gate: cultlib's Unity package resolves CultMath through the org.gamecult.cultmath
# dependency, so a cultlib release must not run ahead of the CultMath package it names.
# (1) The declared dependency version must not exceed the CultMath package's own version.
# (2) The CultMath.dll that package tracks must define every type CultMathResolver formats.
2026-09-29 18:31:16 +02:00
# The names come from the resolver source (its Shape<T> entries); scripts\check-cultmath-types.cs
# looks them up in the assembly's type-definition table, which Windows PowerShell cannot load
# the assembly to do. It checks type names, not signatures.
2026-09-29 17:20:17 +02:00
$cultMathPackageRoot = Join-Path $repoRoot " packages\cultmath\unity\org.gamecult.cultmath "
$cultMathDeclared = ( Get-Content -LiteralPath ( Join-Path $templateRoot " package.json " ) -Raw | ConvertFrom-Json ) . dependencies . 'org.gamecult.cultmath'
$cultMathAvailable = ( Get-Content -LiteralPath ( Join-Path $cultMathPackageRoot " package.json " ) -Raw | ConvertFrom-Json ) . version
if ( [ string ] :: IsNullOrWhiteSpace ( $cultMathDeclared ) ) {
throw " package.json must declare org.gamecult.cultmath, which supplies CultMath.dll. "
}
2026-09-29 18:31:16 +02:00
foreach ( $declared in @ ( @ ( " org.gamecult.cultlib's org.gamecult.cultmath dependency " , $cultMathDeclared ) , @ ( " the CultMath package version " , $cultMathAvailable ) ) ) {
if ( $declared [ 1 ] -notmatch '^\d+\.\d+\.\d+$' ) {
throw " $( $declared [ 0 ] ) is ' $( $declared [ 1 ] ) '. The release gate compares exact release versions (major.minor.patch); prerelease and range versions are not supported. "
}
}
2026-09-29 17:20:17 +02:00
if ( [ version ] $cultMathDeclared -gt [ version ] $cultMathAvailable ) {
throw " Release order: org.gamecult.cultlib depends on org.gamecult.cultmath $cultMathDeclared , but the CultMath package is at $cultMathAvailable . Release CultMath first. "
}
$resolverSource = Get-Content -LiteralPath ( Join-Path $repoRoot " src\GameCult.Caching.MessagePack\CultMathResolver.cs " ) -Raw
$resolverTypes = @ ( [ regex ] :: Matches ( $resolverSource , '\bShape<(?!T>)(\w+)>\(' ) | ForEach-Object { $_ . Groups [ 1 ] . Value } | Sort-Object -Unique )
if ( $resolverTypes . Count -eq 0 ) {
throw " Found no CultMath types in CultMathResolver.cs; the release gate cannot check the CultMath package. "
}
2026-09-29 18:31:16 +02:00
$missingTypes = & dotnet run ( Join-Path $PSScriptRoot " check-cultmath-types.cs " ) - - ( Join-Path $cultMathPackageRoot " Runtime\Plugins\CultMath.dll " ) @resolverTypes
if ( $LASTEXITCODE -eq 1 ) {
throw " The CultMath.dll in org.gamecult.cultmath $cultMathAvailable lacks types CultMathResolver formats: $missingTypes . Release CultMath first. "
}
if ( $LASTEXITCODE -ne 0 ) {
throw " scripts\check-cultmath-types.cs failed with exit code $LASTEXITCODE . "
2026-09-29 17:20:17 +02:00
}
2026-09-14 15:18:42 +02:00
# The tracked DLLs and pdbs are committed beside their source, so none may name a commit or a worktree:
# Source Link writes the commit SHA into the pdb, the informational version carries it too, and each DLL
# carries its pdb's content id. ContinuousIntegrationBuild maps the local source path to /_/.
# dotnet publish has no --no-incremental, so clear the configuration's intermediates instead: a stale obj
# directory must not decide what the byte check compares.
Get-ChildItem -LiteralPath ( Join-Path $repoRoot " obj\src " ) -Directory -ErrorAction SilentlyContinue |
ForEach-Object { Join-Path $_ . FullName $Configuration } |
Where-Object { Test-Path -LiteralPath $_ } |
ForEach-Object { Remove-Item -LiteralPath $_ -Recurse -Force }
$deterministicArguments = @ ( " --disable-build-servers " , " -p:UseSharedCompilation=false " ,
" -p:ContinuousIntegrationBuild=true " , " -p:EnableSourceLink=false " ,
" -p:IncludeSourceRevisionInInformationalVersion=false " , " -m:1 " )
$publishArguments = @ ( " publish " , $projectPath , " -c " , $Configuration , " -o " , $publishRoot , " -p:CultLibPackageVersion= $unityPackageVersion " ) + $deterministicArguments
2026-08-18 23:44:51 +02:00
if ( $NoRestore ) { $publishArguments + = " --no-restore " }
2026-08-18 17:39:59 +02:00
if ( -not [ string ] :: IsNullOrWhiteSpace ( $NuGetConfig ) ) {
$publishArguments + = " -p:RestoreConfigFile= $( [ IO.Path ] :: GetFullPath ( $NuGetConfig ) ) "
}
& dotnet @publishArguments
2026-07-10 03:34:49 +01:00
if ( $LASTEXITCODE -ne 0 ) {
throw " CultLib publish failed with exit code $LASTEXITCODE "
}
2026-09-14 15:18:42 +02:00
$webSocketPublishArguments = @ ( " publish " , $webSocketProjectPath , " -c " , $Configuration , " -f " , " netstandard2.1 " , " -o " , $webSocketPublishRoot , " -p:CultLibPackageVersion= $unityPackageVersion " ) + $deterministicArguments
2026-08-18 23:44:51 +02:00
if ( $NoRestore ) { $webSocketPublishArguments + = " --no-restore " }
2026-08-18 20:09:44 +02:00
if ( -not [ string ] :: IsNullOrWhiteSpace ( $NuGetConfig ) ) {
$webSocketPublishArguments + = " -p:RestoreConfigFile= $( [ IO.Path ] :: GetFullPath ( $NuGetConfig ) ) "
}
& dotnet @webSocketPublishArguments
if ( $LASTEXITCODE -ne 0 ) {
throw " CultLib WebSocket publish failed with exit code $LASTEXITCODE "
}
2026-09-14 15:18:42 +02:00
$quicPublishArguments = @ ( " publish " , $quicProjectPath , " -c " , $Configuration , " -o " , $quicPublishRoot , " -p:CultLibPackageVersion= $unityPackageVersion " ) + $deterministicArguments
2026-08-18 23:44:51 +02:00
if ( $NoRestore ) { $quicPublishArguments + = " --no-restore " }
2026-08-18 17:39:59 +02:00
if ( -not [ string ] :: IsNullOrWhiteSpace ( $NuGetConfig ) ) {
$quicPublishArguments + = " -p:RestoreConfigFile= $( [ IO.Path ] :: GetFullPath ( $NuGetConfig ) ) "
}
& dotnet @quicPublishArguments
2026-07-19 22:55:06 +01:00
if ( $LASTEXITCODE -ne 0 ) {
throw " CultLib native QUIC managed publish failed with exit code $LASTEXITCODE "
}
& ( Join-Path $PSScriptRoot " build-quic-native.ps1 " ) `
-Configuration $Configuration `
-OutputDirectory $quicNativeRoot
2026-07-10 03:34:49 +01:00
New-Item -ItemType Directory -Force -Path $pluginRoot | Out-Null
Copy-Item -LiteralPath ( Join-Path $templateRoot " package.json " ) -Destination $outputRoot
Copy-Item -LiteralPath ( Join-Path $templateRoot " README.md " ) -Destination $outputRoot
2026-09-17 20:40:12 +02:00
Copy-Item -LiteralPath ( Join-Path $templateRoot " CHANGELOG.md " ) -Destination $outputRoot
2026-07-10 03:34:49 +01:00
$expectedAssemblies = @ (
" ConcurrentCollections.dll " ,
" GameCult.Caching.dll " ,
" GameCult.Caching.MessagePack.dll " ,
" GameCult.Logging.dll " ,
" GameCult.Mesh.dll " ,
2026-07-19 22:55:06 +01:00
" GameCult.Mesh.Quic.Native.dll " ,
2026-07-10 03:34:49 +01:00
" GameCult.Networking.dll " ,
2026-08-18 20:09:44 +02:00
" GameCult.Networking.WebSockets.dll " ,
2026-07-10 03:34:49 +01:00
" Isopoh.Cryptography.Argon2.dll " ,
" Isopoh.Cryptography.Blake2b.dll " ,
" Isopoh.Cryptography.SecureArray.dll " ,
" LiteNetLib.dll " ,
" MessagePack.Annotations.dll " ,
" MessagePack.dll " ,
" Microsoft.Bcl.AsyncInterfaces.dll " ,
" Microsoft.Bcl.TimeProvider.dll " ,
" Microsoft.NET.StringTools.dll " ,
" R3.dll " ,
" System.Collections.Immutable.dll " ,
" System.ComponentModel.Annotations.dll " ,
" System.IO.Pipelines.dll " ,
" System.Runtime.CompilerServices.Unsafe.dll " ,
" System.Text.Encodings.Web.dll " ,
" System.Text.Json.dll " ,
" System.Threading.Channels.dll "
)
2026-09-29 16:52:44 +02:00
# Assemblies the package references but never ships: org.gamecult.cultmath owns CultMath.dll, and the
# package.json dependency delivers it. A second copy in Runtime\Plugins would be a duplicate assembly.
$externalAssemblies = @ ( " CultMath.dll " )
$asmdef = Get-Content -LiteralPath ( Join-Path $templateRoot " Runtime\GameCult.CultLib.asmdef " ) -Raw | ConvertFrom-Json
$referencedAssemblies = @ ( $asmdef . precompiledReferences | Sort-Object )
$declaredAssemblies = @ ( $expectedAssemblies + $externalAssemblies | Sort-Object )
if ( ( $referencedAssemblies -join " | " ) -ne ( $declaredAssemblies -join " | " ) ) {
throw " GameCult.CultLib.asmdef precompiledReferences differ from the shipped plus external assembly lists. "
}
2026-07-10 03:34:49 +01:00
$publishedByName = @ { }
foreach ( $assembly in Get-ChildItem -LiteralPath $publishRoot -Filter " *.dll " ) {
$publishedByName [ $assembly . Name ] = $assembly
}
2026-07-19 22:55:06 +01:00
foreach ( $assembly in Get-ChildItem -LiteralPath $quicPublishRoot -Filter " *.dll " ) {
$publishedByName [ $assembly . Name ] = $assembly
}
2026-08-18 20:09:44 +02:00
foreach ( $assembly in Get-ChildItem -LiteralPath $webSocketPublishRoot -Filter " *.dll " ) {
$publishedByName [ $assembly . Name ] = $assembly
}
2026-07-10 03:34:49 +01:00
foreach ( $assemblyName in $expectedAssemblies ) {
if ( -not $publishedByName . ContainsKey ( $assemblyName ) ) {
throw " CultLib Unity package is missing expected assembly: $assemblyName "
}
}
Copy-Item -LiteralPath ( Join-Path $templateRoot " Runtime\GameCult.CultLib.asmdef " ) `
-Destination ( Join-Path $outputRoot " Runtime " )
foreach ( $assemblyName in $expectedAssemblies ) {
$assembly = $publishedByName [ $assemblyName ]
Copy-Item -LiteralPath $assembly . FullName -Destination $pluginRoot
$pdb = [ System.IO.Path ] :: ChangeExtension ( $assembly . FullName , " .pdb " )
if ( Test-Path -LiteralPath $pdb ) {
Copy-Item -LiteralPath $pdb -Destination $pluginRoot
}
}
2026-07-19 22:55:06 +01:00
$nativePluginRoot = Join-Path $pluginRoot " x86_64 "
New-Item -ItemType Directory -Force -Path $nativePluginRoot | Out-Null
Copy-Item -LiteralPath ( Join-Path $quicNativeRoot " gamecult_mesh_quic_native.dll " ) -Destination $nativePluginRoot
Copy-Item -LiteralPath ( Join-Path $quicNativeRoot " msquic.dll " ) -Destination $nativePluginRoot
New-Item -ItemType Directory -Force -Path ( Join-Path $outputRoot " Third Party Notices " ) | Out-Null
Copy-Item -LiteralPath ( Join-Path $quicNativeRoot " MSQUIC-LICENSE.txt " ) `
-Destination ( Join-Path $outputRoot " Third Party Notices\MSQUIC-LICENSE.txt " )
Correct stale Schannel/40,448-byte claims, ship OpenSSL's license notice
Soul flagged (F3) two docs still describing the pre-release native QUIC
bridge: unity/org.gamecult.cultlib/README.md said Schannel, and
native/GameCult.Mesh.Quic.Native/README.md still described the older
40,448-byte committed plugin as current. The shipped bridge (since the
1.0.60/1.4.0 release, commit 0155ba8) is the OpenSSL MsQuic 2.5.9 build,
302,080 bytes, static CRT, no CRT import. Both docs now describe that build
and name what it replaced.
The OpenSSL build's NuGet package carries an Apache-2.0 notice for OpenSSL
itself (Microsoft.Native.Quic.MsQuic.OpenSSL's THIRD-PARTY-NOTICES) that the
committed Third Party Notices folder never carried - the Schannel build had
no such dependency to notice. build-quic-native.ps1 now copies it out
alongside MSQUIC-LICENSE.txt, build-unity-package.ps1 stages it into the
package output and the tracked template, and it is now committed as
OPENSSL-NOTICE.txt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 19:49:14 +02:00
Copy-Item -LiteralPath ( Join-Path $quicNativeRoot " OPENSSL-NOTICE.txt " ) `
-Destination ( Join-Path $outputRoot " Third Party Notices\OPENSSL-NOTICE.txt " )
2026-07-10 03:34:49 +01:00
2026-08-18 20:09:44 +02:00
$meshPackagePath = Join-Path $pluginRoot " GameCult.Mesh.dll "
$meshPackageVersion = [ Reflection.AssemblyName ] :: GetAssemblyName ( $meshPackagePath ) . Version . ToString ( )
if ( $meshPackageVersion -ne " $unityPackageVersion .0 " ) {
throw " Packaged GameCult.Mesh.dll version ' $meshPackageVersion ' does not match package ' $unityPackageVersion '. "
}
$meshPackageMetadata = [ Text.Encoding ] :: UTF8 . GetString ( [ IO.File ] :: ReadAllBytes ( $meshPackagePath ) )
foreach ( $requiredType in @ (
" GameCult.Mesh.CultMeshAuthorityTrustPolicy " ,
" GameCult.Mesh.CultMeshRouteCertificate " ,
" GameCult.Mesh.CultMeshHttpsContentTransportConnector " ) ) {
if ( -not $meshPackageMetadata . Contains ( $requiredType . Split ( '.' ) [ -1 ] ) ) {
throw " Packaged GameCult.Mesh.dll is missing required API ' $requiredType '. "
}
}
$webSocketPackageMetadata = [ Text.Encoding ] :: UTF8 . GetString (
[ IO.File ] :: ReadAllBytes ( ( Join-Path $pluginRoot " GameCult.Networking.WebSockets.dll " ) ) )
if ( -not $webSocketPackageMetadata . Contains ( " CultNetWebSocketSchemaClient " ) ) {
throw " Packaged WebSocket assembly is missing the Unity-compatible CultNet client. "
}
2026-07-14 01:35:18 +01:00
if ( $UpdateTemplate ) {
$templatePluginRoot = Join-Path $templateRoot " Runtime\Plugins "
foreach ( $assemblyName in $expectedAssemblies ) {
Copy-Item -LiteralPath ( Join-Path $pluginRoot $assemblyName ) -Destination $templatePluginRoot -Force
$pdbName = [ System.IO.Path ] :: ChangeExtension ( $assemblyName , " .pdb " )
$pdb = Join-Path $pluginRoot $pdbName
if ( Test-Path -LiteralPath $pdb ) {
Copy-Item -LiteralPath $pdb -Destination $templatePluginRoot -Force
}
}
2026-07-19 22:55:06 +01:00
$templateNativePluginRoot = Join-Path $templatePluginRoot " x86_64 "
New-Item -ItemType Directory -Force -Path $templateNativePluginRoot | Out-Null
Copy-Item -LiteralPath ( Join-Path $nativePluginRoot " gamecult_mesh_quic_native.dll " ) `
-Destination $templateNativePluginRoot -Force
Copy-Item -LiteralPath ( Join-Path $nativePluginRoot " msquic.dll " ) `
-Destination $templateNativePluginRoot -Force
$templateNoticesRoot = Join-Path $templateRoot " Third Party Notices "
New-Item -ItemType Directory -Force -Path $templateNoticesRoot | Out-Null
Copy-Item -LiteralPath ( Join-Path $outputRoot " Third Party Notices\MSQUIC-LICENSE.txt " ) `
-Destination $templateNoticesRoot -Force
Correct stale Schannel/40,448-byte claims, ship OpenSSL's license notice
Soul flagged (F3) two docs still describing the pre-release native QUIC
bridge: unity/org.gamecult.cultlib/README.md said Schannel, and
native/GameCult.Mesh.Quic.Native/README.md still described the older
40,448-byte committed plugin as current. The shipped bridge (since the
1.0.60/1.4.0 release, commit 0155ba8) is the OpenSSL MsQuic 2.5.9 build,
302,080 bytes, static CRT, no CRT import. Both docs now describe that build
and name what it replaced.
The OpenSSL build's NuGet package carries an Apache-2.0 notice for OpenSSL
itself (Microsoft.Native.Quic.MsQuic.OpenSSL's THIRD-PARTY-NOTICES) that the
committed Third Party Notices folder never carried - the Schannel build had
no such dependency to notice. build-quic-native.ps1 now copies it out
alongside MSQUIC-LICENSE.txt, build-unity-package.ps1 stages it into the
package output and the tracked template, and it is now committed as
OPENSSL-NOTICE.txt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 19:49:14 +02:00
Copy-Item -LiteralPath ( Join-Path $outputRoot " Third Party Notices\OPENSSL-NOTICE.txt " ) `
-Destination $templateNoticesRoot -Force
2026-07-14 01:35:18 +01:00
Write-Host " Updated tracked Unity package assemblies: $templatePluginRoot "
}
2026-07-10 03:34:49 +01:00
$manifest = Get-Content -LiteralPath ( Join-Path $outputRoot " package.json " ) -Raw | ConvertFrom-Json
Write-Host " CultLib Unity package: $outputRoot "
Write-Host " Package: $( $manifest . name ) @ $( $manifest . version ) "
Write-Host " Managed assemblies: $( $expectedAssemblies . Count ) "
Pin the Windows MsQuic to the OpenSSL build, and both of its digests
Q9 ruled A. The Schannel MsQuic refuses QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12
with QUIC_STATUS_NOT_SUPPORTED, and PKCS12 is the only credential type a
provider has on both platforms, so a Windows provider could not open a
listener against the runtime this repo was shipping. The same bridge
binary passes all twenty smoke checks and the v1 gate against the OpenSSL
build, which is what Soul verified and what this now downloads.
Two digests, not one. The zip is pinned by FEE9A664 and 19,179,817 bytes;
the msquic.dll inside it by C17C6581 and 4,181,856 bytes. The earlier
report and the cut map's first record put the zip's digest on the DLL,
which is a pin that checks the archive and then ships a file nobody looked
at. Sizes ride along with both, because a digest that fails tells you less
than a digest and a length that fail together.
The CRT is linked statically. A Node or Unity host without the Visual C++
redistributable cannot load a DLL that imports vcruntime140.dll, and
Windows reports that as "the specified module could not be found" while
naming the module that was found. Confirmed on the rebuild: msquic.dll,
CRYPT32, ntdll, KERNEL32, and no CRT import at all. The DLL goes from
83,456 to 302,080 bytes, which is the price of the host not needing
anything installed.
The Unity plugin's committed msquic.dll is still the Schannel one and this
does not touch it; the next build-unity-package.ps1 run is what replaces
it, and that is not in this batch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 22:04:27 +02:00
Write-Host " Native realtime: MsQuic OpenSSL 2.5.9 (Windows x64) "