Modding Resources, tips and tricks!

Ole Marius Myrvold

JWB 96-13
Club Staff
Premium
Hi all! :)

As it is a new game, and a slightly different genre and build that most of the games on this site, there might be some newfound tricks to make modding faster, easier and/or better.
Personally I have more than enough to even try to understand how I might be able to mod the game, but I know that many of you guys are well underway with some cracking mods!
Anyway, sharing is caring, and making each other better is only positive in the long term, who knows what me might be able to achieve with this game! It is also easier to find for new modders, or old modders finding a new game if we are able to gather the brilliant research from you guys on one place :)

Keep up the magnificent work!

@thrashersfreak @Panamera4 @Colin O Callaghan @NemanjaSipka @TheFlamingRed @Mojojo999
 
Open the extracted drivers.txt in WordPad, make the changes and save that WordPad document as a txt file.
Thanks, but what are those random numbers for?

EDIT: I found information on that. But now I'm suck at importing it again. It doesn't change.

EDIT of the EDIT: I found that out as well. But now I have the same problem as someone had already...the Drivers are all named: "." now.
 
Last edited:
Alright I have a question and I do it here since there is no sub_forum for questions yet. Do you have to start a new game when editing files? I am far into this game now and would like to use my old save game but it seems that it doesn't work that way, is there a way to work around it?

I don't believe so. Not if you're just looking to adjust information in the Assets. A 'copy' of the Assets is taken into your save file, and is updated live in there.
You can theoretically edit the save game file, but this is more awkward then editing the database.

Fore those of you getting 'Dots' instead of your changes... are are you importing/exporting... are you using the Dumps/Exports buttons... or the 'Pluggin' button...
 
Last edited:
So... I've made a bunch of balance changes (making things harder) in the assets to:
  1. Make it so that it takes 60 seconds in practice/qualy to change your setup. This will make things harder for the player get a 'spot on setup' - sacrificing information bonuses.
  2. Adjusted the wear rate of Attack and Push - so the AI doesn't burn their tires so violently
  3. Modified Tyre Life, so good drivers can get more life out of their tires, and that your chassis matters more to your tires life too. - Between this and above, depending on your car and drivers, you will finally see the AI be able to push a lot on their tires - and may even wear slower then your tyre wear on medium! (assuming you have weaker car/drivers)
  4. Modified Chassis Fuel consumption, so it actually means something - made fuel slightly heavier also.
  5. Increase race distance slightly, so doing "Softest tires, softest tires, softest tires" is not as easily achievable (car and driver dependant... Milan also needs a wear rate change/to do).
  6. Changed the 'critial component' bonus/penalty spread. This will actually spread the cars out a lot more according to the performance of each car - making it so you should not be winning races at Predator (even with Rodrigues) until you have advanced your car a bit. Note: This is creating increased field spread. This also means worse cars then yours will compete less with you - but this happened in vanilla anyway - as you were able to punch well above your level.
Additionally

  1. I have made a seperate file which will spread out theStarting Performance of each car's coponenets. i.e., instead of Predator having a rear win of 1, and the best car having a rear wing of 200, it will now be 1 vs 400, further increasing the performance gap.
I am still testing this - but its looking 'okay'. My car performance changes may be a little too extreme though, but it's really a hassle trying to nerf the player and without really nerfing the AI.

To Do
  1. May Upgrade Slots 'blind' so you cannot see what the slot is doing.
  2. Change the Unknown driver standing to have a number of debuffs (Development takes +1 day, finances +5% etc)
  3. Change the AI's Race strategy (last on my list - as I expect the first patch to later them all anyway slightly).
Other suggestions welcome
 
I don't believe so. Not if you're just looking to adjust information in the Assets. A 'copy' of the Assets is taken into your save file, and is updated live in there.
You can theoretically edit the save game file, but this is more awkward then editing the database.

I tried it out and nothing is showing up in my old save when I continue my old career. Now starting a new everything shows up what I edited in the resource file. I think someone said that you have to start a new career for everything to show which kinda *Censored*. I do hope that someone comes with a solution.
 
You already have one! The payload is in mm2h.dll, you can replace this with your own payload. You will need a CSharp compiler, I use Visual Studio 2015 the free edition for this. You need a class called "mm2hack" with a static method named "init" this is the entry point the injector calls. So a minimal example:

Code:
public class mm2hack {
    static void init() {
        // we're in!
    }
}

To compile this you want to link to the game's assemblies, so the command would be something like:
Code:
csc /pathtomm/MM_Data/Managed/Assembly-CSharp.dll /path/etc/Assembly-CSharp-firstpass.dll /path/etc/UnityEngine.dll /path/UnityEngine.UI.dll /path/mscorlib.dll /nostdlib my_file.cs

Make sure your resulting DLL is named "mm2h.dll" and replace the other one with it. Now your payload will be injected. Here's the one I'm currently using in my game, which allows you to zoom in and out much further in a race and makes the 3x speed considerably faster.

Code:
using UnityEngine;
using System.Reflection;

public class mm2hack : MonoBehaviour {
    App app;
    Game game;
    GameState.Type state;

    static void init() {
        GameObject obj = new GameObject();
        obj.AddComponent<mm2hack>();
    }

    void Awake() {
        app = App.instance;
        game = Game.instance;
        state = GameState.Type.None;
        GameStatsConstants.minTweetValue = 16;
        GameStatsConstants.maxTweetValue = 32;
    }

    void Update() {
        if(app.gameStateManager.currentState.type != state) {
            state = app.gameStateManager.currentState.type;
            OnStateChange();
        }
    }

    void OnStateChange() {
        System.Console.WriteLine("State changed to: " + state.ToString());
        ConsoleCommands.useAIForDrivers = false;

        switch(state) {
            case GameState.Type.StartUp:
                if(app.database == null)
                    app.LoadGameDatabases();

                app.gameStateManager.LoadToTitleScreen();
                break;

            case GameState.Type.FrontendState:
                int index;
                for(index = 0; index < game.teamManager.count; index += 1) {
                    Team team = game.teamManager.GetEntity(index);
                    //team.carManager.carCount = 3;
                    //team.mainDriverCount = 3;
                    System.Console.WriteLine("Team[" + index + "] = " + team.name);
                }
                break;

            case GameState.Type.PracticePreSession:
            case GameState.Type.QualifyingPreSession:
            case GameState.Type.RacePreSession:
            case GameState.Type.SkipSession:
                game.stateInfo.GoToNextState();
                break;

            case GameState.Type.PracticePostSession:
            case GameState.Type.QualifyingPostSession:
                app.gameStateManager.OnContinueButton();
                break;

            case GameState.Type.RaceGrid:
            case GameState.Type.Race:
                ConsoleCommands.useAIForDrivers = true;
                FreeRoamCamera camera = app.cameraManager.gameCamera.freeRoamCamera;
                camera.GetType()
                    .GetField("minZoom", BindingFlags.NonPublic | BindingFlags.Instance)
                    .SetValue(camera, 20f);
                camera.GetType()
                    .GetField("maxZoom", BindingFlags.NonPublic | BindingFlags.Instance)
                    .SetValue(camera, 2000f);
                camera.GetType()
                    .GetField("minRotationX", BindingFlags.NonPublic | BindingFlags.Instance)
                    .SetValue(camera, 2f);
                camera.GetType()
                    .GetField("maxRotationX", BindingFlags.NonPublic | BindingFlags.Instance)
                    .SetValue(camera, 88f);
                float[,] speedMultipliers = game.time.GetType()
                    .GetField("speedMultipliers", BindingFlags.NonPublic | BindingFlags.Instance)
                    .GetValue(game.time) as float[,];
                speedMultipliers[2, 2] = 10f;
                break;
        }
    }
};

So finally got around to trying this out...
I have decided to try and test your code out as a 'proof of concept' example, as I don't have any real experience with dll or Visual Studio really.

Made a new project, Tick
Copied the code above in, Tick.
Added the references (Assembly-CSharp, firstpass, UE and UEUI) Tick
Build (tick - 1 succeeded, references didn't create any errors)
Moved the DLL to where I had a copy of your injector, and renamed (to replace) it to mm2h.dll, Tick

But I am not seeing any increased zoom in the game with the injector running.

I am now in debug now going over everything working out where I have gone wrong with the basic build. If anyone can see something obvious I have missed, let me know - I can easily spend hours overlooking something... I can be that stupid :)


Edit: oooh, just seen "/nostdlib my_file.cs". No idea how I get this to happen. Seems like the tickbox I used in the 2010 version for it has been removed.

it looks like its being done through a command line, but I don't see any csc.exe with visual Studio, attempts to run it through the csc in Microsoft.NET just gives me errors

Code:
csc "C:\Program Files (x86)\Steam\SteamApps\common\Motorsport Manager\MM_Data\Managed\Assembly-CSharp.dll" "C:\Program Files (x86)\Steam\SteamApps\common\Motorsport Manager\MM_Data\Managed\Assembly-CSharp-firstpass.dll" "C:\Program Files (x86)\Steam\SteamApps\common\Motorsport Manager\MM_Data\Managed\UnityEngine.dll" "C:\Program Files (x86)\Steam\SteamApps\common\Motorsport Manager\MM_Data\Managed\UnityEngine.UI.dll" "C:\Program Files (x86)\Steam\SteamApps\common\Motorsport Manager\MM_Data\Managed\mscorlib.dll" /nostdlib Class1.cs
Lots of "It's a binary file, not a text file" and Mscorbil.dll couldn't be opened.

Clueless now... I'm a mathematician rather then a programming language/syntax person. I can happily For Loop and Conditional statement darn near anything - but making one component of a computer communicate to another - not so much.

All help appreciated.
As soon as I can get a working 'process', I should be able to begin testing and modifying the gameplay
 
Last edited:

I think I forgot to mention you need to specify compiling it as a library, not an application. The command line switch is /target:library. I have no idea how to navigate Visual Studio, I've always just invoked the compiler from the command line. For this project I just have a simple build script written in python:

Code:
import subprocess


def build():
    with subprocess.Popen(["cl", "/nologo", "mm2main.c"]) as p:
        if p.wait():
            return

    with subprocess.Popen(["cl", "/nologo", "/Od", "/Z7", "/LD", "mm2injector.c"]) as p:
        if p.wait():
            return

    assemblies = [
        r"/r:E:\Motorsport Manager\MM_Data\Managed\Assembly-CSharp.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\Assembly-CSharp-firstpass.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\UnityEngine.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\UnityEngine.UI.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\mscorlib.dll",
    ]

    with subprocess.Popen(["csc", "/nologo", "/nostdlib", "/target:library", "mm2h.cs"] + assemblies) as p:
        if p.wait():
            return


if __name__ == "__main__":
    build()

If you get further trouble you might have to compile it for 64 bit architectures, but I think .NET IL is agnostic to that? I don't know how to do that via Visual Studio, but I have a little batch file called "env-msvc.bat" that I keep in a directory in my PATH env. The contents:
Code:
@call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %*
Then I can write "env-msvc amd64" on the command line for setting up my environment for 64 bit compilation, alternatively "env-msvc x86" for 32 bit. Doing it this way keeps your environment clean, I have to explicitly ask for msvc or clang or mingw etc that way they don't step on each others toes.

You may also want to add a method called OnGUI that can render gui elements to the screen. This will be helpful for testing/debuging/figuring out if it's even running.

Code:
using UnityEngine;
using UnityEngine.UI;

public class mm2hack : MonoBehaviour {
    //all your other methods should be included here like init etc

    void OnGUI() {
        GUI.Label(new Rect(0, 0, Screen.width, Screen.height), "mm2hack is running");
    }
}

I've spent numerous hours wrangling with mono but man is it stubborn. It's extremely difficult to load an assembly twice, updating all the classes/methods/etc in the process. I'm still trying to make work, because in order to test any changes you've made to the payload you have to close and relaunch the game :(
 
I think I forgot to mention you need to specify compiling it as a library, not an application. The command line switch is /target:library. I have no idea how to navigate Visual Studio, I've always just invoked the compiler from the command line. For this project I just have a simple build script written in python:

Code:
import subprocess


def build():
    with subprocess.Popen(["cl", "/nologo", "mm2main.c"]) as p:
        if p.wait():
            return

    with subprocess.Popen(["cl", "/nologo", "/Od", "/Z7", "/LD", "mm2injector.c"]) as p:
        if p.wait():
            return

    assemblies = [
        r"/r:E:\Motorsport Manager\MM_Data\Managed\Assembly-CSharp.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\Assembly-CSharp-firstpass.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\UnityEngine.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\UnityEngine.UI.dll",
        r"/r:E:\Motorsport Manager\MM_Data\Managed\mscorlib.dll",
    ]

    with subprocess.Popen(["csc", "/nologo", "/nostdlib", "/target:library", "mm2h.cs"] + assemblies) as p:
        if p.wait():
            return


if __name__ == "__main__":
    build()

If you get further trouble you might have to compile it for 64 bit architectures, but I think .NET IL is agnostic to that? I don't know how to do that via Visual Studio, but I have a little batch file called "env-msvc.bat" that I keep in a directory in my PATH env. The contents:
Code:
@call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %*
Then I can write "env-msvc amd64" on the command line for setting up my environment for 64 bit compilation, alternatively "env-msvc x86" for 32 bit. Doing it this way keeps your environment clean, I have to explicitly ask for msvc or clang or mingw etc that way they don't step on each others toes.

You may also want to add a method called OnGUI that can render gui elements to the screen. This will be helpful for testing/debuging/figuring out if it's even running.

Code:
using UnityEngine;
using UnityEngine.UI;

public class mm2hack : MonoBehaviour {
    //all your other methods should be included here like init etc

    void OnGUI() {
        GUI.Label(new Rect(0, 0, Screen.width, Screen.height), "mm2hack is running");
    }
}

I've spent numerous hours wrangling with mono but man is it stubborn. It's extremely difficult to load an assembly twice, updating all the classes/methods/etc in the process. I'm still trying to make work, because in order to test any changes you've made to the payload you have to close and relaunch the game :(

I've managed to shuffle things around, Moving the dlls into the directory with the project folder for Visual

csc /r:Assembly-CSharp.dll /r:Assembly-CSharp-firstpass.dll /r:UnityEngine.dll /r:UnityEngine.UI.dll /r:mscorlib.dll /nostdlib /target:library /out:mm2h.dll Class1.cs

Works better now, but I have a Class1.cs(45,17): Error CS1501: No overload for method 'loadtoTitleScreen' takes '0' arguments
Assembly-CShard.dll: (Location of symbol related to previous error)

I think that's progress, as on line 45, LoadtoTitleScreen is written. I am wondering if this is something in the code, or something I'm missing when trying to complile

Still working on it.

In terms of testing, I just do something really obvious. Change some variable / line massively but not intrusively. Or I could keep in the Zoom Camera code you have, as that's a rather easy to tell way that the injector is on or off :)

... that camera code works for you, right?
 
Last edited:
Close, but definatly weird things....

The Camera code gives me that one error...

I just tried to copy your 'AI turn on' code in this, and it throws up several errors on compile.

Line 38, cannot convert int to string
Line 71, UnityEngine.Rect.height.get: cannot explicitly call operator or accessor

Who knew compiling some code we know works would cause me so much difficulty :p


edit: Actually, back to square 1... I removed all the AI code and just settled for the Calender moving quickly section... it compiled fine, but wasn't injected correctly, as the calender moved at non rapid pace like it normally does - I suck at this

Opening them all up in ILSpy, your code as a v4.0.0.0 Corlib, mine has two! a v2..5.0 and a 2.0.0.0

Having tried to run and edit your python script, I am still not getting anywhere. :( Think I'll call it a day, been at this for about 6 hours now and not gotten anywhere. You're a magician crash for getting an injector working.

Edit (6 minutes after the last)

....
....
....
....
So, I decide to reset everything, put the full AI code back into Visual, save it. Try to compile it to be sure I can pick up where I left off...

And it compiles fine - no errors... I start up the game and the injector, and its working, fully...

I have no idea why. My process is exactly the same as it was hours ago. But it works...





Okay. It seems to be working well. Now just have to learn how Unity codes :p Value changes seem simple enough. Got to learn what "get" means and how to overwrite some 'get' variables. or work out how to change some read only variables. Next stop, change a background so that designing a part takes longer (so we can't always outpace the AI).

Gotta learn how to chance anything other then static variables. Is there anything that cannot be easily altered Crashed (before I spend 5 hours trying to alter something that cannot be altered?
 
Last edited:
Good to hear you got it working!

FreeRoamCamera has a field written as "private float minZoom = 100f;" the important bit is private. Only FreeRoamCamera is supposed to modify this value, but you can dance around that with the GetType().GetField().SetValue() gymnastics. If it's public you can set the value normally like GameConstants.minTweetValue = some_num. You'll have to do an internet search for how to call private methods / functions.

Right now you can set values and call stuff, but you'll eventually probably want to change what a function in some class does. I have no answer for how to do that, but I've seen some examples on stackoverflow for how it could be done. The trouble here is that if the game gets updated you might have to change your replaced function to reflect the changes.
 
I don't believe so. Not if you're just looking to adjust information in the Assets. A 'copy' of the Assets is taken into your save file, and is updated live in there.
You can theoretically edit the save game file, but this is more awkward then editing the database.

Fore those of you getting 'Dots' instead of your changes... are are you importing/exporting... are you using the Dumps/Exports buttons... or the 'Pluggin' button...

I use the Plugin Button. Do I need to use the Dump Button instead?
 
Thanks, but what are those random numbers for?

EDIT: I found information on that. But now I'm suck at importing it again. It doesn't change.

EDIT of the EDIT: I found that out as well. But now I have the same problem as someone had already...the Drivers are all named: "." now.
I noticed that you need to use Notepad++. I don't have any problems after using that :)
 
Good to hear you got it working!

FreeRoamCamera has a field written as "private float minZoom = 100f;" the important bit is private. Only FreeRoamCamera is supposed to modify this value, but you can dance around that with the GetType().GetField().SetValue() gymnastics. If it's public you can set the value normally like GameConstants.minTweetValue = some_num. You'll have to do an internet search for how to call private methods / functions.

Right now you can set values and call stuff, but you'll eventually probably want to change what a function in some class does. I have no answer for how to do that, but I've seen some examples on stackoverflow for how it could be done. The trouble here is that if the game gets updated you might have to change your replaced function to reflect the changes.

I'll take it easy for now. Still making sure I can get everything working well.

Your AI script is doing fine, but while I can compile your camera/speed script in one of the posts above, I can't seem to get any zoom or speed changes (despite min now being 20f and max being 2000f (from 100f and 220f). Looks like you're turning on the AI to race for you too, but that doesn't come across either.

Comparing the two, the Camera file doesn't make any use of the word 'Private' before the void, unlike the AI file (but I see that ILSpy inserts those. Tried fiddling with that, but that's not making a difference. Camera file doesn't have a toggle switch as far as I can see. Gotta try to understand why it isn't working, before I try altering any other private stats. If I get stuck, I may have to ask you to upload your current file, so I can see what the compiler should be altering, and what is isn't.

I am getting an "Invoke Failed" message now having restarted steam (it appears that the injector wasn't working at all before this, afterwards, invoke failed is as far as I get, and forces MM to crash)

The good news is, I'm learning lots.

Where is the save game file to be found?
\user\(user)\AppData\LocalLow\Playsport Games\Motorsport Manager\Cloud\Saves

AppData is hidden by default, so change folder settings if thats not visible.
 
Last edited:
I am getting an "Invoke Failed" message now having restarted steam (it appears that the injector wasn't working at all before this, afterwards, invoke failed is as far as I get, and forces MM to crash)

I think on that version of the injector you need to return an int in init(). Try this minimal sample:

Code:
using UnityEngine;

public class mm2hack : MonoBehaviour {
    public static int init() {
        GameObject obj = new GameObject();
        obj.AddComponent<mm2hack>();
        return 0; // 0 for success
    }

    public void OnGUI() {
        GUI.Label(new Rect(0, 0, 100, 100), "mm2hack loaded");
    }
}
 
I think on that version of the injector you need to return an int in init(). Try this minimal sample:

Code:
using UnityEngine;

public class mm2hack : MonoBehaviour {
    public static int init() {
        GameObject obj = new GameObject();
        obj.AddComponent<mm2hack>();
        return 0; // 0 for success
    }

    public void OnGUI() {
        GUI.Label(new Rect(0, 0, 100, 100), "mm2hack loaded");
    }
}

:) Will give that a go later - off to go get babygirl from school and go to a play area.

I was able to actually insert the Camera Zoom aspect of the code into the Original AI Driving code from the original mod, and that worked great (Its actually quite cool having a real close up view. I am sure many people may take issue with the resolution that close to the car, or fact the car wheels are not spinning, but it doesn't bother me the least). It's a working Type,Field,Set example now, and I can build off that.

Oddly, the Speed increase cause a crash, but that was probbaly me not coding that right when transfering it. I deleted it to see the zoom levels work wonderfiully, so I am quite happy :D

I'll give that change a shot later

Thanks for all your help crashed :D
 
I think on that version of the injector you need to return an int in init(). Try this minimal sample:

Code:
using UnityEngine;

public class mm2hack : MonoBehaviour {
    public static int init() {
        GameObject obj = new GameObject();
        obj.AddComponent<mm2hack>();
        return 0; // 0 for success
    }

    public void OnGUI() {
        GUI.Label(new Rect(0, 0, 100, 100), "mm2hack loaded");
    }
}

Yeah, the original script I have working is the Static int init, where as the camera one has stactic void init....

Not a problem, I am happy using the released injector and it's methods. I moved over the 'camera' tweaks so I know thats working fine. I get a crash just as the race stats with the

float[,] speedMultipliers = game.time.GetType()
.GetField("speedMultipliers", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(game.time) as float[,];
speedMultipliers[2, 2] = 10f;

Section. Changing the game states also results in a 'less unexpected' crash. Either it doesn't like arrays, or its plainly conflicting with the original 3.75f value and the game gets confused and dies.

Already began some modifications and tweaks in other areas. Its fun to be past the 'I have no idea whats going' phase and enter the 'ooooh, i can change a number and see its effects'.

Still trying to understand where you derived the line "FreeRoamCamera camera = app.cameraManager.gameCamera.freeRoamCamera;" from.



// Side note: Autorotate seems to be a camera function... looks like it's actually possible to have a follow cam on one of your cars afterall
 
Last edited:

Latest News

Do you prefer licensed hardware?

  • Yes for me it is vital

  • Yes, but only if it's a manufacturer I like

  • Yes, but only if the price is right

  • No, a generic wheel is fine

  • No, I would be ok with a replica


Results are only viewable after voting.
Back
Top