Mega Tree API
Project setup
The Mono csproj that actually builds, and how to reference MegaTree.dll without shipping a second copy of it.
This is the page that decides whether anything works
Megastore Simulator runs on Mono. Supermarket Simulator runs on IL2CPP. Everything on this page follows from that one difference, and getting it wrong produces the same symptom every time: the game starts, your mod is not in the plugin list, and nothing is written to the log.
| Supermarket Simulator | Megastore Simulator | |
|---|---|---|
| BepInEx | 6, IL2CPP build | 5, Mono build |
| Target framework | net6.0 | netstandard2.1 |
| BepInEx references | BepInEx.Core + BepInEx.Unity.IL2CPP | one BepInEx.dll |
| Il2CppInterop | required | does not exist |
| Game assemblies | BepInEx/interop/ | Megastore Simulator_Data/Managed/ |
| Base class | BasePlugin, Load() | BaseUnityPlugin, Awake() |
Why netstandard2.1 and not net35
net35 is the classic target for a Mono game mod, and it is the first
thing most people try. It fails on a modern SDK with MSB3644: the reference assemblies for
.NETFramework v3.5 were not found - they do not ship with the SDK any more, and getting
them means installing an old developer pack.
netstandard2.1 avoids that entirely: the reference assemblies come
from NuGet on their own, and Unity's Mono runtime supports the profile natively. Mega Tree itself
is built this way.
The complete csproj
This is the whole file. Nothing is omitted - if you copy it and fix the
GameDir, it builds.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<AssemblyName>BigBoxes</AssemblyName>
<RootNamespace>BigBoxes</RootNamespace>
<LangVersion>latest</LangVersion>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<!-- Without this the build copies every referenced dll into your output. -->
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<Version>1.0.0</Version>
<!-- Change this one line and everything below follows. -->
<GameDir>C:\Program Files (x86)\Steam\steamapps\common\Megastore Simulator</GameDir>
<Managed>$(GameDir)\Megastore Simulator_Data\Managed</Managed>
</PropertyGroup>
<ItemGroup>
<Reference Include="MegaTree">
<HintPath>$(GameDir)\BepInEx\plugins\rodopoulos\MegaTree.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="BepInEx">
<HintPath>$(GameDir)\BepInEx\core\BepInEx.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>$(GameDir)\BepInEx\core\0Harmony.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(Managed)\UnityEngine.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(Managed)\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
</Reference>
<!-- The game itself. Here it is the real assembly, with real IL. -->
<Reference Include="Assembly-CSharp">
<HintPath>$(Managed)\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
</Project>Without it, MSBuild copies MegaTree.dll (and the game assemblies) into your output folder. Anyone who then ships that folder puts a second Mega Tree into BepInEx/plugins. BepInEx drops one of the two over a duplicate guid, and which copy survives is not up to you - so the player may be running a version of Mega Tree you shipped by accident, months out of date.
It also means the zip on your mod page contains most of the game.
Adding more UnityEngine modules
Unity splits the engine across many assemblies, and the compiler error when one is missing does not look like a missing reference - it looks like your code is wrong:
| You use | Add |
|---|---|
Input.GetKeyDown | UnityEngine.InputLegacyModule |
Image, Button, ScrollRect | UnityEngine.UI and UnityEngine.UIModule |
TMP_Text | Unity.TextMeshPro |
ImageConversion.LoadImage | UnityEngine.ImageConversionModule |
Physics.Raycast | UnityEngine.PhysicsModule |
NavMeshAgent | UnityEngine.AIModule |
All of them live in the same Managed folder, so the block is always
the same three lines with a different name.
The plugin skeleton
using BepInEx;
using HarmonyLib;
using MegaTree.Api;
namespace BigBoxes;
[BepInPlugin("BigBoxes", "Big Boxes", "1.0.0")]
[BepInDependency(MegaTreeApi.ModGuid)]
public sealed class Plugin : BaseUnityPlugin
{
private void Awake()
{
var tree = MegaTreeApi.ForPlugin("BigBoxes", "Big Boxes");
// ... your Register calls ...
new Harmony("BigBoxes").PatchAll(typeof(Plugin).Assembly);
}
}What you do NOT need here
If you are porting from Supermarket Simulator, delete all of this - none of it exists in Mono and each one is a compile error waiting to confuse you:
using Il2CppInterop.Runtime.Injection;ClassInjector.RegisterTypeInIl2Cpp<T>()- a MonoBehaviour is just a MonoBehaviour here, andAddComponent<T>()works.- The
public T(IntPtr ptr) : base(ptr) { }constructor. (UnityAction)(() => ...)casts on button listeners.- Il2Cpp collection types -
List<T>isList<T>.
In Supermarket Simulator, writing to a static field of a game class crashes the process - the field is const-backed in native code. That rule is IL2CPP's, not C#'s, and it does not apply here. In Mono a static field is a static field.
Building
dotnet build BigBoxes.csproj -c DebugThen close the game, drop the dll into
BepInEx/plugins/, and start it again. BepInEx locks the file while the
game runs, so copying over a running game fails silently - which reads as "my change did nothing".