Skip to content

fix: MelonLoader 0.7.1 Crash Issue #201

fix: MelonLoader 0.7.1 Crash Issue

fix: MelonLoader 0.7.1 Crash Issue #201

name: IL2CPP Build Check
on:
push:
branches: [ master, main, stable, npc-prefabs ]
pull_request:
branches: [ master, main, stable, npc-prefabs ]
workflow_dispatch:
inputs:
assembly_branch:
description: 'Assembly branch to use (main or beta)'
required: false
default: 'main'
type: choice
options:
- main
- beta
jobs:
build-il2cpp:
runs-on: ubuntu-latest
steps:
- name: Checkout S1API
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
# Determine which assembly branch to use (beta or main) - MUST run before cache
- name: Determine Assembly Branch
id: assembly-branch
run: |
# Manual workflow dispatch takes precedence
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ -n "${{ inputs.assembly_branch }}" ]]; then
echo "branch=${{ inputs.assembly_branch }}" >> $GITHUB_OUTPUT
echo "Using ${{ inputs.assembly_branch }} assemblies (manual trigger)"
elif [[ "${{ github.event_name }}" == "pull_request" ]] && [[ "${{ contains(github.event.pull_request.labels.*.name, 'beta') }}" == "true" ]]; then
echo "branch=beta" >> $GITHUB_OUTPUT
echo "Using beta assemblies for PR with beta label"
else
echo "branch=main" >> $GITHUB_OUTPUT
echo "Using main/stable assemblies"
fi
- name: Create Assembly Directory Structure
run: |
mkdir -p S1API/ScheduleOneAssemblies/Il2CppAssemblies
mkdir -p S1API/ScheduleOneAssemblies/MelonLoader
# Try to restore assemblies from cache first (for external PRs)
# Cache key includes assembly branch to separate beta from main
# v4 suffix allows cache invalidation by bumping version
# Only use cache for PR events to avoid stale assemblies after merges
- name: Restore IL2CPP Game Assemblies from Cache
id: cache-il2cpp-assemblies
if: github.event_name == 'pull_request'
uses: actions/cache/restore@v4
with:
path: S1API/ScheduleOneAssemblies
key: il2cpp-game-assemblies-v4-${{ steps.assembly-branch.outputs.branch }}-${{ hashFiles('S1API/S1API.csproj') }}
restore-keys: |
il2cpp-game-assemblies-v4-${{ steps.assembly-branch.outputs.branch }}-
# Only checkout IL2CPP assemblies if cache miss AND we have access to secrets
- name: Checkout IL2CPP Game Assemblies
uses: actions/checkout@v4
with:
repository: ${{ secrets.IL2CPP_ASSEMBLIES_REPO }}
token: ${{ secrets.IL2CPP_ASSEMBLIES_TOKEN }}
ref: ${{ steps.assembly-branch.outputs.branch }}
path: il2cpp-scheduleone-assemblies
fetch-depth: 1
if: steps.cache-il2cpp-assemblies.outputs.cache-hit != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
continue-on-error: false
- name: Debug IL2CPP Assemblies Structure
run: |
echo "=== IL2CPP Assemblies Repository Structure ==="
find il2cpp-scheduleone-assemblies -type f -name "*.dll" | head -30
echo ""
echo "=== Looking for Assembly-CSharp.dll in Il2CppAssemblies ==="
find il2cpp-scheduleone-assemblies -path "*/Il2CppAssemblies/Assembly-CSharp.dll" -type f
echo ""
echo "=== Looking for MelonLoader assemblies in net6 ==="
find il2cpp-scheduleone-assemblies -path "*/net6/*.dll" -type f | head -10
if: steps.cache-il2cpp-assemblies.outputs.cache-hit != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
- name: Copy IL2CPP Game Assemblies
run: |
# Copy IL2CPP generated assemblies (from MelonLoader/Il2CppAssemblies)
if [ -d "il2cpp-scheduleone-assemblies/MelonLoader/Il2CppAssemblies" ]; then
cp -r il2cpp-scheduleone-assemblies/MelonLoader/Il2CppAssemblies/* S1API/ScheduleOneAssemblies/Il2CppAssemblies/ || echo "Failed to copy IL2CPP generated assemblies"
echo "Copied IL2CPP generated assemblies from il2cpp-scheduleone-assemblies/MelonLoader/Il2CppAssemblies/"
else
echo "No il2cpp-scheduleone-assemblies/MelonLoader/Il2CppAssemblies directory found"
fi
# Copy MelonLoader assemblies (from MelonLoader/net6)
if [ -d "il2cpp-scheduleone-assemblies/MelonLoader/net6" ]; then
cp -r il2cpp-scheduleone-assemblies/MelonLoader/net6/* S1API/ScheduleOneAssemblies/MelonLoader/ || echo "Failed to copy MelonLoader assemblies"
echo "Copied MelonLoader assemblies from il2cpp-scheduleone-assemblies/MelonLoader/net6/"
else
echo "No il2cpp-scheduleone-assemblies/MelonLoader/net6 directory found"
fi
# Ensure all required Il2CppInterop assemblies are available
REQUIRED_IL2CPP_INTEROP_ASSEMBLIES=(
"Il2CppInterop.Runtime.dll"
"Il2CppInterop.Common.dll"
"Il2CppInterop.HarmonySupport.dll"
"Il2CppInterop.Generator.dll"
"UnityEngine.Il2CppAssetBundleManager.dll"
)
for ASSEMBLY in "${REQUIRED_IL2CPP_INTEROP_ASSEMBLIES[@]}"; do
if [ ! -f "S1API/ScheduleOneAssemblies/MelonLoader/$ASSEMBLY" ]; then
echo "Il2CppInterop assembly $ASSEMBLY not found in MelonLoader folder, searching for it..."
ASSEMBLY_PATH=$(find il2cpp-scheduleone-assemblies -name "$ASSEMBLY" -type f | head -1)
if [ -n "$ASSEMBLY_PATH" ]; then
echo "Found $ASSEMBLY at: $ASSEMBLY_PATH"
cp "$ASSEMBLY_PATH" S1API/ScheduleOneAssemblies/MelonLoader/ || echo "Failed to copy $ASSEMBLY"
echo "Copied $ASSEMBLY to MelonLoader folder"
else
echo "Warning: $ASSEMBLY not found in il2cpp-scheduleone-assemblies repository"
fi
fi
done
# Ensure all required Unity assemblies are available in Il2CppAssemblies
# List of required Unity assemblies for IL2CPP builds
REQUIRED_UNITY_ASSEMBLIES=(
"Assembly-CSharp.dll"
"Il2Cppmscorlib.dll"
"UnityEngine.dll"
"UnityEngine.CoreModule.dll"
"UnityEngine.UI.dll"
"UnityEngine.InputLegacyModule.dll"
"UnityEngine.UIModule.dll"
"UnityEngine.JSONSerializeModule.dll"
"UnityEngine.TextRenderingModule.dll"
"UnityEngine.ImageConversionModule.dll"
"UnityEngine.PhysicsModule.dll"
"Unity.TextMeshPro.dll"
"Il2CppFishNet.Runtime.dll"
)
MISSING_ASSEMBLIES=0
for ASSEMBLY in "${REQUIRED_UNITY_ASSEMBLIES[@]}"; do
if [ ! -f "S1API/ScheduleOneAssemblies/Il2CppAssemblies/$ASSEMBLY" ]; then
echo "Unity assembly $ASSEMBLY not found in Il2CppAssemblies folder, searching for it..."
ASSEMBLY_PATH=$(find il2cpp-scheduleone-assemblies -name "$ASSEMBLY" -type f | head -1)
if [ -n "$ASSEMBLY_PATH" ]; then
echo "Found $ASSEMBLY at: $ASSEMBLY_PATH"
cp "$ASSEMBLY_PATH" S1API/ScheduleOneAssemblies/Il2CppAssemblies/ || echo "Failed to copy $ASSEMBLY"
echo "Copied $ASSEMBLY to Il2CppAssemblies folder"
else
echo "Warning: $ASSEMBLY not found in il2cpp-scheduleone-assemblies repository"
MISSING_ASSEMBLIES=1
fi
fi
done
if [ $MISSING_ASSEMBLIES -eq 1 ]; then
echo "Warning: Some Unity assemblies are missing. This may cause build failures."
echo "Please ensure all required Unity assemblies are available in the IL2CPP assemblies repository."
fi
if: steps.cache-il2cpp-assemblies.outputs.cache-hit != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
- name: Verify IL2CPP Assembly Copies
run: |
echo "=== Verifying IL2CPP Assembly Structure ==="
ls -la S1API/ScheduleOneAssemblies/
echo ""
echo "=== IL2CPP Generated Assemblies ==="
ls -la S1API/ScheduleOneAssemblies/Il2CppAssemblies/ | head -10
echo ""
echo "=== MelonLoader Assemblies (net6) ==="
ls -la S1API/ScheduleOneAssemblies/MelonLoader/ | head -10
echo ""
echo "=== Critical Unity Assembly Check ==="
MISSING_ASSEMBLIES=0
# List of required Unity assemblies for IL2CPP builds
REQUIRED_UNITY_ASSEMBLIES=(
"Assembly-CSharp.dll"
"Il2Cppmscorlib.dll"
"UnityEngine.dll"
"UnityEngine.CoreModule.dll"
"UnityEngine.UI.dll"
"UnityEngine.InputLegacyModule.dll"
"UnityEngine.UIModule.dll"
"UnityEngine.JSONSerializeModule.dll"
"UnityEngine.TextRenderingModule.dll"
"UnityEngine.ImageConversionModule.dll"
"UnityEngine.PhysicsModule.dll"
"Unity.TextMeshPro.dll"
"Il2CppFishNet.Runtime.dll"
)
for ASSEMBLY in "${REQUIRED_UNITY_ASSEMBLIES[@]}"; do
if [ -f "S1API/ScheduleOneAssemblies/Il2CppAssemblies/$ASSEMBLY" ]; then
echo "✓ Found $ASSEMBLY"
ls -l "S1API/ScheduleOneAssemblies/Il2CppAssemblies/$ASSEMBLY" | awk '{print " " $9 " (" $5 " bytes)"}'
else
echo "✗ Missing $ASSEMBLY in Il2CppAssemblies"
MISSING_ASSEMBLIES=1
if [ -d "il2cpp-scheduleone-assemblies" ]; then
echo " Attempting auto-discovery..."
find il2cpp-scheduleone-assemblies -name "$ASSEMBLY" -type f -exec ls -l {} \; | head -1 || echo " Not found in il2cpp-scheduleone-assemblies repository"
fi
fi
done
echo ""
echo ""
echo "=== MelonLoader Assembly Check ==="
if [ -f S1API/ScheduleOneAssemblies/MelonLoader/0Harmony.dll ]; then
echo "✓ Found 0Harmony.dll (net6)"
ls -l S1API/ScheduleOneAssemblies/MelonLoader/0Harmony.dll
else
echo "✗ Missing 0Harmony.dll in MelonLoader"
MISSING_ASSEMBLIES=1
if [ -d "il2cpp-scheduleone-assemblies" ]; then
echo "Looking for MelonLoader assemblies..."
find il2cpp-scheduleone-assemblies -name "0Harmony.dll" -type f -exec ls -l {} \;
fi
fi
echo ""
echo "=== Il2CppInterop Assembly Check ==="
REQUIRED_IL2CPP_INTEROP_ASSEMBLIES=(
"Il2CppInterop.Runtime.dll"
"Il2CppInterop.Common.dll"
"Il2CppInterop.HarmonySupport.dll"
"Il2CppInterop.Generator.dll"
"UnityEngine.Il2CppAssetBundleManager.dll"
)
for ASSEMBLY in "${REQUIRED_IL2CPP_INTEROP_ASSEMBLIES[@]}"; do
if [ -f "S1API/ScheduleOneAssemblies/MelonLoader/$ASSEMBLY" ]; then
echo "✓ Found $ASSEMBLY"
ls -l "S1API/ScheduleOneAssemblies/MelonLoader/$ASSEMBLY" | awk '{print " " $9 " (" $5 " bytes)"}'
else
echo "✗ Missing $ASSEMBLY in MelonLoader"
MISSING_ASSEMBLIES=1
if [ -d "il2cpp-scheduleone-assemblies" ]; then
echo " Attempting auto-discovery..."
find il2cpp-scheduleone-assemblies -name "$ASSEMBLY" -type f -exec ls -l {} \; | head -1 || echo " Not found in il2cpp-scheduleone-assemblies repository"
fi
fi
done
echo ""
if [ $MISSING_ASSEMBLIES -eq 1 ]; then
echo "ERROR: Critical assemblies are missing!"
echo "Please ensure:"
echo "1. IL2CPP_ASSEMBLIES_REPO secret is set correctly"
echo "2. IL2CPP_ASSEMBLIES_TOKEN has access to the IL2CPP assemblies repo"
echo "3. The IL2CPP assemblies repo contains MelonLoader/Il2CppAssemblies/ and MelonLoader/net6/ directories"
exit 1
fi
echo ""
if [ "${{ steps.cache-il2cpp-assemblies.outputs.cache-hit }}" == "true" ]; then
echo "✓ Using cached IL2CPP assemblies for build"
else
echo "✓ Using fresh IL2CPP assemblies from repository"
fi
- name: Create CI Build Properties
run: |
cat > ci.build.props << 'EOF'
<Project>
<PropertyGroup>
<AutomateLocalDeployment>false</AutomateLocalDeployment>
<!-- CI Assembly Paths (relative to S1API/S1API.csproj) -->
<MelonLoaderAssembliesPath>$(MSBuildThisFileDirectory)S1API/ScheduleOneAssemblies/MelonLoader</MelonLoaderAssembliesPath>
<!-- IL2CPP Configuration -->
<LocalIl2CppDeploymentPath>null</LocalIl2CppDeploymentPath>
<Il2CppAssembliesPath Condition="'$(Configuration)' == 'Il2CppMelon'">$(MSBuildThisFileDirectory)S1API/ScheduleOneAssemblies/Il2CppAssemblies</Il2CppAssembliesPath>
<Il2CppAssembliesPath Condition="'$(Configuration)' == 'Il2CppBepInEx'">$(MSBuildThisFileDirectory)S1API/ScheduleOneAssemblies/Il2CppAssemblies</Il2CppAssembliesPath>
</PropertyGroup>
</Project>
EOF
- name: Verify Build Properties
run: |
echo "=== CI Build Properties ==="
cat ci.build.props
echo ""
echo "=== MSBuild Property Resolution Test ==="
cd S1API
echo "Current directory: $(pwd)"
echo ""
echo "Testing Il2CppAssembliesPath resolution (expect ./ScheduleOneAssemblies/Il2CppAssemblies from project dir)..."
if [ -f "./ScheduleOneAssemblies/Il2CppAssemblies/Assembly-CSharp.dll" ]; then
echo "✓ Assembly-CSharp.dll accessible from build context"
ls -l ./ScheduleOneAssemblies/Il2CppAssemblies/Assembly-CSharp.dll
else
echo "✗ Assembly-CSharp.dll NOT accessible from build context"
echo "Looking for: ./ScheduleOneAssemblies/Il2CppAssemblies/Assembly-CSharp.dll"
ls -la ./ScheduleOneAssemblies/Il2CppAssemblies/ || echo "Directory doesn't exist"
fi
echo ""
echo "Testing MelonLoaderAssembliesPath resolution (expect ./ScheduleOneAssemblies/MelonLoader from project dir)..."
if [ -f "./ScheduleOneAssemblies/MelonLoader/0Harmony.dll" ]; then
echo "✓ 0Harmony.dll accessible from build context"
ls -l ./ScheduleOneAssemblies/MelonLoader/0Harmony.dll
else
echo "✗ 0Harmony.dll NOT accessible from build context"
echo "Looking for: ./ScheduleOneAssemblies/MelonLoader/0Harmony.dll"
ls -la ./ScheduleOneAssemblies/MelonLoader/ || echo "Directory doesn't exist"
fi
echo ""
echo "Testing Il2CppInterop assemblies availability (required for Il2CppMelon)..."
REQUIRED_IL2CPP_INTEROP_ASSEMBLIES=(
"Il2CppInterop.Runtime.dll"
"Il2CppInterop.Common.dll"
"Il2CppInterop.HarmonySupport.dll"
"Il2CppInterop.Generator.dll"
"UnityEngine.Il2CppAssetBundleManager.dll"
)
MISSING_INTEROP=0
for ASSEMBLY in "${REQUIRED_IL2CPP_INTEROP_ASSEMBLIES[@]}"; do
if [ -f "./ScheduleOneAssemblies/MelonLoader/$ASSEMBLY" ]; then
echo "✓ $ASSEMBLY accessible from build context"
ls -l "./ScheduleOneAssemblies/MelonLoader/$ASSEMBLY" | awk '{print " " $9 " (" $5 " bytes)"}'
else
echo "✗ $ASSEMBLY NOT accessible from build context"
echo " Looking for: ./ScheduleOneAssemblies/MelonLoader/$ASSEMBLY"
MISSING_INTEROP=1
fi
done
if [ $MISSING_INTEROP -eq 1 ]; then
echo ""
echo "Listing contents of MelonLoader directory:"
ls -la ./ScheduleOneAssemblies/MelonLoader/ || echo "Directory doesn't exist"
fi
- name: Verify MSBuild Property Resolution
run: |
ls -la
echo "Attempting to enter S1API directory..."
if [ -d "S1API" ]; then
cd S1API
echo "Successfully entered S1API. Current directory: $(pwd)"
else
echo "ERROR: S1API directory not found in $(pwd)"
exit 1
fi
echo "=== Testing MSBuild Property Evaluation ==="
echo "Evaluating MelonLoaderAssembliesPath..."
# Note: We just check if the property resolves to SOMETHING containing ScheduleOneAssemblies now, as absolute paths might vary in env
dotnet msbuild S1API.csproj -t:ResolveAssemblyReferences -p:Configuration=Il2CppMelon -v:minimal 2>&1 | grep -i "ScheduleOneAssemblies" | head -5 || echo "Property evaluation test output captured"
echo ""
echo "Checking if assemblies are accessible via expected paths..."
# Since we used $(MSBuildThisFileDirectory), the path ends up being AbsoluteROOT/S1API/ScheduleOneAssemblies/...
# We are currently in AbsoluteROOT/S1API
echo "Checking for MelonLoader assemblies in: ./ScheduleOneAssemblies/MelonLoader"
if [ -d "ScheduleOneAssemblies/MelonLoader" ]; then
echo "✓ MelonLoaderAssembliesPath directory exists"
echo " Contents:"
ls -1 ScheduleOneAssemblies/MelonLoader/*.dll 2>/dev/null | head -10 || echo " No DLLs found"
else
echo "✗ MelonLoaderAssembliesPath directory does not exist at ./ScheduleOneAssemblies/MelonLoader"
echo " Current Directory Contents:"
ls -la
fi
echo ""
echo "Checking for Il2CppAssemblies in: ./ScheduleOneAssemblies/Il2CppAssemblies"
if [ -d "ScheduleOneAssemblies/Il2CppAssemblies" ]; then
echo "✓ Il2CppAssembliesPath directory exists"
echo " Sample contents:"
ls -1 ScheduleOneAssemblies/Il2CppAssemblies/*.dll 2>/dev/null | head -5 || echo " No DLLs found"
else
echo "✗ Il2CppAssembliesPath directory does not exist at ./ScheduleOneAssemblies/Il2CppAssemblies"
fi
- name: Restore dependencies
run: dotnet restore S1API/S1API.csproj /p:Configuration=Il2CppMelon
- name: Build S1API (IL2CPP)
run: dotnet build S1API/S1API.csproj --no-restore -c Il2CppMelon -v normal
- name: Upload S1API Il2Cpp Artifact
uses: actions/upload-artifact@v4
with:
name: S1API-Il2Cpp
path: S1API/bin/Il2CppMelon/net6.0/S1API.dll
# Save cache even if build fails (to enable beta cache priming)
# Save on PR events AND on pushes to main branches (to update cache after assembly updates)
- name: Save IL2CPP Game Assemblies to Cache
uses: actions/cache/save@v4
if: always() && (github.event_name == 'pull_request' || (github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/npc-prefabs'))) && steps.cache-il2cpp-assemblies.outputs.cache-hit != 'true'
with:
path: S1API/ScheduleOneAssemblies
key: il2cpp-game-assemblies-v4-${{ steps.assembly-branch.outputs.branch }}-${{ hashFiles('S1API/S1API.csproj') }}
- name: Build Summary
if: always()
run: |
echo "=== IL2CPP Build Check Complete ==="
echo "Configuration: Il2CppMelon"
echo "Status: ${{ job.status }}"