Page MenuHomeFeedback Tracker

Unexpected behavior of += array in configs
Reviewed, LowPublic

Description

The new += array config command needs a non-inherited entry in that class, otherwise it simply overwrites.

Additinally using += on a parent and a child class in one config leeds to unexpected results. {F25058}

Details

Legacy ID
2259502755
Severity
Tweak
Resolution
Open
Reproducibility
Always
Category
Config
Steps To Reproduce

Issue 1:

Set up two configs and binarize them. Then start the game and watch the resulting configs in the config viewer.

Config 1:
class A {

array1[] = {"1"};

};
class B: A {

array2[] = {"1"};

};

Config 2:
class A;
class B: A {

array1[] += {"2"};
array2[] += {"2"};

};

Expected result in the game:
class B: A {

array1[] = {"1", "2"};
array2[] = {"1", "2"};

};

Actual result in the game:
class B: A {

array1[] = {"2"};
array2[] = {"1", "2"};

};

As you can see, instead of adding "2" to array1 it got overwritten, because it wasn't defined in B, but inherited from A.
array2 was defined directly in B, so "2" got added to the array correctly.


Issue 2:

Same steps, but the following setup:

Config 1:
class A {

array1[] = {"1"};

};
class B: A {

array1[] = {"1", "2"};

};

Config 2:
class A {

array1[] += {"3"};

};
class B: A {

array1[] += {"3"};

};

Expected result:
class A {

array1[] = {"1", "3"};

};
class B: A {

array1[] = {"1", "2", "3"};

};

Actual result:
class A {

array1[] = {"1", "3"};

};
class B: A {

array1[] = {"3", "3"};

};

Event Timeline

commy2 edited Steps To Reproduce. (Show Details)Oct 31 2014, 9:20 PM
commy2 edited Additional Information. (Show Details)
commy2 set Category to Config.
commy2 set Reproducibility to Always.
commy2 set Severity to Tweak.
commy2 set Resolution to Open.
commy2 set Legacy ID to 2259502755.May 7 2016, 7:43 PM

Any news on this?

commy2 added a subscriber: commy2.May 7 2016, 7:43 PM

Still the same issue with version 1.34.

pettka added a subscriber: pettka.May 7 2016, 7:43 PM

The description is correct - this feature was never meant to work with inherited classes. The goal was to create a simple tool to work add into array with unknown previous values (e.g. Throw muzzle with various grenades) and it works just like that.
There is currently no plan to change the feature, it may be possibly done in the future, but I cannot promise anything.

When you say "not meant to work with inherited classes" does that also include inheritance inside a single config file?
I.e. what will the following produce (single config file only):
config1:
class A {
property[] = [a,b];
};
class B: A {
property[] += [c];
};

@ImperialAlex

It doesn't. There is no difference whether you put stuff inside one config.cpp or into multiple aside from loading order.

Also for the record, += doesn't even work with "arrays with unkown previous values". In practice it ONLY works with Throw, because there is no reason to inherit from that class as every soldier uses it.

You can't for example add a custom silencer to the MX (compatibleItems[]), because the MXSW inherits from that.

@commy2

Thanks for the explanation. Could you give an example usage (with Throw)?
I'm still trying to get my head around this operator (out of mere interest as it seems a fairly niche thing).

Slightly off-topic: Is there already a feature-request to turn += into a more general tool that *could* handle things like adding a custom silencer to the MX's compatibleItems[]?

PiepMGI added a subscriber: PiepMGI.May 7 2016, 7:43 PM

As exemple with throw:
class CfgWeapons
{

    class GrenadeLauncher;
    class Throw : GrenadeLauncher {
        muzzles[] += {"MGI_LacrymoMuzzle"};
            
        class ThrowMuzzle: GrenadeLauncher {};
        class HandGrenadeMuzzle: ThrowMuzzle {
            magazines[] = {"HandGrenade"};
        };
        class MGI_LacrymoMuzzle : ThrowMuzzle {
            magazines[] = {"MGI_Lacrymo_mag"};
        };... then you need to repeat all grenade classes with their magazines (like handGrenadeMuzzle above)!

btw, i never found the way to add a magazine for the GL_3GL_F launcher. It works for UGL_F (EGLM) but no way for the GL_3GL_F (commonly used in plenty of addons). Seems to need rewriting all guns just for that!

@ImperialAlex

+= certainly is "interesting", but not really useable outside of a few exceptions at this point.

You could say that this report is a feature request for that.

@PiepMGI

Thats exactly the issue I described. The grenade launcher class is inherited and therefore += doesn't work as expected.

Kaban added a subscriber: Kaban.May 7 2016, 7:43 PM
Kaban added a comment.Sep 28 2015, 2:47 AM

BI, please consider to add this feature.
It's very important for island makers.
This command is the only way to make all islands compatible with each other.

Island maker must override (instead of appending) global vehicle classes to add his own dust effects for his terrain.
So end users can't properly use several islands simultaneously because every island override dust effects of all other islands.

More info can be found here: https://forums.bistudio.com/topic/176156-how-to-customize-terrain-dust-sound-and-ocean/?p=2918406

If you integrate it they way it's described in the original post this is going to add a very powerful tool for addon cross compatibility.

Please consider this.

Kaban added a comment.Sep 28 2015, 1:46 PM

True. It would be the greatest config feature ever.

dedmen claimed this task.May 24 2020, 3:13 PM
NikkoJT added a subscriber: NikkoJT.Jun 8 2023, 5:35 PM

Issue 2:

This happens because there are two resolve steps for appends.
One happens during config load, the other during config merge.

At game start, all addon configs are loaded as standalone configs. Then sorted by loadOrder, then merged into one master config according to order.

When the config is loaded, it doesn't know that any other "class A" exists. So it thinks it can just resolve the array append to B right then and there. So it turns it into array[] = {3,3} which, if it stays a standalone config (like description.ext) it would be perfectly correct.
But then later, the merge comes along. It now sees class B with array[] = {3,3} okey that's not a append, so it will overwrite the previous value that was there.

Easy solution, just don't resolve during load, and let it stay around till the merge. That would solve it.
But, then we end up with configs that don't get merged (description.ext) with unresolved array appends and that end would break.

And when a config is loaded, it doesn't know if it might get merged later, or not.

So due to technical debt and how the config loading was designed, we cannot solve this.

Alternate solution, don't resolve append on load if the previous definition was also append.
This would be a behaviour change

`class A {
  arr[] += {1,2,3};
};
class B : A {
  arr[] += {4}; // arr = [1,2,3,4]
};

In addon config, this would be wrong. But in description.ext, this is correct result (though still kinda syntax error to append to a value that doesn't exist, but the game currently just turns that into a =)

So fixing it this way, would break incorrect code that currently works.

Issue 1:

This is a messup in the config merge code.
When we merge two configs together, we check if value exists in class (ignoring parents/inherited values), if yes we overwrite, if not we create a new one.
That works fine for most values, but not with array append. This behaviour causes it to think "Value doesn't exist, so we create new one" which is correct, but also "previous value wasn't found, so theres nothing I can append to" which is of course wrong.

I think that should be safe to fix

dedmen added a comment.EditedJun 13 2023, 4:33 PM

Issue 1 is fixed in 2.13/2.14 150692
Also next profiling branch build, probably early next week

For now I still expect that Issue 2 will stay broken