Modding system discussion

Then mod B should not be “give —sth— to all units from «mod_forge» mod” but “give —sth— to all units from «mod_forge.faction_forge» faction”.
That should be done as bonus on “allCreatures” node with faction limiter.

EDIT: AVS, you were faster. :slight_smile:

@AVS, Tow
So you forbid B’s mod doing it this way? See it from C’s perspective. A and B are already done and B is done wrong.

I don’t want to forbid anything. But these are just different things:

  1. give sth to units introduced by mod A
  2. give sth to units belonging to faction introduced by mod A

Both should be possible to express.

Well, if mod is wrong we can’t help it. (But then… is B really wrong? Or is it just doing a different thing than C’s autor needs?)
C author should fix B, re-release it as B2. Then set dependency on B2 and mark it as possibly conflicting with B.

I think this approach can make dependency tracking messy. It’s good when things evolve linearly, this encourages tree-like evolution (you don’t like how this mod works? fork it!).
Result? Either:

  1. even tiniest change breaks compatibility with dependent mods or
  2. compatibility is specified as a kind of subforest/subtree of version tree.

Why should C author worry about mod B? Perhaps mod B changes are not applicable to C (judging from fact that mod B changes units from B and not from Forge faction).

If these changes are applicable to all Forge creatures no matter where they’re coming from - then this should be fixed by B author.

Or somebody should make mod D that will apply B changes to C.

I think we should go with this. Full ID’s are always present but it is also possible to use short ID’s (if there are no conflicts).

@Ivan

You seem not to understand my changes. I want to avoid breakages between mods. If a set of mods works, modifying this set (in any way) should be possible without much hassle. You all suggest to break what doesn’t fit your system and let modders fix dependencies. Do you really think it will be feasible once biggest dependency graphs hit tens of nodes?

I’m not sure if I got your point correctly.

In this case - script from B won’t affect unit from C. For me this is logical because mod B author is not aware of C and he specifically modifies objects from B (and not C).

If he would modify something that A and C have in common (e.g. Forge faction) his scripts will work on both A and C.

What’s wrong here?

I’m not proposing to completely block access to C from B - for example iterating over all units in script from B will also include units from C. Bonuses from B will affect C (if applicable). But any explicit requests for C objects by ID’s should not be allowed.

B’s author has no idea about C. Let’s suppose he (B) did a popular script and quit. It’s either not maintained (it may be simple, so nothing to maintain actually) or its maintainers don’t agree for whatever C needs.

Then C wants do a mod. He wants to change the set of creatures – a very minor change, isn’t it? Just add one more. And you ask him to fork B’s mod, making his work incompatible with everything that requires B. Or, don’t bother with anything and just copy&paste B’s work to his mod…

For B, modifying every creature from a faction and modifying all creatures from a mod is the same – his script, then, has the same effect. He may not even realize that one of this methods blocks C’s modding attempts. If B is fully aware of consequences of his choice then it might be reasonable to allow him to do so but people usually write code they don’t understand.

BTW, it’s a part of a more general problem: what restrictions should be imposed on mods beyond engine limitations. I say that there will be complains for every such limitation and we should refrain from creating them unless there is a really strong motivation for them. ,because there will be conflicts’’ is a very poor motivation until you show why these conflicts are problematic. I see no problems with unit conflicts – modders should know the order of loading, there should be a convention (not enforced!) for avoiding conflicts, but if one wants a conflict, he should have it. Resolving them is really easy here – just overwrite/append existing entry. If you want to be 100% safe from stupid modders complaining about conflicts, make a switch between my/your modes and set your as default.

For me this is an obvious bug in B that should be fixed - either by sending report to B author or to its maintainer. I don’t see any other way to fix such issues.

Restricting of usage of ID’s is needed - there is just too easy to make mod that won’t work on another system because it uses ID from unrelated mod. So we need at least detection of such cases.

ID conflicts on another hand can be solved by engine in most cases - if two unrelated mods have same ID’s this is not a bug at least right now. The only exception in current (script-less) system is mod that requires two other mods that have conflicting ID’s.

Any presence of “long ID’s” or “short ID’s” will be (to some point) hidden from modders. Just like right now ID prefixes (“creature.”) are mostly internal detail and used only where id’s may have any type - e.g. bonus system.

This is an example of strong motivation.

Author of C made his mod, he uses faction added by A (Forge). Everything seems to be working fine. Now user installs this mod but he does not have mod A. Thus mod C won’t work - unknown faction. This restriction is needed to detect such situations - if mod C uses mod A it must be stated explicitly.

I don’t see how a switch will help here unless you can propose some way to reliably fix missing ID’s.

But we don’t have it. Current loading depends on order in hashtable + some reordering to load dependencies first. And so far system works without problems - there is no need to rely on load order.

If one mod wants to modify object from another mod - this should be allowed. We may even allow for mod A to modify mod B and vice versa at once - without any obvious load order.
But this should be differentiated from name conflicts.

No it is not. For example there is mods X and Y that add different creatures but with same ID - this is something that we can (theoretically) handle. But what if there is mod Z that wants to access creature from X? There is no way to determine this - mod may want to access creature from Y just as well.
Implementing explicit dependencies will fix this problem - because it will become clear to which unit mod Z wants to get access.

C needs both A and B. Why you thought otherwise?

If nothing else, scripts will need load order.

Actually, they set specific properties of --the same-- creature, with that ID. Mod Z accesses it. This is the method of conflict resolution I’m thinking about. Yes, it could be a mess (or could not). And yes, at least one of the modders explicitly asked for a mess (by setting the flag).

Strict dependencies is something we need to avoid easy to break mods. I read your quotes-only post as disagreement on this. Or it was not?

This is quite real problem - I started this discussion after I got mod with such problem.

Why? Or more correctly - why behavior of scripts should depend on load order?

But this may not be what modders expect. We already have one such conflict (currently solved by renaming). Both WoG and HotA have creature with same name - enchantress. But these creatures in these mods are completely different, the only thing they have in common is name.

Replacing “set of properties” from one mod by another set is something we should support but as separate feature. And this should be done explicitly. That is, mod should not just say “replace OR add X” but “replace X from mod Y”.
Actually most of this feature is already here - only missing part is transferring data between mods.

Actually, strict dependencies is the only restriction that is undoubtedly reasonable. And we should design other features in such a way that dependencies actually help modders, not limit them.

Take a look at ERM. It has global tables. Scripts save data in them. Some data can be written during initialization. One script can modify the way other script works. Then it needs to be fired e.g. after the first one to modify its initialization variables.

Well, in theory scripts and configs do not have to be parsed/executed mod-by-mod but more independently and then my argument is invalid. Scripts could be executed (in a specified order) e.g. after all configs are loaded (in any order). But does it have any advantage over mod-by-mod config+script init?

That’s why I suggest this feature to be optional, that is modder can decide which ‘namespace’ he wants to put his creatures in (defaults to this mod’s namespace, but could be global or other mod’s if modder wishes so).

I’ve already written what is the drawback (major IMHO) of this solution and suggested a better one without that drawback and with all advantages of your solution. In my solution you need to explicitly change namespace to the one with creature X (default: mod Y’s namespace).
f.e.: mod Y adds creature A. Default ID: creature.Y.A.
if mod X want to change it, then it refers to it creature.Y.A
but if mod Y wants do add a creature to namespace X, it can do so by stating that it wants to add creature.X.A. And then overwriting happens. example (config in mod Y):

"creature.X.A" : { "growth" : 30 }

sets growth of creature A from namespace X.

Even with clearly defined load order this may not work. Most of scripts will reside in WoG mod so all mods that use scripts from WoG must be marked as depending on WoG and will be loaded after it (and after any scripts initialization).

But why scripts need initialization to begin with? Any settings (e.g. enable script A, disable script B) should be done using something other than scripts like Json configuration.

"creature.X.A" : { "growth" : 30 }

For something like this I agree. I just want to avoid conflicts between unrelated mods.

Some cases we need to think about:

  • What to do if mod Y defined . BUT mod x is not present (and not a dependency)? Perhaps allow this as “compatibility patch” - piece of config that will be used only when X is present?

  • How to handle identifiers in such cases? Consider this example:

"creature.X.A" : { "shots" : 10, "upgrade" : "B" }

Current approach is to merge Json entries and load whole object at once. In this case all loading will be done from “X” namespace. But creature B is part of Y so this ID won’t be in scope.

Another approach - load partial configs and overwrite original values is tricky to implement - for example field “shots” is a bonus. So simple “add bonus” won’t overwrite original value but will increase number of shots by 10 - all cases like this will need to be handled manually. Which means more complex (to some point) loading.

@Ivan, what if resolve namspaces before merge?
for each mod:
[ul] Load mods config
remove “compatibility patches” for disabled|not existing mods
convert identifiers to full form[/ul]
merge all configs
load

To change other script’s configuration? Or change game config in nontrivial way (e.g. all flying creatures are not flying anymore but get 2xHP)? I think it’s a reasonable idea.

I see we finally begin to understand each other :).

By default I’d make at least a warning – it could be a typo. Compatibility patches is a good idea but I’m not sure if this is the right syntax for them.

Good point.

Shots: I think the simples solution could be to to make a stack of config entries during merging and for each field pick value from first config from top defining it.

IDs: AVS wrote a good solution for this problem.

It’s not only namespaces that are problem here. In this case loading is done from “X” and request goes for ID from “Y”. But “Y” is not marked as required by “X”. So ID’s Y.xxx can’t be used in “X”

Maybe we can use stack as Tow Dragon proposes or somehow store metainformation on where each field came from.

But why you should use script to change another script? IMO correct approach is either:
a) Load configuration from file (possibly changed by mod and/or user) - something similar to current modSettings.json
b) Allow enabling\disabling scripts anytime. So for example 2x hp to flying creatures will add (or remove) global bonuses with such effects.
In this case - there can be “library” with existing script functions that can be called by any other script to toggle option on\off.

Yeah. But I can’t think of any other syntax for something like this.

One more thing:
What about H3 objects? I think they should have namespace of their own to distinguish replacing H3 data from new object in mod namespace.

When i think discussions of mod compatibility i always think of mlox:

code.google.com/p/mlox/

much better than the Baldur’s gate community way of making a fixed order list of everything that can be possibly installed together.

A warning, a project like this, once you get to ‘enough’ mods (+100 i guess, which i don’t really expect with H3) means that rule dataset can become huge. Mlox is +5 mb i think. Choosing the right syntax, especially as to how pertains the For All operator seems like a must.

I actually wanted to do something similar to mlox but for the baldur’s gate trilogy, should get off my ass and just do it (but categorically don’t want to maintain the data).

Also, i’d make sure the ‘mod container format’ you standardize on is very amenable to auto downloading. The last try anyone used to make a autodownloader for BG2 mods, it was a mess (and quickly became outdated) because the mods have no notion of a standized container format:

  1. of how to uncompressed it
  2. of what is the folder structure inside of it
  3. of the VERSION of the mod (Weidu scripts versions are just a non-mandatory string with no validation, which helps noone when trying to compare numeric versions of the local versus the remote version)
  4. of how to download the info to compare versions from a server without downloading the whole mod (no protocol, related to the container format)
  5. of how to update the rules

etc.
I’d look into solutions like maven or ivy or even apt to get ideas. Just having a standard, even if it’s just ‘zip file with root on the Heroes 3 data folder’ will help alot when it’s time to do a downloader like that, even if you don’t want to create it yourselves.