Jump to content

First v25 modding impressions


Recommended Posts

I tried modding the game last week. Of course that's not supported yet (I had to edit a DLL to get stuff loaded), but I think I'm able to comment on a few things:

  • The game runs on Unity 5.5.6f1... which is five years old. Likely as a result, Harmony patching does not work because of missing equality operators in System.Reflection which were added in .NET Framework 4. This is really bad for modding, because it means the game's code can't be easily patched and anything we want to do has to be possible with the existing code. So that basically rules out changing existing behaviour or installing hooks. It might be possible to edit mscorlib.dll or change Harmony so that it does not use missing stuff, but the first is impractical and the second is hard. I'm just being dumb, Harmony works fine provided you use the .NET Framework 3.5 version.
  • Everything is happening in one scene, which really surprised me. This also means it's not possible to use any of Unity's SceneManager events, and the game does not yet have something to replace that as far as I can tell. No doubt that'll come though.
  • There is a lot of inheritance going on, I think I need to make a flowchart to see which things inherit from MonoBehaviour.
Edited by asdfcyber
Harmony DOES work, error on my part
Link to comment
Share on other sites

Xenonauts 2 uses Unity predominantly as a rendering and authoring engine - most of the logic is actually unobfuscated, native C# (not inheriting from Monobehaviour) and all the hooks that the programmers have are exposed to modders.
It did not make much sense to go with a scene-based approach because most content is not static - Ground Combat is fully generated at runtime, there are no separate Scenes.
It's mostly UI or visual aspects which use Monobehaviour based inheritance, the rest is full native C#.

I will provide an outline with an example once we are further along how to create proper mods using injection, which to my recollection does work but it has been some time since I last tested it.
Can you be more detailed in what issues you ran into getting Harmony to work? 

At the moment we are completely focused on the early access release though, so I can't commit any resources to helping you out, but if you can provide the information then I have it available when I will.

Link to comment
Share on other sites

27 minutes ago, Gijs-Jan said:

Xenonauts 2 uses Unity predominantly as a rendering and authoring engine - most of the logic is actually unobfuscated, native C# (not inheriting from Monobehaviour) and all the hooks that the programmers have are exposed to modders.

I hadn't encountered that before in Unity games, but I suppose it makes sense. Wouldn't that make it easier to upgrade though? About the hooks: if I want to have code execute every time the game uses a specific method I'd need to patch that method, and I haven't found many hooks in that native C# part. For instance, I would like to execute something when the strategy screen is loaded, but there is not something like an event I can subscribe to (or is there?)

35 minutes ago, Gijs-Jan said:

I will provide an outline with an example once we are further along how to create proper mods using injection, which to my recollection does work but it has been some time since I last tested it.

I couldn't find such a thing in the Assembly-CSharp.dll when I looked so I added my own IMod and Xenonauts.XenonautsMain.LoadMods. I can share those if you want? If mod assembly loading is included in the game I must've been searching for the wrong thing, can you tell me what namespace/class it might live in?

43 minutes ago, Gijs-Jan said:

Can you be more detailed in what issues you ran into getting Harmony to work? 

Executing Harmony.PatchAll gave a System.MissingMethodException about System.Reflection.PropertyInfo.op_Inequality. But for some reason I was being quite dumb and was building against .NET Framework 4.8 instead of 3.5 (Harmony luckily still supports that), so changing the build target actually solved it :)

1 hour ago, Gijs-Jan said:

At the moment we are completely focused on the early access release though, so I can't commit any resources to helping you out, but if you can provide the information then I have it available when I will.

Of course, I don't expect you guys to.

Link to comment
Share on other sites

I hadn't encountered that before in Unity games, but I suppose it makes sense. Wouldn't that make it easier to upgrade though?

Yes, it does in general, however the issue is mostly in the third party plugins we use that also then need to be upgraded. Personally I would like to upgrade, even if just for the tooling and speedups that Unity now has. The problem is mostly that we simply don't have the budget at the moment and given the length of development the focus is and was on finishing the game. 

About the hooks: if I want to have code execute every time the game uses a specific method I'd need to patch that method, and I haven't found many hooks in that native C# part. For instance, I would like to execute something when the strategy screen is loaded, but there is not something like an event I can subscribe to (or is there?)

The core of our game is an ECS framework I developed that you will find under the Artitas namespace. The `World` class in this namespace is the general entry point to managing an ECS instance. This framework has two aspects: high level communication through messages inheriting from `IEvent`, which any `System` can listen to by annotating methods with `Subscriber`  - and low-level event handling based on changes to entities through `Family`.  In general we setup mechanics in `System`s, with each System listening to certain events and setting up various Family-s to respond to events. A System can listen to any event, define its priority, and if needed stop event propagation.

To get an overview of compositions of entities we use, look into the classes that end in `Archetypes`.

tl;dr: look at the inheritance of `IEvent` for all the high level communication you can hook into.

A `World` can have `System`s registered and unregistered at runtime - the main way in which we want to support modding.

The core entry point of the game is `XenonautsMain`, with `StrategyScreen` & `GroundCombatScreen` being the core entry points for each section of the game. In those Screen classes you'll find the setup of the World and its Systems.

Executing Harmony.PatchAll gave a System.MissingMethodException about System.Reflection.PropertyInfo.op_Inequality. But for some reason I was being quite dumb and was building against .NET Framework 4.8 instead of 3.5 (Harmony luckily still supports that), so changing the build target actually solved it :)  

Yeah, that'll be it :D. I'd love to be able to upgrade :')

  • Like 1
Link to comment
Share on other sites

6 hours ago, Gijs-Jan said:

The problem is mostly that we simply don't have the budget at the moment and given the length of development the focus is and was on finishing the game. 

Quite understandable. I'm sure you'll be able to get to it eventually :)

6 hours ago, Gijs-Jan said:

information

Thanks, that's really helpful! When I have some more spare time next month or so I'll try actually making a mod.

I thought I posted this 10h ago but apparently not

Link to comment
Share on other sites

  • 3 months later...

I managed to do something:

20230426231646_1.thumb.jpg.4ec85b3de6d9a9b455cb7b99717e04fe.jpg

Not entirely correct though, 0° latitude isn't in the middle because Antarctica has been cut off and there might be other issues as well

image.thumb.png.45c42d285467dd516cd69bd0fd1e2d46.png

 

Edit: fixed it, though now it behaves weirdly around the north pole where the geoscape map goes higher than is physically possible.

image.thumb.png.6736f978dc2587aaf1b5a248806ee40e.png

Edited by asdfcyber
  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...
On 4/26/2023 at 11:28 PM, asdfcyber said:

I managed to do something:

20230426231646_1.thumb.jpg.4ec85b3de6d9a9b455cb7b99717e04fe.jpg

Not entirely correct though, 0° latitude isn't in the middle because Antarctica has been cut off and there might be other issues as well

image.thumb.png.45c42d285467dd516cd69bd0fd1e2d46.png

 

Edit: fixed it, though now it behaves weirdly around the north pole where the geoscape map goes higher than is physically possible.

image.thumb.png.6736f978dc2587aaf1b5a248806ee40e.png

Very nicely done! 
Would you mind sharing the code & setup as I'm very interested in keeping track of what issues people run into during more complex modding.

Also, are there any particular issues you ran into while rummaging around the codebase?

Link to comment
Share on other sites

On 5/8/2023 at 8:58 PM, Gijs-Jan said:

Very nicely done! 
Would you mind sharing the code & setup as I'm very interested in keeping track of what issues people run into during more complex modding.

Also, are there any particular issues you ran into while rummaging around the codebase?

Definitely! You can find it on github.com/asdfcyber/geoshape. Most of the work is figuring out how the code works (in particular, I'm not sure what the difference is between GeoscapeNavigationSystem and GeoscapeMovementSystem - I think the first sets future velocities/rotations that get applied in the second?). I'm not used to the level of abstraction in the code which adds some extra difficulty but so far I haven't encountered any showstoppers. Currently I'm trying to figure out how to find interception points, and after that I want to try to tackle radar range and accurate day/night cycles.

Edit: oh, and the setup: I have a post-build event set up that copies the .dll to My Games/Xenonauts 2/Mods/Geoshape/Geoshape.dll and starts the game, and I modified the original Assembly-CSharp.dll to scan that mods directory for any modname/modname.dll that implements Xenonauts.IMod to load. These are all the modifications I made (I didn't care about code style here btw, this has to be reapplied every time there's a patch anyway and the compiler will replace certain things such as inverted conditions):

public interface IMod
{
    void Initialise();
}

public sealed class XenonautsMain : bunchofstuff
{
    public static List<IMod> ActiveMods;

    public void LoadMods()
    {
        if (this.ActiveMods == null)
        {
            this.ActiveMods = new List<IMod>();
        }
        foreach (string dir in Directory.GetDirectories(Environment.ExpandEnvironmentVariables(
          "%USERPROFILE%\\Documents\\My Games\\Xenonauts 2\\Mods")))
        {
            string modname = Path.GetFileName(dir);
            Debug.Log("LoadMods is checking if mod '" + modname + "' exists");
            string modfile = dir + "\\" + modname + ".dll";
            if (File.Exists(modfile))
            {
                foreach (Type type in Assembly.LoadFile(modfile).GetExportedTypes())
                {
                    if (type.IsClass && typeof(IMod).IsAssignableFrom(type))
                    {
                        IMod mod = (IMod)Activator.CreateInstance(type);
                        if (mod != null)
                        {
                            this.ActiveMods.Add(mod);
                            mod.Initialise();
                            Debug.Log("LoadMods loaded '" + dir + "'  succesfully");
                        }
                    }
                }
            }
        }
    }
  
    static XenonautsMain()
    {
      	// bunch of stuff
      	LoadMods();
    }
}

To get this compiled some compiler-generated things need to be turned into valid C#, so I rename fields like <>f__mg$cache0 fields to cache0 and delete most of the new LoadLocalizationFiles because anonymous types also become something weird

Edited by asdfcyber
IMod is in the namespace, not in the class
  • Like 1
Link to comment
Share on other sites

Very nicely done!

If you do have specific questions, feel free to ask. I can't promise I'll reply in a timely fashion given that we're laser focused on the upcoming deadline, but I'll do what I can.

The GeoscapeMovementSystem is best seen as a very trivial physics system (apply velocity, etc), the NavigationSystem indeed is the system that given a target converts it into a target velocity, etc. 

This was done so we can have different strategies driving the movement (AI, physics, etc) - and is mostly a hold over from when we had different designs.

Link to comment
Share on other sites

Except for the Unity development console which lists errors, which we've hidden, there's none.
There are minimal developer tools that are in the released game except for some cheats and debug information in GC. 
Given that we work in Unity, most development tools are exposed through that.

public static readonly string SHOW_FPS = "-showFPS";
public static readonly string CHEAT_MODE = "-cheatmode";
public static readonly string SHOW_DEBUG_UI = "-showDebugUI";
public static readonly string DEBUG_AI = "-debugAI";

The above are the command line arguments you can give the program. cheatmode & showDebugUI enable the cheats in GC & visualization.

The GC has a grid data visualizer:

Option B: Slot View

List of GC cheats:

```

  • ALT-RMB: Select the entity represented by the GameObject under the cursor in the hierarchy.

  • ALT-SHIFT-RMB: Select the GameObject under the cursor in the hierarchy.

  • CTRL+SHIFT+V: Kill all aliens and win the mission.

Health

  • M: Set entity under the cursor to full health.

  • B: Report mission state.

  • CTRL+SHIFT+M: Stun Vips and kill enemies.

  • N: Set entity under the cursor to 25% health

  • SHIFT+U: Set selected unit to 1000 HP

  • U: Set selected unit to Max Hp

Time Units

  • Shitft+T: Set selected unit to 1000 TU

  • T: Set selected unit to Max Tu

Others

  • O: Enable/Disable corner peeking

  • R: Refill ammo clip

  • K: Kill unit under the cursor

  • Y: Teleport selected unit

  • ALT+L: Toggle wall hiding

  • [: Switched to ConnectedAdjacentCoverFinder

  • ]: Switched to AdjacentCoverFinder

```

  • Thanks 1
Link to comment
Share on other sites

  • 3 months later...
  • 2 weeks later...
15 hours ago, Razorsharp said:

Could someone please help me? I have no idea how to mod on this... all i want is to replace the beginner Xenonaut armor skins with the soldiers in the desert/urban camo variant

 

PLEASE HELP

I'm not sure its really possible at this stage (not without a lot of knowledge of Unity and 3d modelling)

Its not like X1 where you could just copy some sprites from one folder to another. 99% of the assets for X2 are contained in Unity asset bundles. It is possible to alter the bundles and replace assets. However, the Xenonaut armour is going to be its own 3d model with its own texture and animations. I'd imagine you can't just switch the models as it's likely the local forces have their own 3d models with their own textures and animations.

If the local forces and the Xenonaut starter armour share the same model then it might be possible to swap the textures but that would require finding the texture in the 370 odd asset bundles, extracting them, swapping them and then saving them back to the bundle.

 

 

 

Link to comment
Share on other sites

On 8/31/2023 at 12:24 AM, 38Simulated said:

I'm not sure its really possible at this stage (not without a lot of knowledge of Unity and 3d modelling)

Its not like X1 where you could just copy some sprites from one folder to another. 99% of the assets for X2 are contained in Unity asset bundles. It is possible to alter the bundles and replace assets. However, the Xenonaut armour is going to be its own 3d model with its own texture and animations. I'd imagine you can't just switch the models as it's likely the local forces have their own 3d models with their own textures and animations.

If the local forces and the Xenonaut starter armour share the same model then it might be possible to swap the textures but that would require finding the texture in the 370 odd asset bundles, extracting them, swapping them and then saving them back to the bundle.

 

 

 

Oh... oh well, guess i'll just wait until it's added, hopefully.

Link to comment
Share on other sites

On 8/15/2023 at 1:20 PM, PinkPanteRus said:

Read this topic and don`t understand how to start modding.

How do you open compiled game for changes?

Do you use Unity Editor or what?

No unity editor is involved, I edited the game's compiled code to make it load my own code (I explained my setup earlier in this thread). It's not exactly beginner friendly. As for replacing assets, it's probably possible to do it at runtime this way but I've never done it.

Edited by asdfcyber
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...