Skip to content

Instantly share code, notes, and snippets.

@sodiboo
Last active July 25, 2021 23:25
Show Gist options
  • Save sodiboo/198e0a741af46034fa685853c9136a5c to your computer and use it in GitHub Desktop.
Save sodiboo/198e0a741af46034fa685853c9136a5c to your computer and use it in GitHub Desktop.
Cross-Platform modding guide for Muck

Cross-Platform modding for Muck.

Even though this guide is specifically for Muck, most of it applies to any Unity game.

Muck is compiled using Mono, which is cross-platform. I did notice that the hashes of the assemblies for the Linux version are not the same as the Windows version, but my mods seemed to work regardless.

Because assemblies are cross-platform, if your mod only applies code patches with no additional assets, you don't need to do anything about the AssetBundles. However, if your mod reads or writes any files to the disk, make sure not to hardcode backslashes! To work with the filesystem, use .NET's Path helpers, they will make sure you write to the desired location!

BAD:

Paths.ConfigPath + @"\MyModConfig.cfg"

GOOD:

Path.Combine(Paths.ConfigPath, "MyModConfig.cfg")

Do reference Facepunch.Steamworks directly

There used to be a reflection fix here that doesn't reference Facepunch.Steamworks. Don't do that, there's a more elegant way! Simply ignore the fact that Facepunch.Steamworks has two different assembly identities for Windows and Posix. If any of your users experience issues, tell them to get UnifiedSteamworks. This package has all the Steamworks types forwarded to the actual assembly, with an identity that pretends to be the other platform.

This is a very neat feature of .NET which is intended to let you move a type to another assembly without breaking programs that depend on the old version. In this case, the "old" version is whatever you built against, and the "new" location of the type is the one from the other platform that comes with Muck. And the "new" version of the original assembly is my UnifiedSteamworks, which forwards all the types.

AssetBundles

If your mod uses AssetBundles to add any assets, anything other than unity objects (i.e. sprites, sounds) will likely be wrong if you just use the windows version, including assets that you didn't explicitly specify because AssetBundles don't reference game assets, they're duplicated in the AssetBundle. (be very cautious about that when adding items! it's likely a better idea to leave item references like recipe ingredients blank in your assets and add them in code, because it can quickly increase your bundle size and cause issues)

Installing build tools

First, you'll need to make sure you install the correct build tools for the platforms your mod should support (all of them, because really, it's not hard). You need to also make sure to install them for the correct unity version (2019.4.19f1 for Muck)

Go to the Installs tab in Unity Hub and press the 3 dots in the top right of your install.

Click "Add Modules" to add more Unity modules.

Install the "Build Support (Mono)" module for the platforms you will support (seriously, do it for all of them because it's not harder than pressing 3 more buttons)

Building the AssetBundles

Now, open the Unity project where you have your AssetBundles. Open AssetBundle Browser and go to the Build tab. In the "Build Target" dropdown, select a platform to build AssetBundles for and press Build. Repeat this for all platforms.

When you build the bundles, also make sure they're not overwriting each other with a custom output path. To avoid this, just press "Reset" before each build if the names don't make sense.

What platforms do you actually want to build for? Preferably, you'd want Standalone OSX Universal, Standalone Linux and Standalone Windows. I was getting issues when trying to compile for Standalone Linux, and i thought that instead of fixing it, because Muck is only compiled for 64-bit systems by Dani, i just chose to compile for Standalone Linux 64 instead. It worked in my testing VM, but you should absolutely not rely on this if Muck ever supports 32-bit in the future. The image below shows the platforms i compile my mods for.

Now, in the Unity project root, you should have a folder called "AssetBundles" and within it 3 folders, one for each platform.

Loading the AssetBundles

Copy these AssetBundles into your mod's C# project, and add a platform suffix. If you want your own platform-implementation, you can stop reading here, but if you wanna yank mine, feel free to. For my implementation, you need to make sure that all the AssetBundles are named the same thing, with a suffix that represents the platform (windows, osx, linux) in lowercase.

Make sure to also include them all as embedded resources in your assembly, if you want a clean single file output.

  <ItemGroup>
    <EmbeddedResource Include="settings-windows" />
    <EmbeddedResource Include="settings-linux" />
    <EmbeddedResource Include="settings-osx" />
  </ItemGroup>

And finally, in your actual C# code, add the following method, which will load the correct platform's bundle. This method is based on... uh, who did i steal the non-crossplatform implementation from again?

        static readonly OSPlatform[] supportedPlatforms = new[] { OSPlatform.Windows, OSPlatform.Linux, OSPlatform.OSX };

        static AssetBundle GetAssetBundle(string name)
        {
            foreach (var platform in supportedPlatforms) {
                if (RuntimeInformation.IsOSPlatform(platform)) {
                    name = $"{name}-{platform.ToString().ToLower()}";
                    goto load;
                }
            }

            throw new PlatformNotSupportedException("Unsupported platform, cannot load AssetBundles");

            load:
            var execAssembly = Assembly.GetExecutingAssembly();

            var resourceName = execAssembly.GetManifestResourceNames().Single(str => str.EndsWith(name));

            using (var stream = execAssembly.GetManifestResourceStream(resourceName))
            {
                return AssetBundle.LoadFromStream(stream);
            }
        }

You can then retreive the bundle by calling this method with the actual AssetBundle's name, such as in my settings mod GetAssetBundle("settings") will pick the correct AssetBundle from settings-windows, settings-linux and settings-osx. You should load the AssetBundle before you patch the game with Harmony, because if for some reason the AssetBundle is unavailable, it's preferable for one exception to stop your mod before it starts, instead of causing many exceptions in the methods you patch.

That's it, provided you didn't fuck up any steps, your mod should now work for all platforms! Yay!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment