Jump to content
pierremgi

Variables from description.ext

Recommended Posts

Hi all,

I spent hours trying to understand how to declare some big arrays from description.ext.

I had working scripts with usual functions/ variables in sqf til I decided to manage functions in cfgFunctions (this part is OK), but I fail to understand how variables work with preprocessor:

 

I tried to add a variable.hpp supposed to host the variables

then

#include variables.hpp

in description.ext, before the include for functions using these variables.

my variables.hpp looks like this for test:

 

#define random_loot_array  ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"))

 

Note that:

random_loot_array =  ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"));

works fine (formerly used in the function).

 

I guess I can't define a variable referring to the bunch of commands (getText, getNumber, configClasses...)

But how to declare such (permanent - non parametered) variables in a hpp?  If any added value for readability or performance.

 

Thanks

 

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Tested:

random_loot_array = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"))

 

even:

w = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"))

#define random_loot_array w

 

random_loot_array stays undefined. I'm lost with these prepro commands.

Share this post


Link to post
Share on other sites

Can't help much until this afternoon as out at the office. I will look when home and reply.

Share this post


Link to post
Share on other sites
8 hours ago, pierremgi said:

but I fail to understand how variables work with preprocessor

They.. eh.. don't?

 

8 hours ago, pierremgi said:

in description.ext, before the include for functions using these variables.

What?

 

Can you explain what you are trying to do, and why you are trying it?

 

EVAL won't work, eval only supports a limited set of commands, and config lookups certainly aren't supported.

Share this post


Link to post
Share on other sites

Just, on my trial to optimize some working codes, as I said, I declared some sqf into functions and this stage works.

Now, I never declared any variable in a hpp except for dialogs. I wondered if it was possible to proceed in a similar way

So I started by creating a:   variables.hpp  and wrote a ridiculous:

#define random_loot_array ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"))

On my mind, this was a way to "prepare", "define", or any term you want, this variable, gaining some performance or "touch of class in coding", instead of running it in two different functions. And, I

#include "variables.hpp"

before

#include "functions.hpp"

That seemed to me logical. I'm aware I'm noob for all hpp things, On HazJ comment, I took a bunch of time just for writing __EVAL instead of _EVAL  (The difference wasn't an evidence)... with no success in attempts I reported.

Share this post


Link to post
Share on other sites

I don't know what you mean with declaring a variable in a hpp?

You can't declare variables in configs.

Also hpp says absolutely NOTHING about what it's supposed to be. You can #include a hpp into whereever, and it will have a different meaning everywhere.

#include basically just copy-pastes things into your file. And #define defines a name that every occurrence of that name will be replaced by the text that you write in the #define. You know that right?

 

All that #include and #define do is copy-paste text around. You are not declaring any variables anywhere.

  • Like 1

Share this post


Link to post
Share on other sites

I feel this part of the code optimization page might be relevant to the discussion: 

https://community.bistudio.com/wiki/Code_Optimisation#Constants_2

 

@Dedmen is correct though, #define DUDE 1 does not declare a variable DUDE which is equal to 1, it merely let's the preprocessor know to replace DUDE with 1 when preprocessing the current file. 

 

Writing if( DUDE == 1 ) would in runtime execute as if( 1 == 1 ) 

  • Thanks 1

Share this post


Link to post
Share on other sites

Excellent! Thank you so much all!

I totally missed the #include as a simple insertion, and misunderstood the #define functionality.

 

Now, let see how to make it work:

#include "variables.hpp"   (description.ext)

then, in variables.hpp (first attempt):

myVar = ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"));

 

1. a simple recall of myVar fails (not defined at all)

but, reading the @mrcurry  link for optimization, i tend to reach my goal:

2. getMissionConfigValue "myVar"  returns the chain:

("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"))

but not the value...

 

So tested (2nd attempt):

myVar = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"));

 

Now, getMissionConfigValue "myVar" returns the working array, but wrapped inside a string (in debug console)...

"[bin\config.bin/CfgWeapons/CUP_launch_M72A6,......]"

 

So, the good question is, should I compile (getMissionConfigValue "myVar") ?

emmh... call compile (getMissionConfigValue "myVar") doesn't return anything...

tested also: parseSimpleArray (getMissionConfigValue "myVar"), returns [<nul>,<nul>,<nul>,......]

 

:hang:

 

>>  And how could this improve performance as described here?...

 

EDITED

Share this post


Link to post
Share on other sites
1 hour ago, pierremgi said:

So tested (2nd attempt):

myVar = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons"));

 

Now, getMissionConfigValue "myVar" returns the working array, but wrapped inside a string (in debug console)...

 

The output of your _EVAL is interpreted as a string, try 

myVar[] = _EVAL(...

Note the brackets 

Share this post


Link to post
Share on other sites

myVar[] = __EVAL ("getText (_x >>.....

throws an error _ encountered instead of {

 

Share this post


Link to post
Share on other sites

Silly me, myVar[] = is a array declaration and requires { } around the array not [ ], the array declaration also seems to be processed before the __EVAL since the error thrown is for _ not [ or ".

We also need to remember that configs only support simple data types like scalar, string and nD-arrays consisting of scalar(s) and/or string(s). 

If you really need to return an array of CONFIG types see the "if all else fails" at the bottom.

 

So I finally popped on my main PC and did some tests of my own.

 

Array as config entry
This is the best I've managed to come up with so far that is along your original line of thought.

Keep in mind we have to convert the configs into simple data types if you want to store it in description.ext or any other config file:

//description.ext
myVar = __EVAL ( "getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons") apply compile "configName _x" );

//runtime call
private _classes = parseSimpleArray getMissionConfig "myVar";
//or 
private _classes = call compile getMissionConfig "myVar";

Yes that "apply compile" is required, __EVAL does not take kindly to the } bracket and makes the preprocessor interpret it as eof for some reason. :shrug:

 

Now I haven't tested if the "call compile getMissionConfig" is faster than the just straight up calling the configClasses and apply during runtime but it does achieve the "array as config constant" criteria.

 

Pros: Mod version independent, pretty much no upkeep.

Cons: Requires a call to parseSimpleArray, a runtime compile or similiar action. For big arrays this might be a big no no.

 

Optimisation
A possible optimisation, that might be impossible, is to somehow convert the value from the above __EVAL to an array-type config entry similiar to this:

myVar[] = {"class1", "class2", ... , "classN"};

The problem is I have yet to figure out a way to do this using the preprocessor.

I suppose treating the returned array like a string and doing a find & replace might work but that's a big maybe.

You still have to get around the fact that myVar[] = expects to be followed by a {} pair.

 

The easy way

Another option is to just straight up define the array with classnames like below, this method does make mission upkeep a bit more involved since every update to the mod or vanilla might mean you need to update the list.

The runtime call should be fast though which is nice.
 

//1. Get the array using configClasses apply {configName _x} in the debug console.
//2. Copy the result to your favorite text editor.
//3. Replace the first [ and last ] with { and }
//4. Paste the into the config like so:

//description.ext
cup_weaponClasses[] = {"class1", "class2", ... "classN"};

//runtime call 
getMissionConfig "cup_weaponClasses" //Returns an array of classnames

 

If all else fails

A global variable computed during pre-Init has never hurt anyone right? ^^

 

The goal is to get the array-assembly out of runtime code so it doesn't have to run multiple times to regenerate what is essentially a static data set, correct?

If so setting up a pre-init function to compute the array contents is probably the best bet. You'll have full access to all SQF data types and don't have to pull your hair over config syntax.

 

  • Like 2
  • Thanks 2

Share this post


Link to post
Share on other sites

Thank you so much! Let the included hpp and constants optimization (red dot critical!) for simple data, or "array of simple elements".  All "computed arrays", like I wanted for configClasses filter, can be grouped in a function. I will test a pre-init one as you mentioned. But I guess that, if I declare it or include it first in description.ext, the engine will read it, and the other functions (using the variables) will not return any error.

In other words, is pre-init mandatory?

Share this post


Link to post
Share on other sites
8 minutes ago, pierremgi said:

In other words, is pre-init mandatory?

Really depends on when you expect the data to be used. 

I just chose pre-init cause it guarantees execution before most if not all other scopes. 

  • Thanks 1

Share this post


Link to post
Share on other sites

Thanks all. Special thanks to you @mrcurry   Now, I'm digging deeper in the abyss of optimization.

I just find a topic, with good comments for general optimization and various interesting things about scheduler and its usage.

  • Like 1

Share this post


Link to post
Share on other sites

Well, I added my configClasses filter as variable in an sqf, then added this sqf in my hpp with all other functions.

Something like:

class MGI
{
    class missionVars {
        file = "functions";
        class serverVars {
            preInit = 1;
        };
        class publicVars {
            preInit = 1;
        };
    };
    class basicFnc {
        file ="functions\basic";
        class distanceGrp {}; // fn_distance.sqf
        class randomPos {};
        class punishKill {};
        class punishrelax {};
        class rewarding {};
        class addDamageEffect {};
        class silencer {};
... etc

 

What I remarked:

- If I don't write preInit, so the variables are not "called" (sorry for the exact term), and then not defined while in game.

- if I add preInit =1, that works, variables are "read" and "called" then defined. But the weird thing is I catch an error: functions\fn_serverVars.sqf not found when I save and open a new scenario. It's weird because I wrote preInit = 1, supposed to be for the mission scope, (and not preStart = 1 supposed to be for Arma session and config.cpp addons), if I'm right.

I can live with that, but if there is a work around for "calling" the function (in it's class), That could be great! Thanks.

Share this post


Link to post
Share on other sites
On 30.11.2018 at 5:33 PM, pierremgi said:

So, the good question is, should I compile

No. Stringified configs are not parseable. which is why

On 30.11.2018 at 5:33 PM, pierremgi said:

tested also: parseSimpleArray (getMissionConfigValue "myVar"), returns [<nul>,<nul>,<nul>,......]

that happens.

 

On 1.12.2018 at 12:24 PM, mrcurry said:

and 1d-arrays

Nope. You can have ND arrays all you want.

 

On 1.12.2018 at 12:24 PM, mrcurry said:

Requires a runtime compile

No it doesn't parseSimpleArray should work in that case.

 

On 1.12.2018 at 12:24 PM, mrcurry said:

If so setting up a pre-init function to compute the array contents is probably the best bet.

That would be my recommendation too.

 

On 1.12.2018 at 3:55 PM, pierremgi said:

red dot critical!

Did you even think about what you read and what you are actually trying to do there?

Why do you think would this be better than just computing it once at preInit or in init.sqf?

So in init.sqf, you execute the code once and store it in a variable.
In description.ext, before even the mission is loaded (just trying to display the mission name in the mission selection screen has to parse the description.ext) your code is parsed, executed, and the result turned into a string. If you then choose to really start the mission you just clicked on, you will then have to take the string out and parse it back into an array. If you don't actually start that mission, you've just computed a potentially expensive and completely useless value that will be thrown away again.
Performance wise the description.ext variant is simply stupid nonsense. there is 0 performance advantage.

 

 

Yes, It's great if you just need something small like a random number or something, for that the solution is better than compileFinal'ing a piece of code with the value and calling that over and over again.

But for things like this, it's just utterly stupid.

 

The Code optimization wiki page assumes that you still take everything with a grain of salt and really think about what you are doing there.
I'll see that I get that red dot removed, or add a big fat disclaimer to that explaining what it actually is doing.

 

Share this post


Link to post
Share on other sites

@Dedmen  I'd like your explanation if you didn't abuse of the term "stupid". That's probably what you think because you're so skilled for this part of scripting. Nevertheless, I hope this post will help some guys who could ask themselves similar question about optimization, without suffering your criticism.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×