How to synchronize random value to clients without trusting the client and without server communication?

How would I synchronize a random value across the server and the client without using server-to-client communication, and without trusting the client to send this value to the server. I need a random value to be the same across the server and client when a remoteevent is called. I’ve tried using os.time(), tick(), game.Workspace:GetServerTimeNow(), and other methods, but all of those return slightly different values across the server and client as the client calls it earlier than the server. I’m fine with trusting the client to generate their own random value, but this value must sync with the server, and they are not allowed to send it to the server. Is this even possible?

1 Like

put a NumberValue in ReplicatedStorage which is set by the server (so clients cant change it)

The Random object has an argument called “seed”. If the seed is the same, the sequence of generated numbers are the same:

--|| client
local ClientRandom = Random.new(1)

print(ClientRandom:NextNumber())
print(ClientRandom:NextNumber())
--|| server
local ServerRandom = Random.new(1) --> same seed as client

print(ServerRandom:NextNumber())
print(ServerRandom:NextNumber())

Running these both on the client and server will yield the same results.

I knew about seeds, but not the NextNumber() method. Is there a way to prevent this method from getting desynced, for example if the client fails to fire the remoteevent to the server, could we not update the random seed?

You can always update the client with a new seed that both the client and server are now using. There really isn’t a good general way of doing this. If you can name a specific use case, I may be able to think of a better solution.

If a desync occurs, you could potentially call the NextNumber() method on either the client or server until both numbers match.

EDIT: FartedWayTooHard’s idea is better, use it instead.

As an aside, could you provide some context to your question? You might get better answers if we know why you need two synced random numbers.

Updating the client would require the server to communicate with the client. I am using this in a gun script, where the gun has a chance of getting jammed. If the gun is jammed, everything is handled on the server so the client can’t exploit to get their gun unjammed. I would like this random number to sync with the client so the client actually knows their gun is jammed, instead of having no idea why they can’t fire their gun. The reason I don’t want the server to communicate with the client is because the client already communicates with the server to tell it they fired their gun, so I believe for the client to wait for the server to tell them if their gun is jammed is inefficient, as this would require more time lost and may not be optimal, especially for slower devices.

Ah, I see. This can be solved with a few tweaks to how jamming is implemented.

Instead of running a random value every single time you shoot, why not simply specify the ammo capacities that jam the gun? For example, the gun will jam once it reaches 17 or 9 ammo, and the jamming capacities change every reload. This way, you’d just have to send the client the ammo amounts that cause jamming which removes the problem of synchronization entirely.

1 Like

That’s actually a really good idea, but I wanted the gun to have a chance to jam if it is shot too fast, instead of just randomly at any time. I probably should mention that server-to-client communication is allowed only when the client equips the gun.

The aforementioned method should still work even if it only jams when fired too quickly. You’d just need to add an extra condition that jams the gun if it reaches a jamming value and if the player is shooting the gun quickly.

Why is this the case? Surely you can send more signals when you reload the gun.

Alternatively, you can create a separate remote event for jamming values.

--|| sample implementation

local jammingValues = {1, 5, 11}
gun.Shot:Connect(function(currentFireRate, bulletsLeft)
   if currentFireRate > 5 and table.find(jammingValues, bulletsLeft) then
      print("gun is jammed")
   end
end)

NewJammingValues.OnClientEvent:Connect(function(newJammingValues)
    jammingValues = newJammingValues
end)

Oh, I did not think of that either. For some reason I was under the impression that I would need different random values for depending on how fast the gun is shot, but now that I think about it, that wouldn’t be the case. As for why I don’t send more signals when the gun is reloaded, it’s just because I never needed to, and I wanted to minimize server-to-client communication as much as possible.