Jump to content

[QUESTION] Random selection in table.


Moony

Recommended Posts

I have this:

-- 2 points on each city.
local spawnCoords = {
	{1125, -2036, 69.89, -90},
	{2504, -1686, 13.55, 45},
	
	{-1972, 643, 46.57, -45},
	{-2720, -317, 7.85, 45},
	
	{2023, 1008, 10.83, -90},
	{1298, 2084, 12.83, -90},
}

-- all skins.
local validSkins = {0, 1, 2, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312}

function spawnPoints (player)
	local x,y,z,r = unpack(spawnCoords[math.random(1,#spawnCoords)])
	spawnPlayer(source, x, y, z, r, validSkins[math.random(1,#validSkins)])
	fadeCamera(source, true)
	setCameraTarget(source, source)
end
addEventHandler("onPlayerJoin", getRootElement(), spawnPoints)

Althought it works, after giving it several tests, I realized the spawn locations where always in the same order, as well as the skins.

Coincidentally, I've reached the tables subject in the MTA's tutorial, and I learned about <for key, value in pairs(table) do>. Contrary to "ipairs", "pairs" is supposed to give you a random reading throughout the table everytime the script is ran.
I used 'broph.Lua' as a way to understand the process, and to make sure that if I do the same, it'll work.
My small knowledge told me that, by adding <for key, value in pairs(spawnCoords) do> right after the beginning of the function, I would get the random effect of the table reading. However, the result is far from what I expected: it destroys the function, and the player doesn't spawn at all (black screen).

I'm continuing the tutorial, but 'in pairs' is the last thing NanoBob mentions about the tables.

This time, I'd like a bit of a challenge, so if you could give me the hints and point me in the right direction, I would appreciate that.

Link to comment
43 minutes ago, _Ace said:

add a random seed https://en.wikipedia.org/wiki/Random_seed

without a seed everytime you run the resource you will get the same sequence of skins/spawns

Using the server tick count as seed is a way of doing it, try this:


function spawnPoints (player)
    math.randomseed(getTickCount())
    local x,y,z,r = unpack(spawnCoords[math.random(1,#spawnCoords)])

 

Excuse my possibly noobish questions, but:
- Why are the parentheses next to 'getTickCount' empty? Is there supposed to be any value (not necessarily here, but maybe in any other function)?
- How does the randomseed directly affects the selection of the coordinates within the table? Shouldn't you tell the 'math.randomseed' where to select pseudorandomly?

Edited by Moony
Link to comment

getTickCount don't take arguments so the parentheses are empty, to check if a specific mta function take arguments, see the "syntax" part in the wiki

about the randomseed it is using the current milliseconds of the server so it is the same of changing argument in math.random() every time a millisecond pass, it will make the random much less predictable, more details in the Lua site, scroll to randomseed in http://Lua-users.org/wiki/MathLibraryTutorial
 

 

  • Thanks 1
Link to comment
23 minutes ago, _Ace said:

getTickCount don't take arguments so the parentheses are empty, to check if a specific mta function take arguments, see the "syntax" part in the wiki

about the randomseed it is using the current milliseconds of the server so it is the same of changing argument in math.random() every time a millisecond pass, it will make the random much less predictable, more details in the Lua site, scroll to randomseed in http://Lua-users.org/wiki/MathLibraryTutorial
 

 

Would it then be correct to say that 'math.randomseed' is affecting everything under it inside the function? Will it affect other functions outside this function that are above or beneath?

Edited by Moony
Link to comment

I tested it and indeed it does affect everything outside above and below the code (server side, after using the other function with randomseed) also trying to use local math.randomseed(getTickCount()) gives and error and the script doesn't load

  • Thanks 1
Link to comment

Dear Moony and dear _Ace,

I would like to suggest moving the seeding of the pseudo RNG to start of the resource:

-- Seed at start of resource, not predictable.
math.randomseed(getTickCount());

function spawnPoints (player)
	local x,y,z,r = unpack(spawnCoords[math.random(1,#spawnCoords)])
	spawnPlayer(source, x, y, z, r, validSkins[math.random(1,#validSkins)])
	fadeCamera(source, true)
	setCameraTarget(source, source)
end
addEventHandler("onPlayerJoin", getRootElement(), spawnPoints)

math.randomseed does affect the pseudo RNG generation in the entire resource.  There is nothing you can or should do about it because random is supposed to be unpredictable. Even setting the seed counts as unpredictable.

Advantage of moving the seeding outside of the "onPlayerJoin" event is that the seed is even more unpredictable. Imagine if a player did measure the time from connect to the onPlayerJoin event, does know the tick count of your server and uses that information to reliably select the skin and spawn location.

  • Thanks 1
Link to comment
8 hours ago, _Ace said:

I tested it and indeed it does affect everything outside above and below the code (server side, after using the other function with randomseed) also trying to use local math.randomseed(getTickCount()) gives and error and the script doesn't load

I don't remember why I put it there, but next to the join function, I set a 'setSunSize' and 'setMoonSize'. I can't imagine how it could affect the other scripts, but if the randomseed is in the same .Lua as the join function, then I should move the celetial bodies' size to a different resource, right?

 

5 hours ago, The_GTA said:

I would like to suggest moving the seeding of the pseudo RNG to start of the resource:

math.randomseed does affect the pseudo RNG generation in the entire resource.  There is nothing you can or should do about it because random is supposed to be unpredictable. Even setting the seed counts as unpredictable.

Advantage of moving the seeding outside of the "onPlayerJoin" event is that the seed is even more unpredictable. Imagine if a player did measure the time from connect to the onPlayerJoin event, does know the tick count of your server and uses that information to reliably select the skin and spawn location.

Do you know, by any chance, how the random seed could affect other scripts that don't have a random factor? Like I told _Ace, a good example would be the size of both sun and moon.

EDIT: would it be enough to just set up another server-type .Lua with the sun and moon, but within the same folder (being fetched by the same meta.xml that fetches the join script)?

Edited by Moony
  • Thanks 1
Link to comment
6 hours ago, Moony said:

Do you know, by any chance, how the random seed could affect other scripts that don't have a random factor? Like I told _Ace, a good example would be the size of both sun and moon.

EDIT: would it be enough to just set up another server-type .Lua with the sun and moon, but within the same folder (being fetched by the same meta.xml that fetches the join script)?

I think you discovered a "flaw" in MTA's resource system: sharing of the RNG seed across multiple resources. This can be considered an exploit because a non-admin resource can corrupt the RNG of an admin resource. You could post on the bugtracker of MTA to address this, but I don't think that they want to mess around with Lua too much.

So the answer is: the random seed affects every other resource running on the client or serverside (depending on where you did call math.randomseed); once you use math.randomseed it counts for the entire server or the entire client, especially across resources. It could even affect MTA native logic if it happened to use CRT random anywhere.

Relevant Lua source code: https://github.com/multitheftauto/mtasa-blue/blob/master/vendor/Lua/src/lmathlib.c#L209

Link to comment

math.randomseed can affect the RNG of internal MTA logic, yes, but it also works the other way round too -- your math.randomseed can be overridden by internal MTA logic. This could affect seed-based generation if it happens to be interrupted by a map resource with XML vehicle definitions starting for example, which invokes MTA's internal RandomizeRandomSeed function which overrides srand (and by extension math.randomseed)

Link to comment
17 hours ago, The_GTA said:

I think you discovered a "flaw" in MTA's resource system: sharing of the RNG seed across multiple resources. This can be considered an exploit because a non-admin resource can corrupt the RNG of an admin resource. You could post on the bugtracker of MTA to address this, but I don't think that they want to mess around with Lua too much.

So the answer is: the random seed affects every other resource running on the client or serverside (depending on where you did call math.randomseed); once you use math.randomseed it counts for the entire server or the entire client, especially across resources. It could even affect MTA native logic if it happened to use CRT random anywhere.

Relevant Lua source code: https://github.com/multitheftauto/mtasa-blue/blob/master/vendor/Lua/src/lmathlib.c#L209

Would this happen even if I created a script that runs on its own resource folder, without any other scripts in the same .Lua?

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...