Jump to content

Recommended Posts

Hello guys,
I am looking for a method to save and edit data across mission restarts.


We are currently scripting a round based multiplayer game mode. After each round, the mission is restarted and a winner and loser team is determined. Now we are looking for a way to track the won rounds per team and the team that has three points first is the winner.
The problem is that with each the mission restarts, the variables are overwritten.
Does anyone have an idea how to achieve it so that the points per round are counted regardless of the mission?


Thanks for your help

Share this post


Link to post
Share on other sites

You should be able to do this pretty easily by setting the variable in the serverNamespace. e.g

//initServer.sqf
if (isNil {serverNamespace getVariable "scoreCounter"}) then
{
  serverNamespace setVariable ["scoreCounter", 0]
};

As this has to be run on the server, naturally you will have to broadcast the value of the variable to all clients whenever they need to access/view it. You will also have to update the variable again with setVariable every time you want to modify it. Alternatively if you wanted the variable to last across multiple game sessions you could use profileNamespace. I've never used either myself so i'm not aware of whether there are any pitfalls to this but this should work for your purposes.

https://community.bistudio.com/wiki/Namespace

Share this post


Link to post
Share on other sites
9 hours ago, Chuggacharles said:

You should be able to do this pretty easily by setting the variable in the serverNamespace. e.g


//initServer.sqf
if (isNil {serverNamespace getVariable "scoreCounter"}) then
{
  serverNamespace setVariable ["scoreCounter", 0]
};

As this has to be run on the server, naturally you will have to broadcast the value of the variable to all clients whenever they need to access/view it. You will also have to update the variable again with setVariable every time you want to modify it. Alternatively if you wanted the variable to last across multiple game sessions you could use profileNamespace. I've never used either myself so i'm not aware of whether there are any pitfalls to this but this should work for your purposes.

https://community.bistudio.com/wiki/Namespace

 

Thank you for your reply.
So I check as long as the server is running if a game was won. If this is the case, the respective team variable should be increased by one in the serverNamespace. If one of the two teams has reached three points, the one team has won and both are set to 0 again.

Do you think this would work?
 

Share this post


Link to post
Share on other sites
17 hours ago, MinervaArts said:

Does anyone have an idea how to achieve it so that the points per round are counted regardless of the mission?

 

As @Chuggacharles mentioned serverNamespace will work, but only as long as the server runs, if a crash or restart happens w/e is stored in that namespace will be lost.

 

If you want to be able to handle that you could store your data on the server using missionProfileNamespace instead. Just remember to call saveMissionProfileNamespace when you've done your updates.

 

Drawback for this approach is that any reset needs to be done in the code.

 

11 minutes ago, MinervaArts said:

Do you think this would work?

Yes

  • Like 1

Share this post


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

 

As @Chuggacharles mentioned serverNamespace will work, but only as long as the server runs, if a crash or restart happens w/e is stored in that namespace will be lost.

 

If you want to be able to handle that you could store your data on the server using missionProfileNamespace instead. Just remember to call saveMissionProfileNamespace when you've done your updates.

 

Drawback for this approach is that any reset needs to be done in the code.

 

Yes

Okay, thanks.
Do I have to run the script in initServer.sqf then? But I think the initServer.sqf is re-run after every mission restart, which would probably reset the points.


I use TADST for the server and I saw that there is a menu for server site scripting. Do you have experience with this or know if it can be used for this?

Share this post


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

Okay, thanks.
Do I have to run the script in initServer.sqf then? But I think the initServer.sqf is re-run after every mission restart, which would probably reset the points.


I use TADST for the server and I saw that there is a menu for server site scripting. Do you have experience with this or know if it can be used for this?

The if statement i posted above only creates the variable if the variable doesn't exist in the specified namespace already, so it won't reset. When you want the score to reset to 0 all you need to do is add a check somewhere to test if either team has reached 3 point and then use setVariable again to make the value 0.

Share this post


Link to post
Share on other sites

One point I never understood (and the explanation is not clear in BIKI) is the use of saveMissionProfileNameSpace (or saveProfileNameSpace as well)... Because when you SET a variable in a name space, example missionProfileNameSpace setVariable ["scoreCounter",1234]; why should you add saveMissionProfileNameSpace ?  Did you SET a variable, yes or no? (yes)

So, saving what for? In which context? (can be resource demanding if always used for nuts).

 

The other point, when testing, you will set variable once and your will probably need to reset your script by missionProfileNameSpace setVariable ["scoreCounter",nil]  (should delete it in this name space and make your initial check available once again, if you want to play the same mission).

 

Share this post


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

Because when you SET a variable in a name space, example missionProfileNameSpace setVariable ["scoreCounter",1234]; why should you add saveMissionProfileNameSpace ?  Did you SET a variable, yes or no? (yes)

Yes you set a variable in the namespace but it was not exported to the file (profile).

Quote

By default the variables set in this namespace will exist while the game is running, and variables are saved persistently when the game is closed. Saving can also be forced by using saveProfileNamespace to prevent data loss on e.g game crash.

  • Game loads
  • Profile vars loaded from file into namespace
  • Vars updated in namespace var setVariable
  • File updated by either...
    • Closing game correctly
    • By saveProfileNamespace

Share this post


Link to post
Share on other sites
3 hours ago, Larrow said:

Yes you set a variable in the namespace but it was not exported to the file (profile).

 

If I'm right, setting a variable with profileNameSpace was enough for exporting it in yourname.vars.arma3profile  file. Perhaps it's not (no more) the case for missionProfileNameSpace...

 

3 hours ago, Larrow said:
  • Game loads
  • Profile vars loaded from file into namespace
  • Vars updated in namespace var setVariable
  • File updated by either...
    • Closing game correctly
    • By saveProfileNamespace

 

As we can't guess when a crash occurs, the question is when is it useful to save into these name space (or more exactly from name space to profile file if I understand). Because I'm rather sure that using a (global, whole) save for many variables, each time you change a setting on one, can be resource consuming. So, when? Periodically? BI could gain in clarity with that.

Share this post


Link to post
Share on other sites
On 10/29/2023 at 9:46 AM, pierremgi said:

 

If I'm right, setting a variable with profileNameSpace was enough for exporting it in yourname.vars.arma3profile  file. Perhaps it's not (no more) the case for missionProfileNameSpace...

 

 

As we can't guess when a crash occurs, the question is when is it useful to save into these name space (or more exactly from name space to profile file if I understand). Because I'm rather sure that using a (global, whole) save for many variables, each time you change a setting on one, can be resource consuming. So, when? Periodically? BI could gain in clarity with that.

The save* commands pushes the data to the file. BI probably only pushes changed data but let's assume otherwise for a worst case scenario. If the game exits cleanly it also commits the changes to disk. 

 

File operations are slow in a computing sense but from human perspective they're still quite fast. My guess is that unless your pushing megabytes of data with each saveProfilenamespace call you don't need to worry about perf too much.

Could do some quick testing.

 

Each save call likely carries with it an overhead for opening and closing the filestream so avoid doing it every frame and preferably batch save as much data as is reasonable.

 

As for when it's useful to save well that depends on your requirements don't it? Consider what data you're saving and either save after each update or, if it's worth the effort, implement an autosave feature.

Sometimes you can just fallback on default data.

 

If the data is likely to change alot and/or the impact of its loss is major then save often.

Example: player position, player gear, time of day (hour)

 

If the data is fairly static and/or is unimportant then you can get away with losing it after a crash.

Example: time of day ( minute / second )

 

We can't predict a crash but can assume they will happen, build protections against them.

  • Like 1

Share this post


Link to post
Share on other sites
_ctr = true;
while {_ctr} do {
    if (!alive p1) then {
        _ctr = false;
        GW_scoreData = profileNamespace getVariable ["GW_scoreData_NS", 0];
        GW_scoreData = GW_scoreData + 1;
        sleep 0.2;
        profileNamespace setVariable ["GW_scoreData_NS", GW_scoreData, true];
        sleep 0.2;
        
        //{hint str GW_scoreData;} remoteExec ["call", 0];
        sleep 1;
        if (GW_scoreData == 2) then {
            "Blufor has won!" remoteExec ["hint", 0];
            sleep 0.5;
            hintSilent "";
        };
    };
};

I have tried around a bit with the namespaces but somehow I can't come up with a solution. I used the following script for testing:


I run the script to test in the initServer.sqf so that the values are stored in the server namespace. When I reload the mission, the script should read the current value and increase it by one. After the second reload, the second if function should be executed.
Does anyone have an idea where the error could be?

Share this post


Link to post
Share on other sites
27 minutes ago, MinervaArts said:

I have tried around a bit with the namespaces but somehow I can't come up with a solution. I used the following script for testing:


I run the script to test in the initServer.sqf so that the values are stored in the server namespace. When I reload the mission, the script should read the current value and increase it by one. After the second reload, the second if function should be executed.
Does anyone have an idea where the error could be?

profileNamespace variables can't be broadcast,  since GW_scoreData is the same value as GW_scoreData_NS anyway, you might as well just broadcast that instead.

Share this post


Link to post
Share on other sites
2 hours ago, Chuggacharles said:

profileNamespace variables can't be broadcast,  since GW_scoreData is the same value as GW_scoreData_NS anyway, you might as well just broadcast that instead.

 

Do you mean like this:

_ctr = true;
while {_ctr} do {

    if (!alive p1) then {
        _ctr = false;
        profileNamespace getVariable ["GW_scoreData_NS", 0];

        GW_scoreData_NS = GW_scoreData_NS + 1;
        sleep 0.2;
        profileNamespace setVariable ["GW_scoreData_NS", GW_scoreData_NS, true];
        sleep 0.2;
        
        //{hint str GW_scoreData;} remoteExec ["call", 0];

        sleep 1;
        if (GW_scoreData_NS == 2) then {
            "Blufor has won!2" remoteExec ["hint", 0];
            sleep 0.5;
            hintSilent "";
        };

    };
};

Share this post


Link to post
Share on other sites

No, the problem is with setVariable.

profileNamespace setVariable ["GW_scoreData_NS", GW_scoreData, true];

The third argument is optional and determines whether the server will broadcast the variable publicly to all clients after it is made. The problem is profileNamespace variables can't be broadcast, so instead of using the third argument, when you want players to receive the GW_scoreData_NS value,  you just broadcast GW_scoreData instead because they should be the same value like so:

GW_scoreData = profileNamespace getVariable ["GW_scoreData_NS", 0]; 
GW_scoreData = GW_scoreData + 1; 
publicVariable "GW_scoreData"; //GW_scoreData is broadcast to all clients
sleep 0.2;
profileNamespace setVariable ["GW_scoreData_NS", GW_scoreData]; //GW_scoreData is used to set the value of GW_scoreData_NS, both variables contain equal values
//instead of profileNamespace setVariable ["GW_scoreData_NS", GW_scoreData, true];

 

Share this post


Link to post
Share on other sites
19 hours ago, Chuggacharles said:

GW_scoreData = profileNamespace getVariable ["GW_scoreData_NS", 0]; GW_scoreData = GW_scoreData + 1; publicVariable "GW_scoreData"; //GW_scoreData is broadcast to all clients sleep 0.2; profileNamespace setVariable ["GW_scoreData_NS", GW_scoreData]; //GW_scoreData is used to set the value of GW_scoreData_NS, both variables contain equal values //instead of profileNamespace setVariable ["GW_scoreData_NS", GW_scoreData, true];

Thanks for your help 🙂
When I run the code in the initServer.sqf it works as it should. I want to execute the script in an extra script at the end of the round though. I already tried to execute the script with "myScript.sqf" remoteExec ["execVM", 2]; but unfortunately it didn't work that way.

How can I make the script run only on the server at a certain time?

Share this post


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

Thanks for your help 🙂
When I run the code in the initServer.sqf it works as it should. I want to execute the script in an extra script at the end of the round though. I already tried to execute the script with "myScript.sqf" remoteExec ["execVM", 2]; but unfortunately it didn't work that way.

How can I make the script run only on the server at a certain time?

When the mission is ending you could either use remoteExec to execute a script/command only on the server by setting the targets argument to 2, or you could use isServer to check in an if statement whether the current machine is a server, and the if statement will only be true on the server. You might also be interested in exit.sqf just keep in mind this is run on all clients so you will have to use one of the two options above if you put it in exit.sqf,

 

Share this post


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

When the mission is ending you could either use remoteExec to execute a script/command only on the server by setting the targets argument to 2

As written in the previous post, I already tried this but I think I screwed it up somehow. Can you tell me how to execute a script with remoteExec?

Share this post


Link to post
Share on other sites
6 hours ago, MinervaArts said:

As written in the previous post, I already tried this but I think I screwed it up somehow. Can you tell me how to execute a script with remoteExec?

Apologies i didn't read your post properly.

 

That might be a problem with the syntax of execVM, your example may be trying to use execVM as an argument rather than the filename. Try this:

 [[], "myScript.sqf"] remoteExec ["execVM", 2];

 

 

Share this post


Link to post
Share on other sites
On 11/1/2023 at 12:38 AM, Chuggacharles said:

Apologies i didn't read your post properly.

 

That might be a problem with the syntax of execVM, your example may be trying to use execVM as an argument rather than the filename. Try this:


 [[], "myScript.sqf"] remoteExec ["execVM", 2];

  

 

Thanks for your help. The way you wrote it, it worked 🙂

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

×