Server Ownership of Player Model? If not, what's the "correct" solution to tampering?

I specifically joined the dev forums to ask this question, because I don’t see an elegant solution to this fundamental problem in Roblox…

  1. My own testing shows that players always have network ownership of their character even if you set its network ownership to the server. (Can anyone confirm?)

  2. Because of #1, any client tampering (physics, position, walkspeed, etc) is replicated to server+other clients, and we the developers are left to pick up the pieces, causing substantial overhead by having to constantly check if character models are behaving in every conceivable way.

If your game doesn’t need fancy low-lag physics calculations, you’re inheriting a replication headache without getting the benefit of forced client ownership.

What is the most elegant way to handle this conundrum? Having to double-check and reset every client model property on every heartbeat is a serious kludge that can’t possibly be the optimal solution. Is there some way to override client replication that I’m not aware of?

Thanks in advance,
Yevoc

3 Likes

The only tip I can give you is to listen for events signalling that the character’s properties have changed, instead of constantly polling. You can use the .Changed event of Instances, or the GetPropertyChangedEvent method. I think this doesn’t fire when BaseParts move due to physics, so you might have to check the character’s position every heartbeat, or however often you choose to check.

1 Like

This helps a small bit. Thanks. You’re right that Instance.Changed and GetPropertyChangedEvent don’t detect anything about physics:

Instance.Changed API :
“This event does not fire for physics-related changes, like when the CFrame , Velocity , RotVelocity , Position , Orientation and CFrame properties of a BasePart change due to gravity. To detect changes in these properties, consider using a physics-based event like RunService.Stepped

Like you said though, this means I’m still having to check all physics for each player in RunService.Stepped. That’s quite computationally expensive and would limit the max # of players per place. It’s hard to believe this is the best approach.

1 Like

It might not have to be every step. It could be just a few times a second, depending on your exact needs. But yeah, it’s not an ideal situation.

1 Like

I had an idea where the client is expected to enforce physics values on the player and then send encrypted/compressed results of that enforcement to the server, which the server would poll at random intervals and do a quick check to see if its compressed results match the client’s. This reduces bandwidth and server CPU overhead to a minimum, and any client hardware should be able to handle doing heartbeat checks for a single player.

While that may work in theory, all numbers in Roblox are floats, meaning the physical values being compressed client-side could be ever-so-slightly different from the server, which could result in a wildly different compressed result.

I suppose there are workarounds to the inherent number imprecision in Roblox, but those workarounds couldn’t be stated in public, as they diminish the security of the compressed result.

Is there any way to actually work with integers instead of floats in Roblox Lua so that you have no epsilon issues during compression?

Trusting the client is not the solution to game security. It is trivial to “de-encrypt” whatever scheme you devise, as your “encyption” logic is evaluated on the client’s machine.

Furthermore, I don’t understand why “workarounds to the inherent number imprecision in Roblox” would diminish the security of anything?

To answer your question on how to “work with integers instead of floats”, you can simply use math.modf to extract the Integer portion of a number. You can also use math.floor or math.ceil to round if you would like.

2 Likes

Yes, thanks for the sanity check. My last post assumed one could execute pre-compiled bytecode of the encryption process on the client, but that isn’t allowed anymore in Roblox, so like you said, whatever we do client-side is just wide open for perfect data forgery.

How in the world did Roblox think this was the right way to run a client-server model?? This problem is really starting to bother me. You can’t create a decent, stable game without addressing this.

Well, you can execute ‘pre-compiled’ bytecode. That’s how ROBLOX LocalScripts work, as the compiled Luau byte code is replicated to clients to run (source code is not replicated). Compiling your own byte code and then running it on the client is fundamentally the same as what is done now, except ROBLOX just handles it for you.

If you want the server to own the player model, just create your own custom player model. Replicate the player movement vector (the velocity/acceleration vector from user input) + local position vector and verify it on the server, just like how real video games do it. If you don’t want to make your own player model, you can just replicate user movement vector and verify it against the player model position/velocity.

1 Like

I agree. Your suggestions seem to be the logical next step:

  1. Make your own model so that the server can own it.
    OR
  2. Replicate a separate movement vector and verify it against the player model.

Both are viable paths forward. I am basically doing your second suggestion right now.

Thanks.

I’ve been testing and working on this, and I think my approach has been wrong. Instead of treating Roblox’s client replication as a bug, I realize that I should have been using it as a feature.

In my game, total server-side control of the player doesn’t seem to be working out due to unpredictable lag that ends up appearing as input lag, which is a game killer.

To deal with this, I’ve left the periodic server position & physics checks in place, but the client is now responsible for moving the player, not the server. This seems to be the synergy that Roblox’s engineers decided upon, where lag appears minimal to the player and somewhat loose enforcement of physics+positioning keeps the game 99% legit.

I’ll check back whether this approach passes muster, but this is probably the intended approach with Roblox’s current engine.

1 Like

I think handling character movement on the client is the best approach for 99% of games (at least the ones you’ll see on Roblox). Even other games I’ve played like Runescape, in which character physics are not very important, suffer immensely from having input lag.

Maybe this isn’t so bad when you live in the USA, but living in New Zealand having a 300-400+ average ping in Roblox games, I would leave the game after 30 seconds if there was no immediate client feedback. That’s 0.6-0.8 seconds just to see my character move, which would be a terrible experience.

1 Like

Total server-side control of the player is still possible (if you are referring to doing a custom player model that does not use Humanoid to control it). The only problem you are having is that there is no instantaneous player feedback. To fix this issue, you could simply have a Local model of the character that is solely used for instant-local client feedback.

However, ROBLOX does have already pretty good netcode for player movements (as it does a lot of the position interpolation with velocity stuff for you). Verifying player movements is probably the easiest approach that can be reasonably secured.

1 Like