ZDaemon Forum Index ZDaemon
Client/Server DOOM
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Improved Random for Open and Enter Scripts

 
Post new topic   Reply to topic    ZDaemon Forum Index -> Implemented Requests
View previous topic :: View next topic  
Author Message
Krawa
Spamming!


Joined: 23 Nov 2008
Location: #SDA

PostPosted: Fri Jan 03, 2014 8:52 pm    Post subject: Improved Random for Open and Enter Scripts Reply with quote

Random on Enter Scripts starts always with the same number. Every time you restart e.g. map01 you get the same number. Same happens with other maps, they all have their own number.
Something similar happens with Enter Scripts, but for the first player, only.
It happens Single Player and on servers.

Demo wad map01 and LOADACS

Simple code example:
Code:
script 601 open
{
Print(s:"open script: ", d:Random(0, 255));
}
Back to top
View user's profile Send private message Send e-mail
Krawa
Spamming!


Joined: 23 Nov 2008
Location: #SDA

PostPosted: Fri Jan 03, 2014 9:24 pm    Post subject: Reply with quote

Workaround using cv_gmtime:

Code:
function void Randomizer(void)
{
int rand = GetCVar("cv_gmtime") % 256;

for (int i = 0; i <= rand; i++)
    {
    Random(0, 255);
    }
}



script 1 open
{
Randomizer();
Print(s:"open script: ", d:Random(0, 255));
}



script 2 enter
{
Randomizer();
Print(s:"enter script: ", d:Random(0, 255));
}


Back to top
View user's profile Send private message Send e-mail
UberGewei
Spamming!


Joined: 01 Sep 2010
Location: The Netherlands

PostPosted: Fri Jan 03, 2014 10:43 pm    Post subject: Reply with quote

Thanks Krawa! Wink
Back to top
View user's profile Send private message Visit poster's website
Sr69Mm-jC
Spamming!


Joined: 21 Jul 2011

PostPosted: Fri Dec 12, 2014 3:57 am    Post subject: Reply with quote

I know this topic is rather old but I'd like to point out of a few things - especially for those who stumble upon this topic while trying to figure out how randomness in Doom works.

1. The problem is not about OPEN and ENTER scripts.
The problem is actually the fact that the RNG seed remains the same each time you initially start the game up. It is hardcoded and it does not depend on any variables that may be different from one game start to another - such as server time, player movement/angles/actions, etc.

2. OPEN scripts are executed on map init.
They are called before anything else manages to call P_Random function (which is the RNG in doom code used by monsters, damage calculations, flickering lights, ACS Random, and other things). No matter how things-that-use-P_Random you have on your map, OPEN scripts will use P_Random before all of those things do.
You see, if you put a long enough delay in an OPEN script before actually using Random() ACS function, then maybe some players could spawn and alert/fight monsters. Since player positions and actions are nondeterministic (that is, they are different every time - unless a demo is played), monsters would also react differently, the damage calculations would be different and thus P_Random would be called different number of times, letting you get actually random values through Random() function in an OPEN script. But this isn't a reliable solution, if it is a solution at all.

3. ENTER scripts are not really a part of the problem.
I believe players can never instantly spawn in the game; there is always at least a few tics of delay between connecting (or starting the map in singleplayer) and entering the game. If you have a simple test map with nothing but a player start then yes, Random() will return the same values (because, again, nothing else is calling P_Random). However, if you have some monsters on the map, then the results will mostly be random - because the number of tics elapsed between connecting and entering can be different, and in the meanwhile the idle monsters could be using P_Random. If it's a multiplayer server then some other players have very likely already caused many P_Random calls. You could even have an OPEN script that uses Random() each tic and that'd be pretty much sufficient. But yes, this is not really a nice solution either.

4. Using GetCVar("cv_gmtime") as a seed is a very bright idea, if not the best one.
Server time is pretty much always different from one game start to another. Using time as a seed for RNG is a very common solution in programming (as long as you simply need unique seeds and you're not concerned about seed security) and it's no different for Doom. Use it.

Slightly unrelated but needed to understand the final section:
5. OPEN scripts are always executed in descending order.
For example, if you have "script 1 OPEN", "script 999 OPEN" and "script 500 OPEN" (defined in this order), then script 999 will be executed first, then script 500, then script 1. Only script numbers matter; it does not matter which script goes first/last/etc in the source code. The same actually applies to ENTER/RESPAWN scripts (if you have multiple of those). I'm not sure about normal (void) scripts.

Finally,
6. Krawa's Randomizer() function is very good but I wouldn't recommend calling it more than once.
It is perfectly sufficient to only use it once, no need to run the loop and read cv_gmtime every time a player enters the game etc. However, if you only call it once you must ensure that it is called before any other OPEN scripts are executed. Hence it should be run in an OPEN script with a script number higher than any other one in your wad. You could simply use script 999 to achieve that.

7. So, here's the best and clearest solution I can think of.
Simply write this anywhere into your ACS code:
Code:
script 999 OPEN
{
    int rand = GetCVar("cv_gmtime") % 256;
    for (int i = 0; i < rand; i++) Random(0,255);
}

P.S. Minor note: "<=" has been replaced with "<". If rand==255 then it doesn't make sense to call P_Random 256 times in a row as that will have no effect.
Back to top
View user's profile Send private message
Kilgore
Air Cavalry


Joined: 17 Jun 2003
Location: Up the river

PostPosted: Sun Aug 02, 2015 4:03 am    Post subject: Reply with quote

From the changelog:
Code:
20. Implementation of 4 new ACS functions for simpler/better
    pseudorandom numbers:

    1. int ZD_rand(int min_value, int max_value);
       Draws a uniformly distributed random number in the specified
       range (the range includes the min. and max. values). It can
       work in "repeatable" mode (via the functions below), but
       that's NOT the default operation; it defaults to "always
       new" drawings.

    2. void ZD_srand(int seed);
       Sets the seed of the random number generator; if the specified
       value is negative, then it will use something "random" based
       on the current time and other parameters. This function gets
       auto-called with seed = -1 on every map load, which results
       in "always new" drawings; if you want repeatability, you can
       call the function with a fixed (and non-negative) seed value
       (eg., 0).

    3. void ZD_rand_savestate();
       Saves the current state so you can restore it later.

    4. void ZD_rand_restorestate();
       Restores a previously saved state so you can re-draw the same
       random numbers.

    These extensions require a new "zdaemon.acs" file for ACC (the
    ACS compiler); you can download it from:
        http://downloads.zdaemon.org/zdaemon.acs
It will appear on the next release (110b04).
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    ZDaemon Forum Index -> Implemented Requests All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group