Jump to content
Sign in to follow this  
Tomsk

Difficulties creating reusable scripting libraries

Recommended Posts

Hello,

I'm trying to create a reusable library of scripting functions which is proving to be surprisingly challenging. Now because I have lots of functions, I want to use the style:

MyFirstFunc = {
   hint "Hello world!";
};

MySecondFunc = {
  hint "Hello world, again!";
};

Functions are defined as global variables. This style allows you to define multiple functions per file, which is important for what I'm doing.

So I take these two function, and put them in a file "MyLibrary.sqf". Now I open up another sqf called from my mission, "dosomething.sqf"; And I call ...

[] execVM "MyLibrary.sqf";

[ 1, 2, 3 ] call MyFirstFunc;

And Arma 2 tells me that it can't find MyFirstFunc ... I add a trace line in MyLibrary.sqf to check that it is being executed, it definitely is being executed.

So it seems that execing a script that defines functions as global variables does not cause those global variables to be accessible to the next script.

So next, I try the rather dubious #include mechanism ...

#include "MyLibrary.sqf";

[ 1, 2, 3 ] call MyFirstFunc;

Hoorah! Success Arma calls my function and shows me a hint for 'Hello world'. Awesome, so now I'll just move my reusable MyLibrary.sqf out of the mission folder ... into the 'Documents\Arma 2\scripts' folder ... try again and Arma 2 blows up (CTD) saying it can't find the script. So I try this instead.

#include <MyLibrary.sqf>;   // Note the <> brackets instead of "" quotes.

[ 1, 2, 3 ] call MyFirstFunc;

Same result. Maybe I messed up, so I switch back to this:

[] execVM "MyLibrary.sqf";

[ 1, 2, 3 ] call MyFirstFunc;

But with my MyLibrary.sqf still in Documents\Arma 2\scripts. Apparently that works as well as it did before, it does execute MyLibrary.sqf but the functions within it still don't work.

So it seems that execVM doesn't work with the 'multiple functions per sqf' style ... and #include only works if the script is in the same directory. Even if #include can be made to work from other directories ... can it still be made to work if I put my scripts in a PBO (which is my ultimate goal)?

So my question is as follows: Does anyone have any idea how you go about creating a reusable library of scripts in the multiple-functions-per-script style?

Share this post


Link to post
Share on other sites

You should be able to use execvm just fine. Maybe you're calling the function too fast before it's compiled (or w/e you want to say). Try a sleep inbetween

[] execVM "MyLibrary.sqf"; 
sleep 1; // Just for fun here 
[ 1, 2, 3 ] call MyFirstFunc;

I know you can have alot of functions in 1 file thats execVM'd. Below is one of my function files. It works fine & I ExecVM "TheFile.sqf" in the init. Ofcourse, I also don't call any of the functions right away after running the script that compiles the functions, like you pretty much do in your op.

//UI FUNCTIONS
private ["_PWeaponIndex","_SWeaponIndex","_PistolIndex","_ReconIndex","_Pistols"];

PWUIEvents = 

   {
      If (!Local Player) ExitWith {};

     _PWeaponIndex = lbCurSel 2100;

     If (PrimaryWeapon Player != "") Then {Player RemoveWeapon (PrimaryWeapon Player)}; 

     Switch (_PWeaponIndex) Do 

           {

             Case 0:{Player AddWeapon "m4a1";Player selectWeapon "m4a1"};
             Case 1:{Player AddWeapon "G36_C_SD_eotech";Player selectWeapon "G36_C_SD_eotech"};
             Case 2:{Player AddWeapon "mp5a5";Player selectWeapon "mp5a5"};

           };





   };

SWUIEvents = 

   {
     If (!Local Player) ExitWith {};

      _SWeaponIndex = lbCurSel 2101;

      If (SecondaryWeapon Player != "") Then {Player RemoveWeapon (SecondaryWeapon Player)}; 

      Switch (_SWeaponIndex) Do 

           {

             Case 0:{Player AddWeapon "SMAW"};
             Case 1:{Player AddWeapon "M136"};
             Case 2:{Player AddWeapon "Stinger"};

           };





   };



PisUIEvents = 

   {

      If (!Local Player) ExitWith {};
      _PistolIndex = lbCurSel 2102;
      waituntil {!isnil "bis_fnc_init"};
      _PlayerInv = [Player] call BIS_fnc_InvString;
      //hint format ["%1", _playerInv];

      Switch (_PistolIndex) Do 

           {

             Case 0:{Player AddWeapon "Colt1911"};
             Case 1:{Player AddWeapon "M9"};
             Case 2:{Player AddWeapon "M9SD"};

           };





   };



ReconUIEvents = 

   {

     If (!Local Player) ExitWith {};
      _ReconIndex = lbCurSel 2103;

         Player RemoveWeapon "Binocular";
         Player RemoveWeapon "NVGogglesr";
         Player RemoveWeapon "LaserDesignator";

      Switch (_ReconIndex) Do 

           {

             Case 0:{Player Addweapon "Binocular";Player addweapon "NVGoggles"};
             Case 1:{Player Addweapon "NVGoggles";Player addweapon "LaserDesignator"};

           };




   };



PWPics = 

   {
       If (!Local Player) ExitWith {};
       _PWeaponIndex = lbCurSel 2100;
       Switch (_PWeaponIndex) Do 

           {

             Case 0:{
                     ctrlSetText [1201, "\ca\Weapons\Data\equip\w_m4_ca.paa"];
                     ctrlSetText [1203, "\ca\weapons\data\equip\m_30stanag_CA.paa"];
                    };

             Case 1:{
                     ctrlSetText [1201, "\ca\weapons\G36\Data\Equip\w_G36_C_SD_eotech_ca.paa"];
                     ctrlSetText [1203, "\ca\weapons\data\equip\m_G36_CA.paa"];
                    };

             Case 2:{
                     ctrlSetText [1201, "\CA\weapons\data\equip\W_HKM5_A5_CA.paa"];
                     ctrlSetText [1203, "\Ca\weapons\Data\Equip\m_HKM5_CA.paa"];
                    };




           };



  };


SWPics = 

   {
       If (!Local Player) ExitWith {};
       _SWeaponIndex = lbCurSel 2101;
       Switch (_SWeaponIndex) Do 

           {

             Case 0:{
                     ctrlSetText [1202, "\ca\weapons2\smaw\data\equip\W_SMAW_CA.paa"];
                     ctrlSetText [1204, "\ca\weapons2\smaw\data\equip\M_SMAW_CA.paa"];
                    };

             Case 1:{
                     ctrlSetText [1202, "\CA\weapons\data\equip\W_m136_launcher_CA.paa"];
                     ctrlSetText [1204, "\CA\weapons\data\equip\m_M136_ca.paa"];
                    };

             Case 2:{
                     ctrlSetText [1202, "\CA\weapons\data\equip\w_fim92_ca.paa"];
                     ctrlSetText [1204, "\CA\weapons\data\equip\m_Stinger_ca.paa"];
                    };



           };



  };

PisPics = 

   {
       If (!Local Player) ExitWith {};
       _PisWeaponIndex = lbCurSel 2102;
       Switch (_PisWeaponIndex) Do 

           { 

             Case 0:{
                     ctrlSetText [1210, "\CA\weapons\data\equip\w_colt1911_ca.paa"];
                     ctrlSetText [1205, "\CA\weapons\data\equip\m_colt1911_ca.paa"];
                    };

             Case 1:{
                     ctrlSetText [1210, "\CA\weapons\data\equip\w_M9_ca.paa"];
                     ctrlSetText [1205, "\Ca\weapons\Data\Equip\m_m9_beretta_CA.paa"];
                    };

             Case 2:{
                     ctrlSetText [1210, "\CA\weapons\data\equip\w_M9SD_ca.paa"];
                     ctrlSetText [1205, "\Ca\weapons\Data\Equip\m_m9_beretta_CA.paa"];
                    };



           };



  };

ReconPics = 

   {
       If (!Local Player) ExitWith {};
       _ReconWeaponIndex = lbCurSel 2103;
       Switch (_ReconWeaponIndex) Do 

           { 

             Case 0:{
                     ctrlSetText [1217, "\CA\weapons_E\Data\icons\bino_vector_CA.paa"];
                    };

             Case 1:{
                     ctrlSetText [1217, "\Ca\weapons\Data\Equip\W_SOFLAM_CA.paa"];
                    };



           };



  };




PWMags = 

   {
       If (!Local Player) ExitWith {};

       _PWeaponIndex = lbCurSel 2100;

       Switch (_PWeaponIndex) Do 

           {

             Case 0:{PWMag = "30Rnd_556x45_Stanag"};
             Case 1:{PWMag = "30Rnd_556x45_G36SD"};
             Case 2:{PWMag = "30Rnd_9x19_MP5"};

           };





  };


PWAddMags = 

  {
       If (!Local Player) ExitWith {};



       Player AddMagazine PWMag;

  };

PWMinusMags = 

  {
       If (!Local Player) ExitWith {};



       Player RemoveMagazine PWMag;

  };



Edited by Iceman77

Share this post


Link to post
Share on other sites

The "correct" way to do this is:

[] call compile preProcessFile "MyLibrary.sqf"; 

#include can also be used, but it can cause unwanted issues, and it has to be in the same folder.

Share this post


Link to post
Share on other sites

Hey Muzzle. I'm trying to learn too. What exactly is the advantage of using [] call compile preProcessFile "MyLibrary.sqf"over ExecVM, if you need to run a script that has all of the functions? I know I have used

Handle = compile PreprocessFile "script.sqf"; before and then used Call Handle; in a different script. Or do you mean it's not good to have a 1 script containing all functions & that each function should be compiled separately? Cheers.

regards,

David

Share this post


Link to post
Share on other sites

execVM will spawn it in a separate "thread". So your functions/scripts will be loaded at the same time as the file your execVM tried to use them. So anything from nothing to all of your functions could be loaded. When using call instead, the script waits whatever you called to be done - in this case your functions package.

Share this post


Link to post
Share on other sites

Thanks Muzzleflash that worked great. Wow nowhere in the OFPEC COMREF for execVM does it mention that it runs in a separate thread! Worthy of a mention I think, so I'll have to add a comment to that effect. :D

I didn't notice it in in the bistudio wiki either, but looking more closely I notice that it's vaguely mentioned in one of the comments. Not a well documented feature it seems.

Many thanks!

Tomsk

Share this post


Link to post
Share on other sites

Well, execvm and spawn run on seperate threads. Regardless, since muzzle says so, I guess I'll compile the "library.sqf" I have aswell. I just didn't think it had to be done since the library.sqf will only get ran once, to declare the functions inside. Then ofcourse use call after the script is done declaring the functions, to call anyone of the functions. In any case, there's no arguing with muzzle :)

Edited by Iceman77

Share this post


Link to post
Share on other sites
I just didn't think it had to be done since the library.sqf will only get ran once, to declare the functions inside.

execVM is basically the same as 'spawn compile preProcessFile'. So when you see a line like:

[] execVM "myfile.sqf";

Is basically the same as:

[] spawn compile preProcessFile "myfile.sqf";

Using call instead means we wait for the code to be done running. The code we wait for to be done running is the code that defines your functions.

[] call compile preProcessFile "myfile.sqf";

So there is no need to do it more than once. Doesn't matter whether you use execVM or call (except for wait/do-not-wait issue). If you only want to load the library once then only use the above code one time.

Share this post


Link to post
Share on other sites

Gotcha, especially since he's calling his function straight after he execVm his library.sqf, this is a problem with execvm/spawn.

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  

×