Jump to content
Sign in to follow this  
Rommel

ROMM_fGroups

Recommended Posts

<span style='color:red'><span style='font-size:19pt;line-height:100%'>Rommels fGroups</span></span>

Version 102 by Rommel

Aim: Designed for a question in a previous OFPEC thread, but its usefulness and apparent challenge to make it a single script made it fun, and quite useful. Works great, and allows the player to change throughout groups as it goes along in-game. Hopefully it can be useful for those in need.

Description: Ability to leave and join groups at will of the player with little CPU usage through one script, with incredibly easy implementation by users.

NO GLOBAL VARIABLES + SQF Optimized.

Preview Mission Included, feel free to edit, keep base credits if possible please, otherwise its open source and go insane. smile_o.gif

Download:

http://www.ofpec.com/forum....ch=2539

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

/* PreProcess the File to save load times and easier execution. */

ROMM_fGroups = compile preprocessFile "fGroups.sqf";

[player,"","",[0]] call ROMM_fGroups;

Share this post


Link to post
Share on other sites

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">/* PreProcess the File to save load times and easier execution. */

Storing code in a variable does NOT save load times.

There is NO reason to use a global variable to store a constant; use #defines instead.

The engine caches scripts when you use the compile command, so it only loads a given script ONCE, even if you issue multiple "compile preprocessfile" commands.

This is easy to prove via experimentation.

Sorry for being picky, I just wish this little misnomer would go away...

Share this post


Link to post
Share on other sites

I was referring to the load time when executing the script, less load on the server etc.

Surely a cached script that doesn't have to re-compile would be less load on the server/player. whistle.gif

huh.gif

Share this post


Link to post
Share on other sites
I was referring to the load time when executing the script, less load on the server etc.

Surely a cached script that doesn't have to re-compile would be less load on the server/player. whistle.gif

huh.gif

Nope, it only compiles the FIRST time it sees the 'compile preprocessfile' command. Subsequent commands take no time at all. Example:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

for "_i" from 0 to 100 do

{

[] call compile preprocessfile "myscript.sqf";

sleep 0.1;

}

In this code, there will be a slight delay the first time "myscript.sqf" is run, as the engine compiles it. But every subsequent time it is run, there will be no delay, because the engine doesn't need to compile it anymore (its already been compiled).

This is simple to demonstrate via experiment.

Sorry, I really didn't mean to hijack your thread though... confused_o.gif

Share this post


Link to post
Share on other sites

Aren't we arguing the same point?

And nah mate, I'm happy to discuss it here, learnings learning, t'is why I make these scripts. biggrin_o.gif

Share this post


Link to post
Share on other sites

Very interesting info General Barron. Thanks for sharing!

Share this post


Link to post
Share on other sites

Ah I think I understand where your coming from now, so say I did this:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

nul = compile preProcessFile "script1.sqf";

nul = compile preProcessFile "script2.sqf";

nul = compile preProcessFile "script3.sqf";

nul = compile preProcessFile "script4.sqf";

nul = "";

Does this mean If I were to "execVM 'script1.sqf'" from here on it would not have to compile the script, or if I did:

"call compile preProcessFile 'script4.sqf'" it would only have to compile once. (Which is what I knew before anyway, I just stored in a global variabled as I had learnt from DACv2).

thumbs-up.gif

Share this post


Link to post
Share on other sites

I don't understand what you're asking at all crazy_o.gif .

What I'm saying, is that there is no difference, speed-wise, between the following scripts:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

myfunction = compile preprocessfile "myfunction.sqf";

[] call myfunction;

[] call myfunction;

[] call myfunction;

[] call myfunction;

[] call myfunction;

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

[] call compile preprocessfile "myfunction.sqf";

[] call compile preprocessfile "myfunction.sqf";

[] call compile preprocessfile "myfunction.sqf";

[] call compile preprocessfile "myfunction.sqf";

[] call compile preprocessfile "myfunction.sqf";

There is a perfectly reasonable assumption that the first script would be faster, because the second one looks like it would be loading the file over and over, while the first would only load it once.

This assumption is incorrect. In the second script, the engine already 'remembers' that it compiled myfunction.sqf, and it is smart enough to not reload the script over and over again.

So the first script basically wastes a global variable, for no real gain/reason.

--------------------------------------------------------

A better practice would be to write the following:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

#define myfunction compile preprocessfile "myfunction.sqf"

[] call myfunction;

[] call myfunction;

[] call myfunction;

[] call myfunction;

[] call myfunction;

This is just as "nice" looking as the first example, but it uses no global variables. Instead, the define is used to do a find/replace operation when the file is preprocessed (when loaded in-game, this script looks identical to the second example I gave above).

If you have a lot of functions, you can throw all the #defines in a separate header file, and use the #include instruction at the top of your script to include that file.

This same trick is great for other constants, such as IDCs. I've seen a lot of scripts that waste a LOT of global variables, storing data that never changes.

Share this post


Link to post
Share on other sites
Very interesting info General Barron. Thanks for sharing!

SB, you must have known this already?! it's common knowledge isn't it? there's been numerous discussions on the topic from day one... seem to remeber you even participating in those forum discussions... but, my memory could be failing me...

Share this post


Link to post
Share on other sites

*topic derailing*

Rommel could you provide some more info on what your script does?

Share this post


Link to post
Share on other sites

@Rommel; I hope you are okay with me continueing on this subject. If not, I will take it to PM and want to apologize for any inconvenience smile_o.gif

Very interesting info General Barron. Thanks for sharing!

SB, you must have known this already?! it's common knowledge isn't it? there's been numerous discussions on the topic from day one... seem to remeber you even participating in those forum discussions... but, my memory could be failing me...

I was unaware that compile preProcessFile means that it will not compile nor preProcess the file again, at consequent compile preProcess. I always thought you could change a script file on the fly, and compile preProcess it again, so it would update.

The problem is basicly that I never did any tests smile_o.gif

The part by Barron "You can include header files into other scripts", I guess this is the method used for general functions, available in multiple scripts.

I'm not too sure im liking this too much... #include  stuff in all of the script files... Would you then make 1 general functions header file, or would you make all kinds of seperate ones, and include the seperate ones into the scripts, where you need them, etc? smile_o.gif

however after I've ran through my editor with the defines, I might see the benefit smile_o.gif

(esp considering that amount of global vars is not a problem anymore since v1.09 or so)

Share this post


Link to post
Share on other sites
Ah I think I understand where your coming from now, so say I did this:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

nul = compile preProcessFile "script1.sqf";

nul = compile preProcessFile "script2.sqf";

nul = compile preProcessFile "script3.sqf";

nul = compile preProcessFile "script4.sqf";

nul = "";

Does this mean If I were to "execVM 'script1.sqf'" from here on it would not have to compile the script, or if I did:

"call compile preProcessFile 'script4.sqf'" it would only have to compile once. (Which is what I knew before anyway, I just stored in a global variabled as I had learnt from DACv2).

thumbs-up.gif

Another bit...

In your case, you're using nul which is not a reserved keyword, but many people unfortunately have been using nil in that or similar contexts, overwriting the effect/value of nil.

So if the context requires a (var = code) syntax where the var is disposable, please use a unique global or better yet a disposable private var.

Share this post


Link to post
Share on other sites
Quote[/b] ]I always thought you could change a script file on the fly, and compile preProcess it again, so it would update.

You can, and it will reload the script with the changes. Although I guess the OS could be informing Arma when the file changes?

I also ran this test script:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_Count=0;

_MyTime=Time;

_Func=Compile PreProcessFile "Script2.sqs";

WaitUntil

      {

      //_Result=[] Call Compile PreProcessFile "Script2.sqs";

      _Result=[] Call _Func;

      _Count=_Count+1;

      (_Count==1000)

      };

Hint Format ["Result %1 %2",Time-_MyTime];

PreProcessing the file outside of the loop always resulted in less time taken, compared with processing it in the loop. The larger you make Script2.sqs or the longer the loop, the more apparent it becomes.

Obviously with small scripts executed once or twice in a game, it makes no difference. Even the above test only resulted in tiny performance changes.

I'm happy to believe that BI included the new functions because they serve a purpose, rather than just doing it for the sake of it?

One definite advantage when it comes to referencing scripts with variables is the ability to reduce network traffic. Communicating a variable name across a network can require less data than communicating a script name and it's entire path.

As for the advantages of memory and saved game management, I'm not really qualified to say. But I prefer the idea of having the commonly used scripts and functions, loaded into unique, static memory locations, for the duration of a mission. Rather that loading and unloading multiple copies of the same script. When the duplicate scripts will occupy multiple locations in memory.

For me the appeal is not so much about performance or efficiency, it's more about stability. For example pre-loading all the scripts and functions will result in a larger saved game, but the size will remain relatively constant. As opposed to loading all your scripts when you need the. Sometimes the save game will be small, sometimes it might 10x larger, depending on what’s been loaded at the time.

So far I've used a mixture of both, depending on the frequency, size and purpose of the required scripts. But if the saved game issues have been resolved now, when it comes to global vars? I would be happy to convert every script that’s called more than once, over to precomiled global script pointers.

Share this post


Link to post
Share on other sites

I always love the completeness of your posts UNN smile_o.gif

Awesome points you got there. Esp concerning sending commands over the net.

I am evaluating switching to defines for most constants etc, however for the functions in this case I probably stick to global vars and local vars :-p

Share this post


Link to post
Share on other sites

As far as I'm concerned, this discussion is beneficial to me and this thread, so I'm happy to discuss it, its not exactly de-railing. whistle.gif

I understand you now GB, I wasn't sure what you referring too (the use of the Global variables, not so much the compile PreProcessFile)

Thanks for the hint of the #define, looks great, and is obviously only recognised during the compilation of the script via the engine?

Can this be used globally, or only at the start of each script file containing this. (Ie do I put it as a header of the script or in the description.ext etc)

Thanks a tonne!

Romm.

thumbs-up.gif

EDIT: For MadDog: Description: Ability to leave and join groups at will of the player with little CPU usage through one script, with incredibly easy implementation by users.

EDIT:

http://www.ofpec.com/forum/index.php?topic=31520.msg216560#msg216560

Updated.

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

[player,"","",[0]] call compile preprocessFile "fGroups.sqf";

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

/* ROMM_fGroups.sqf |

Version: 102 [26, 05, 2008] |

Author(s): Rommel [rommelvonrichtofen_at_bigpond.com] |

Execution: [player,",",[_var,_temp]] call ROMM_fGroups;

_VAR being the optional values for which option you wish to use. See below:

(0 - Init / 1 - Leave Group / 2 - Join Group Init / 3 - Join Group) and _temp being the name of the group to Join when using case 3.

*/

_player = _this select 0;

_id = _this select 2;

_arg = _this select 3;

_var = _arg select 0;

_temp = _arg select 1;

_group = group _player;

switch (_var) do

{

case 0:

{

if (count units _group > 1) then

{

sleep 2.75;

_leave = _player addaction [format["Leave %1",_group],"fGroups.sqf",[1]];

};

};

case 1:

{

if (count units _group > 1) then

{

[_player] join grpNull;

_player removeAction _id;

sleep 2.75;

[_player,"","",[2]] call compile preProcessFile "fGroups.sqf";

};

};

case 2:

{

if (count units _group < 2) then

{

sleep 2.75;

while {(alive _player) && (count units _group < 2)} do

{

_group = group _player;

_temp = nearestObjects [position _player,["Man"],10];

_temp = leader group (_temp select 1);

if ((_temp distance _player < 4) && (side _temp == side _player)) then

{

_action = _player addaction [format["Join group %1",group _temp],"fGroups.sqf",[3, _temp]];

waitUntil {_temp distance _player > 5};

if (_temp distance _player > 4) then {_player removeAction _action;};

};

sleep 1.75;

};

};

};

case 3:

{

_player removeAction _id;

sleep 1.75;

[_player] join group _temp;

[_player,"","",[0]] call compile preProcessFile "fGroups.sqf";

};

};

Share this post


Link to post
Share on other sites

A few points into the stew-pot:

1) General Barron states to the effect that the engine recognises "compile preprocessFile" as a compound entity and pre-cashes the result of loading, preprocessing and compiling the result, so future calls cost nothing. From my knowledge of the compilation process and guesses about the parser that ArmA uses, though, I would interpret that as preprocessFile caching either the last, or the last few files, loaded. File access is a relatively slow process compared to scanning and parsing any script, so it might just be that compile takes negligible time. It might even be Windows itself caching regularly accessed files for ArmA, which would make it unpredictable (though predictable in an artificially tight loop)...

I can't believe that if the ArmA parser doesn't cache the files called by execVM or actions (single commands that load, process and run a file), then it would be busy looking for the pairing of compile and preprocessFile and caching the result of that. What if I do this?

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_frog = preprocessFileLineNumbers "fish.sqf";

_fish = compile _frog;

Also, you are saying that I can delete compiled code stored in a function (_function = nil), but that doesn't mark it for the garbage collector and it will be kept in memory forever even if not ever used again? I don't think that would be good design for BIS at all (though, perhaps, it IS cached for a while, at least until it is collected, which might mean that later runs would be more costly again).

Anyway, what I'm saying is that if any caching occurs it probably is at the loading level, possibly even outside ArmA, not anything at the compile/preprocess level.

2) Assuming that the compiled code is automatically stored somewhere, adding a global will do no more than add that string into the variable hash table (so, 10-20 bytes on average, perhaps) and add a pointer (4 bytes; well 8 on 64bit, I suppose) to the cached compiled code. There is no reason to believe that the cached compiled code would need to be copied if you "stored" it in a variable any more than an array would be (not sure if you were implying that).

3) preprocessFile is completely obsolete. You should always be using preprocessFileLineNumbers in ArmA. preprocessFileLineNumbers does everything that preprocessFile does, but additionally, the user will be given file and line-number information in any error reports when running the function created. Yes, it blatantly wastes a couple more bytes, but if you write code that never gives errors, then I want to hire you at $1000/hour and I know I'd do well out of the deal ;P

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
Sign in to follow this  

×