Modding in C# feels incredibly boring and tedious.

Users who are viewing this thread

To add more detail here:

LINQ is a performance issue because it tends to create a lot of short-lived objects that need to be garbage collected. In a tight loop where the loop needs to run as quickly as possible on the CPU, the work done collecting that garbage can create more work than the time spent on the loop itself. Unfortunately for games, the game loop itself is a tight loop!

I use ReSharper's Heap Allocation Viewer plugin when I'm writing C# code to keep track of stuff like that and make sure I'm only using LINQ in code that doesn't run every frame, e.g. initialization or in response to loading a new resource at run-time for debugging.




That code you're looking at is decompiled, meaning that an algorithm has turned something that isn't human readable into what you're seeing. It's not necessarily what the original code looked like.

To use another example, consider

C#:
public override float GetScoreOfKingdomToGetClan(Kingdom kingdom, Clan clan)
{
    float num1 = Math.Min(2f, Math.Max(0.33f, (float) (1.0 + 0.0199999995529652 * (double) FactionManager.GetRelationBetweenClans(kingdom.RulingClan, clan))));
    float num2 = (float) (1.0 + (kingdom.Culture == clan.Culture ? 1.0 : 0.0));
    int num3 = clan.CommanderHeroes != null ? clan.CommanderHeroes.Count<Hero>() : 0;
    float num4 = (float) (((double) clan.TotalStrength + 150.0 * (double) num3) * 10.0);
    float powerRatioToEnemies = FactionHelper.GetPowerRatioToEnemies(kingdom);
    float reliabilityConstant = HeroHelper.CalculateReliabilityConstant(clan.Leader, 1f);
    float num5 = 1f / Math.Max(0.4f, Math.Min(2.5f, (float) Math.Sqrt((double) powerRatioToEnemies)));
    float num6 = num4 * num5;
    return (clan.CalculateSettlementValue(kingdom) * 0.1f + num6) * num1 * num2 * reliabilityConstant;
}

It's possible that this code is fairly close to what was in the game's original code.

It's also possible that it actually looked something more like:

C#:
public override float GetScoreOfKingdomToGetClan(Kingdom kingdom, Clan clan)
{
    var numClanHeroes = clan.CommanderHeroes.Count;
    var existingHeroesInKingdom = GetNumberOfHeroesInKingdom(kingdom);

    if ((numClanHeroes == 0) ||
        (existingHeroesInKingdom == 0))
    {
        return -1E+08f;
    }

    // Start with a basis of 0.
    var valueProposition = 0.0f;

    // Add value for the settlements the clan will bring to the kingdom, and the adjusted clan strength.
    // Adjusted clan strength is the clan strength ~ how badly the kingdom needs allies.
    var clanStrength = GetClanStrength(clan, numClanHeroes);
    var powerRatioToEnemies = FactionHelper.GetPowerRatioToEnemies(kingdom);
    var howBadlyDoesThisKingdomNeedSupport = 1f / Clamp(powerRatioToEnemies > 1 ? (float)Math.Sqrt(powerRatioToEnemies) : powerRatioToEnemies, 0.1f, 2.5f);
    var adjustedClanStrength = clanStrength * howBadlyDoesThisKingdomNeedSupport;
    valueProposition += clan.CalculateSettlementValue(kingdom) * 0.1f + adjustedClanStrength;

    // Increase the value for happier relations.
    var relationMult = Clamp(1.0f + 0.02f * FactionManager.GetRelationBetweenClans(kingdom.RulingClan, clan), 0.33f, 2f);
    valueProposition *= relationMult;

    // Increase the value for the same culture.
    var sameCultureMult = (float)(1.0 + (kingdom.Culture == clan.Culture ? 1.0 : 0.0));
    valueProposition *= sameCultureMult;

    // Increase the value if the lord is known to be honorable.
    var reliabilityConstant = HeroHelper.CalculateReliabilityConstant(clan.Leader);
    valueProposition *= reliabilityConstant;

    return valueProposition;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Clamp(float value, float min, float max)
{
    return Math.Min(max, Math.Max(min, value));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetNumberOfHeroesInKingdom(Kingdom kingdom)
{
    var numHeroes = 0;
    foreach (var clan in kingdom.Clans)
    {
        if (clan.IsMinorFaction && clan != Clan.PlayerClan)
        {
            continue;
        }

        numHeroes += clan.CommanderHeroes.Count;
    }
    return numHeroes;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float GetClanStrength(Clan clan, int numClanHeroes)
{
    return (clan.TotalStrength + 150.0f * numClanHeroes) * 10.0f;
}

The decompiled code could have lost variable names, or added new intermediate values. There may be function calls missing because they were inlined. There won't be any comments. The spacing and separation won't look like a human did it.



This function CalculateStrikeMagnitudeForSwing that you're looking at is actually used by a class called DefaultStrikeMagnitudeModel. While the function isn't immediately modifiable, the class can be entirely replaced. Once you do so, everything else in the game will use your class instead. That is incredibly powerful. It's as easy as creating a new class that inherits from DefaultStrikeMagnitudeModel and overriding whatever methods you want. You could replace the swing calculation, or the thrust calculation, or anything.



It is common in game development to write your code in one language (e.g. C++, Java, or C#) and then support reading from a data file at run-time (XML, JSON, other custom text formats) so that you can quickly tweak or iterate on specific numbers, including multipliers.



C# has a syntax much like all other typed languages. It's comparable to Java, C, C++, TypeScript, and others. If you've only used a dynamic language like Python or Lua I can understand why it might be surprising to you.



The architecture of the game engine seems fine to me. I've only made two mods so far but in both cases I was pleasantly surprised by the organization of the code and thought given to decoupling components from each other. It's been very easy to reach in and make changes by swapping out a model or an XML file and the game's care given to the mod-focused architecture is what makes that possible, e.g. the loop that finds game models by searching backwards through the list of all the models grabbed from modules during init, or the use of reflection for the data-binding between the UI schema in XML and the view models in the game.

I think what's happening here is that for many people this is their first experience with a large, complex codebase. With a dedicated sandbox for game scripting, that complexity is hidden from you. Unfortunately, the only way to hide that complexity is by making assumptions about what modders will want or need for you. One way to avoid that is to "dog-food" your system, meaning to use the system you expose for making mods to make your game. That's what TaleWorlds has done here. The core game is a module loader. The native game is a module. You'll get truly incredible mods from this design.

It'll get easier to learn as you see more mods demonstrate how to replace or modify different parts of the code.

^^^
 
Replacing models and other classes to make mods may be the "right" way to do things from a programming architecture perspective, but it's absolutely terrible for modding. I feel like people aren't thinking this through *at all*.

So you replaced one of the default models with your own, all fine and good, right?

Except now your mod is completely incompatible with any other mod that wants to alter anything in that model as well.

If you instead patched the one or two fields or methods you're interested in, unless other mods directly interfered with those exact fields or methods, it'd be compatible.

Doing everything the "right" way is going to rapidly result in mod compatibility going into the toilet, all in pursuit of "clean" code, which matters exactly ZEEEERRRRROOOOO to mod users.
 
If you instead patched the one or two fields or methods you're interested in, unless other mods directly interfered with those exact fields or methods, it'd be compatible.

you should read the above post again, as you didnt understand how multiple mods will work (including the base game itself) with each other.
 
Replacing models and other classes to make mods may be the "right" way to do things from a programming architecture perspective, but it's absolutely terrible for modding. I feel like people aren't thinking this through *at all*.

So you replaced one of the default models with your own, all fine and good, right?

Except now your mod is completely incompatible with any other mod that wants to alter anything in that model as well.

If you instead patched the one or two fields or methods you're interested in, unless other mods directly interfered with those exact fields or methods, it'd be compatible.

Doing everything the "right" way is going to rapidly result in mod compatibility going into the toilet, all in pursuit of "clean" code, which matters exactly ZEEEERRRRROOOOO to mod users.
Mod support is a spectrum.

On one end of the spectrum, you have limited, sandboxed access to the game. You can add new items, but you can't replace existing items. Or -- if you support replacing or adding items, you can only do so one mod at a time, in the case of games that can't resolve differences between mods, like Warband.

Some games limit mods to pure resources & data with no support for scripting. Other games like Skyrim provide scripting through events that mods can register a callback on, e.g. every game tick, or when something interesting happens like a hit is detected, or a new room is entered.

I'm going through this list of definitions because I want to express how much mod support Bannerlord has before talking about Harmony and other methods that take advantage of C#'s rich runtime support for reflection.

What can we do in Bannerlord, that we know of, several days after early access release?
  • Modules can replace resources defined by any module loaded before us, including the native game.
  • Modules can add to the XML, or replace the XML, of any module loaded before us, including the native game.
  • The game tries to resolve differences between modules by load order.
  • The game provides events that mods can register a callback on so that they can be called by the game engine to run their own code, e.g. the various OnTick events.
  • The game provides attributes that mods can use to register their classes for the reflection that the game provides, e.g. the encyclopedia or save format definitions.
  • The game code is divided into hundreds of small, discrete models with a handful of methods. Each model can be overridden or replaced in its entirely, by any mod, without conflict.
  • Game entities are defined in the XML modifiable above.
  • The UI code is divided into hundreds of small, discrete view models. These use reflection and can be replaced or overridden.
  • The UI layout is defined in the XML modifiable above.
  • It would be possible for multiple mods to override the same model if the game exposed a method that would find the last model of a given type in the list of loaded models before our mod, when we're loading our mod.
And then there's Harmony. Not all languages are as easy to monkey patch as C#. There's no equivalent, easy-to-use library for monkey-patching C++ that I'm aware of, even though C++ supports run-time type information too. Java's reflection is similarly crippled, although Slay the Spire modders seem to make it work. There's a reason why Unity uses C# as well and Godot is adding support for it too.

I'm not saying mod support is perfect.

There are events that would be helpful that I don't see in the DLLs anywhere.
There are functions and members that are private that should be public.
There are functions that aren't marked as virtual.
There are XML files that we can't seem to make work except as part of the native module.
Loading & saving custom definitions can cause a crash after a mod is removed.

But it should be clear to anyone who understands system design that this game was designed with the intent of supporting the modding community. For y'all to argue that the engine / architecture is terrible for modding is completely unfounded (refer back to all of the design choices in the bulleted list above).
 
Mod support is a spectrum.

On one end of the spectrum, you have limited, sandboxed access to the game. You can add new items, but you can't replace existing items. Or -- if you support replacing or adding items, you can only do so one mod at a time, in the case of games that can't resolve differences between mods, like Warband.

Some games limit mods to pure resources & data with no support for scripting. Other games like Skyrim provide scripting through events that mods can register a callback on, e.g. every game tick, or when something interesting happens like a hit is detected, or a new room is entered.

I'm going through this list of definitions because I want to express how much mod support Bannerlord has before talking about Harmony and other methods that take advantage of C#'s rich runtime support for reflection.

What can we do in Bannerlord, that we know of, several days after early access release?
  • Modules can replace resources defined by any module loaded before us, including the native game.
  • Modules can add to the XML, or replace the XML, of any module loaded before us, including the native game.
  • The game tries to resolve differences between modules by load order.
  • The game provides events that mods can register a callback on so that they can be called by the game engine to run their own code, e.g. the various OnTick events.
  • The game provides attributes that mods can use to register their classes for the reflection that the game provides, e.g. the encyclopedia or save format definitions.
  • The game code is divided into hundreds of small, discrete models with a handful of methods. Each model can be overridden or replaced in its entirely, by any mod, without conflict.
  • Game entities are defined in the XML modifiable above.
  • The UI code is divided into hundreds of small, discrete view models. These use reflection and can be replaced or overridden.
  • The UI layout is defined in the XML modifiable above.
  • It would be possible for multiple mods to override the same model if the game exposed a method that would find the last model of a given type in the list of loaded models before our mod, when we're loading our mod.
And then there's Harmony. Not all languages are as easy to monkey patch as C#. There's no equivalent, easy-to-use library for monkey-patching C++ that I'm aware of, even though C++ supports run-time type information too. Java's reflection is similarly crippled, although Slay the Spire modders seem to make it work. There's a reason why Unity uses C# as well and Godot is adding support for it too.

I'm not saying mod support is perfect.

There are events that would be helpful that I don't see in the DLLs anywhere.
There are functions and members that are private that should be public.
There are functions that aren't marked as virtual.
There are XML files that we can't seem to make work except as part of the native module.
Loading & saving custom definitions can cause a crash after a mod is removed.

But it should be clear to anyone who understands system design that this game was designed with the intent of supporting the modding community. For y'all to argue that the engine / architecture is terrible for modding is completely unfounded (refer back to all of the design choices in the bulleted list above).

I have been trying to explain this everyone... Thank you so much!
 
I believe that a lot of the old code from Native Warband is actually still in the game. Currently everyone is mostly using hacks and injections to modify the game but I'm sure old native code is still there; the way that it is implemented is just different. So I'm waiting until they release official mod tools to do so. The same dialogue for the Tavern Mercenaries is just like the one used in Warband.
 
Yo @Nunu, I don't know if you do it on purpose but you give off a very hostile attitude to people who are not trying to attack you in any way. you are frustrated and its completely understandable but telling people off because they offer a different perspective is frankly the kind off knee jerk reaction that is not going to help you solve your problem.

learning C# is a pain in the ass if you have to learn it from scratch and also while trying to reverse engineer code from a game that has no support whatsoever. If you really want to mod right now, you'll have to learn C#. I'd advice you to take it out of the bannerlord context and learn it in a more user friendly environment, download Unity engine, go work on some tutorials you'll get a grip on the syntax and as you go you'll get a better understanding of why C# works the way it does, who knows you might even start to like it.

As a game devellopment decision its probably the smartest thing they could have done, its a versatile language that is very optimisable, while being accessible enough for new people (unlike C++, or even C). Every language has its purpose, but the current state of game devellopment doesn't really have a use for surface level languages like Python, you might not like having to do tedious work like declaring if something is a float or an int, but you doing it saves CPU power and memory because your computer doesnt have to do it. and in a micro level yes it feels unnecesary however on a macro, big project level its a make it or break it issue.

Still all the respect for the fact that you started learning this even though its frustrating, I personally got stuck when I started because I kept ****ing up with brackets and then dropped programming for years. which is a shame because I use it pretty regularly nowadays.

This, 100 times.

Right now you are basically trying to reverse engineer the game itself while also teaching yourself a new language. So you are trying to do something that really isn't trivial even for someone with experience in the language, and trying to learn the language on top of that. No wonder you are frustrated! Mod support is going to be much better than this, we just have to be patient.

Also, something that was mentioned already but it bears repeting: keep in mind that the decompiled code is not written the way it is supposed to be written. It is just generated code that works but is ugly as sin.
 
Replacing models and other classes to make mods may be the "right" way to do things from a programming architecture perspective, but it's absolutely terrible for modding. I feel like people aren't thinking this through *at all*.

So you replaced one of the default models with your own, all fine and good, right?

Except now your mod is completely incompatible with any other mod that wants to alter anything in that model as well.

If you instead patched the one or two fields or methods you're interested in, unless other mods directly interfered with those exact fields or methods, it'd be compatible.

Doing everything the "right" way is going to rapidly result in mod compatibility going into the toilet, all in pursuit of "clean" code, which matters exactly ZEEEERRRRROOOOO to mod users.

I'm pretty sure you don't actually have to *replace* the default model, if I understand the system correctly you can derive from a model's class and basically create your own local version of that model without interfering with anything upstream. Same goes for (supposedly) almost any class of objects within the game.

This will hopefully lead to a much much better compatibility between mods, as every mod would be able to use inheiratance to implement any modifications to the core functions and classes without actually changing them. This is extremely powerful and it's really cool that they went full OOP with this. I'm just waiting for the proper toolkit support, the possibilities are mindblowing.

By the looks of it Bannerlord will be sitting somewhere between an extremely moddable game (eg Skyrim) and a full-blown game engine (like Unity) in terms of modability. If everything goes right, the modding scene will be like nothing before in scale.
 
First of all, I can say pretty confidently that they're not going to change how mods are intended to be developed this late in the game. People complaining will just have to cut their losses and go home. It took them 8 years to get us this far.

The reality is that Bannerlord is x1000 better for modding because we now have a *real* programming language to do it in, with access to a significant amount of in-game hooks. You could literally write a mod that lets Twitch play Bannerlord (though, it would be a lot of work).

c# is a really easy language. It's analogous to Java and other 'modern' OOP languages. Yes, it's more verbose than Python. No, it's not perfect. But it's waaaaaay better than what we could have gotten. And because it's a 'hardcore' language, we can actually build libraries & nuget packages to make other developer's lives easier.

Actually, that's the trade off: With power, comes responsibility. And now you have a higher barrier of entry to making mods.

This may suck for some people, but in the end it will give us more comprehensive mods.

Right now though, it seems like everything isn't right. I'm expecting and hoping they will address the issues with the SDK in the coming year. There are a lot of issues with it, the least of which being a total lack of documentation. It is not yet very intuitive.
 
[...] this game was designed with the intent of supporting the modding community. For y'all to argue that the engine / architecture is terrible for modding is completely unfounded [...]

Absolutely!

Finally working towards a standard language across other mod-able games, one language to rule them all!

Creating classes and assets that could be ported between other c# moddable games, with a few edits, would be ace too! Space&Mount Engineers mod or something...
 
well Unity uses it, so its not like its not used by amateur/hobbyists, modders and semi-pro game developers too

True but simply installing Unity does a lot of work for you and there are tons of great video tutorials available. If the need for Harmony goes away then it's much more practical to learn with Bannerlord. Unity is actually a great example of how much better this can be.

Choose the smart way and start with a hello world tutorial is all I'm saying.
 
Replacing models and other classes to make mods may be the "right" way to do things from a programming architecture perspective, but it's absolutely terrible for modding. I feel like people aren't thinking this through *at all*.

So you replaced one of the default models with your own, all fine and good, right?

Except now your mod is completely incompatible with any other mod that wants to alter anything in that model as well.

If you instead patched the one or two fields or methods you're interested in, unless other mods directly interfered with those exact fields or methods, it'd be compatible.

Doing everything the "right" way is going to rapidly result in mod compatibility going into the toilet, all in pursuit of "clean" code, which matters exactly ZEEEERRRRROOOOO to mod users.

I think you actually do not understand how the game and mod supports works, and you do not either understand how Harmony works. People should spend a few minutes before using it on the website reading what it is and maybe also read wikipedia article on monkeypatching and why it's called this way.
It's someday now i'm trying to ease people mod the game without using Harmony and i encounter quite some hostility about it, like if telling people "Be careful with Harmony it's a dangerous tool" was hurting their deep conviction to the point they would enter a deny state and attack you.

Harmony is like the good old 'goto' in basic and first programming languages, powerful, fast easy to use, and god i loved goto instruction, but sadly it was removed from all the modern languages because people start abusing of it for everything, ESPECIALLY for patching that leaded to spaghetti code what might be the ancestor of the actually monkey patching. So people simply decided to remove it, so people would stop using it and stop doing trashy code but use their brain. That's a shame cause sometime in those modern languages you have case you can't solve without a goto, at least not without stupid instructions like "Do{}while(true)" that is a hidden Goto. But as people abused and every app was becoming a mess something had to be done. I really feel the same currently with Harmony on M&B2. Yes it's way easier to patch anything with Harmony, of course it is. Harmony is designed exactly for this, and it's really cleverly designed and it makes everything easier to mod. But most of the time this is lazy developers not wanting to learn and simply wanting to lazy design things. That's fun cause afterward they complain about TW lazy code, but they do the same in worst!

Most of the mods i have seen using Harmony lately could be done easily with simply a behavior and a submodule loader, with actually almost no incompatibility issue and allowing people to be aware you changed something to the game, unlike Harmony.
Cause you are speaking about the compatibility of mods and how Harmony is the holy grail, ok. Now i make a mod that takes the result from a method that as been patched with Harmony, the results are totally out of what is expected and makes my mod crash. How am i supposed to deal with it? I will never know the method have been modified cause it's signature is still the same. When looking the logs if there are, i will see incorrect value without knowing if that's coming from the base game or a mod. Happy debugging. Are you so sure it makes things more compatible? When you extend a class from another, modifications made to the mother class are passed to the child, that do not happen with Harmony either. Still sure it's more compatible?

Seriously, game support for mod isn't perfect but it's the way TW made it. Use their tools, improve them with community features if you wish, fill the documentation, makes requests for changes to be done to the engine and keep Harmony for the specific cases of static methods and some private classes/methods that can't be worked around, but stop using it everywhere because "It's easier" and then use the excuse of "It's TW's fault", if you want to go the quick and dirty way, do not blame someone else. If something need to be changed, but each time you asked for the change to TW you have a horde of Harmony fanboy posting to tell "Use Harmony it works" why would TW even bother changing it and makes it the way it should be for everyone to enjoy the game?



I'm pretty sure you don't actually have to *replace* the default model, if I understand the system correctly you can derive from a model's class and basically create your own local version of that model without interfering with anything upstream. Same goes for (supposedly) almost any class of objects within the game.

This will hopefully lead to a much much better compatibility between mods, as every mod would be able to use inheiratance to implement any modifications to the core functions and classes without actually changing them. This is extremely powerful and it's really cool that they went full OOP with this. I'm just waiting for the proper toolkit support, the possibilities are mindblowing.

By the looks of it Bannerlord will be sitting somewhere between an extremely moddable game (eg Skyrim) and a full-blown game engine (like Unity) in terms of modability. If everything goes right, the modding scene will be like nothing before in scale.
If you do your own version, you then shouldn't extend it from the default class and it have not really any point doing so as you can use any class and any method of your name that is linked to your own behavior. But if you want to replace a model, you replace it for everyone, it's one part of the current game limitations until we find a way around.
 
Last edited:
Except now your mod is completely incompatible with any other mod that wants to alter anything in that model as well.

I think it is true for small mods. However, for something like Brytenwalda... really, they redesigned the whole experience from the ground up. It does not have to be compatible with anything. Because it is like a whole new game. It is more like if their users want to use another mod, they will merge it into it. Or other mod makers can submod it and make patches.

Personally I have struggled enough with mod compatibility in Skyrim and would much rather use one of these really big mods at one time with which have everything and the kitchen sink added to them, and it is tested well that they work well together. I would rather not use multiple mods.
 
lulwut? For me, the setup with Bannerlord is several billion orders of magnitude better than Warband. I looked into Warband modding, but decided it wasn't worth the effort. It seemed to be an absolute sh**show.

Modding Bannerlord is an absolute dream. I have near-zero experience with modding and have already made three different mods that alter very different parts of the game in the past couple of days.

There's alot that needs improvement - defaulting to "private" instead of "protected", things that could be virtual that aren't, etc, but in general it's kinda amazing.

I also don't understand your hate for C# in particular - it's pretty widely regarded as one of the handful of best designed languages in the history of computer science. None of your complaints were actually accurate - but that's because you haven't learned it yet. Blaming the language because you haven't bothered to learn it seems a bit daft if I'm honest.

Again, there's LOTS I want to see improved (please do a Find & Replace All "private" with "protected" at the very least), but I can't think of any other game which I've been able to go from "hmm, I wonder if I could mod this" to "ok, now how do I publish my mods to Nexus" (still working on that) in a couple of nights.
 
Must I use Visual Studio for C# programming or can I use Visual Studio Code? I have used C# in VSC for other purposes previously (web development). Can I do the same? What is really the difference? I have never used VS I just heard it is a big slow beast and VSC is nicely lighter.
you can totally use VSC
 
As I feared, no progress has been made towards a better AI. Everything is so poor that we can't even add our own templates.

This is a setup for tweakers, not modders.
 
C# is a world class language used by a lot of things. Web, Desktop, Games (Unity Engine) and is based on object oriented programming like C++.
If you never learned object oriented programming or don't come from a developer background, I can see why people complain. However, using C# means you have a lot of power and flexibility. You can hook into cloud systems like Azure and AWS and do basically anything you want.

If you are writing a lot of repetitive code, then something is wrong. You can automate repetitive code through tooling and code generation and abstraction.
 
Last edited:
Back
Top Bottom