that’s what i’ve been doing, good tutorial
Ok after reading this I understand how functions work. Very helpful for other stuff, too, thanks.
EDIT: These questions are for a FilteringEnabled, enabled game environment. That would’ve been nice to add on.
Say you have two ways of counting the player’s money, 1 being a regular IntValue Object:
money.Value = 100
2, being inside the client’s script:
playerVals = {money = 100}
Are they both exploitable? I’m looking for a defense-level as much as the server has, which would be a level of “the client can’t change the server’s values” (which is correct, right?)
If they’re both exploitable, shouldn’t you run a check every time a player fires a gun then, to see if they truly have enough ammo then?
And thanks for posting, this cleared up some things.
If you have FilteringEnabled checked, then yes. Otherwise the client can do whatever it wants.
If you have FilteringEnabled checked, then yes. Otherwise the client can do whatever it wants.[/quote]
Not true. If FilteringEnabled is not checked, the client can edit the data model. It doesn’t mean client can change server memory. I strictly recommend to store the player’s money and other crucial data for your game in server memory and not in the datamodel, FilteringEnabled or not.
The other caveat with this is that client is not able to execute server side code. This goes back to never having any “trust the client” functions in your game if necessary. There are certain exceptions like in a shooter you trust the client to say whether it hit the other person (with some checks for latency of the player and exploit checks) This is still only a bit check and not a execute whatever this client says on the server.
If you have FilteringEnabled checked, then yes. Otherwise the client can do whatever it wants.[/quote]
I strictly recommend to store the player’s money and other crucial data for your game in server memory and not in the datamodel, FilteringEnabled or not.
[/quote]
What do you mean by storing data in the datamodel, like an IntValue in Workspace or under a Player object would be storing in the datamodel? And storing in the server memory would be a table inside server script?
Also would really like if someone, maybe you Sorcus could answer my other question. I changed all my value containers (IntValue, NumberValue, BoolValue) to data tables inside the client script and then called ammo-hacking exploit proof.
My other question:
[spoiler] Say you have two ways of counting the player’s money, 1 being a regular IntValue Object:
money.Value = 100
2, being inside the client’s script:
playerVals = {money = 100}
Are they both exploitable? I’m looking for a defense-level as much as the server has, which would be a level of “the client can’t change the server’s values” (which is correct, right?) [/spoiler]
Not true. If FilteringEnabled is not checked, the client can edit the data model. It doesn’t mean client can change server memory.
Semantics: You have to remember that the client may always edit the datamodel, FilteringEnabled just protects you from ROBLOX automatically sending said-changes to the server to be respectfully changed.
Why you shouldn’t worry too much about validation
That being said, Sorcus is 100% right when he’s saying you shouldn’t start checking everything. Paranoia in security on ROBLOX is a downfall for you, simply because it’s impossible from our end. There’s nothing to stop players from doing the following in your game:
- Shutting down every single server with a malformed packet
- Falsifying RemoteEvent requests (although not from other players as far as I know).
- Editing anything stored on the client to their liking.
- Teleporting any items under their physics control to wherever they want
- Messing with clientside physics in whatever way they want.
- Spamming remote event requests to disconnect everyone.
All data is stored on the client. I don’t try to protect against everything, but I do try to do some basic input validation to help with debugging on RemoteEvents. Most of my validation is used for debugging.
By practice, it’s safer to store all your values in non-ROBLOX objects parented to the datamodel, but in the end, it doesn’t matter a whole lot. Just validate anything really important to your game and chances are no-one will bother to exploit your game.
I would always be sure to authenticate the player requesting specific commands. I know Merely has ran into issues with this before. I like to call warn()
when validation fails to help with debugging.
Corrected the formatting of the original post. (Last post 548 days ago)
Necro-bumping this thread because:
- This is still relevant in 2019, and most devs probably don’t think about this enough.
- This is is still the simplest, most straight-forward explanation of this topic.
- I have something cool to add after dealing with this problem for a long time!
I ran into the problem of input validation a TON when working on my game (Spell Slingers). Spell/ability casting is one of the first features I implemented; it’s mostly stayed the same because I came up with a pattern that feels very organized I figure I’d share it here. The idea here is that I had to find a way to validate many kinds of general spell inputs - like directional spells, ground-target spells, unit-target spells, and so on. I needed to provide the casting system with a general interface to work with so generation/validation can work with all kinds of data.
Disclaimer: I’m the kind of guy that puts gameplay server code in the same ModuleScript as client code.
Object oriented programming is the way to go if you want really nice clean code on Roblox. So, I started with creating an abstract class called CastType
to represent a casting paradigm. This class would be used on the client to generate input data (the direction you want to cast, the unit on which you want to cast, etc) and on the server to validate input data. Here’s the functions involved:
-
CastType.new
- Constructor -
CastType:start
- Called by client when the player starts the spell input (presses down the key to aim) -
CastType:cancel
- Called when the player no longer is providing spell input (releases the key or cancels the cast) -
CastType:finish
- Called right after cancel to generate cast data (direction, unit, point, etc). -
CastType:validate
- Called by the client/server to make sure the data sent makes sense, including type+sanity checks.
Then, I make a subclass of CastType
in order to implement a specific kind of cast type. Let’s say you have a ground-target ability in which you pick a spot on the ground to cast the ability. For this, CastType:start
summons a ring which follows your mouse until CastType:cancel
is called. CastType:finish
then sends the mouse’s world position.
But wait there’s more!
Practically every ability has a range limit. Having the same code for checking cast radii in multiple places violates the DRY (don’t repeat yourself) principle. I made an abstract subclass of CastType
called RangeCastType
for any ability that must validate against the player’s current position. In addition, abilities that are cast on Humanoids can take different forms, like allies, enemies, specific kinds of units (towers/minions). So I have another abstract subclass of RangeCastType
called HumanCastType
to validate humanoids as well.
This is what my class tree hierarchy looks like:
-
CastType
-
NoneCastType
- Dummy cast type that never sends data (for abilities without active effects). -
RangeCastType
- Validates input data against distance from player’s character. Renders the cast range around the player’s character.-
PointCastType
- Renders an X under the mouse where the player aims, sends a Vector3.-
ArrowCastType
- For directional casting. Renders an arrow pointed towards mouse position (instead of an X). -
GroundAoECastType
- Renders an O w/ given radius (instead of an X). -
WallCastType
- Same as point, but renders a wall indicator facing the player (instead of an X).
-
-
HumanCastType
- For spells in which a humanoid must be selected (can also filter).
-
-
Finally, I have a handful of ModuleScripts provide instances of each of these classes. This way, all abilities that share the same cast paradigm use the same instance (and therefore the same visual resources on the client) of their chosen CastType
object. For HumanCastType
, I pass a filter function to the constructor that determines whether the humanoid can be target (ally, enemy, tower, minion, etc).
The result: Now, everything has a single place for potentially many abilities. Sanity checks can be done on the client and the server by using the same objects, just calling different functions. Abilities can make their own subclasses of any of these if they want to “break the rules” a little bit.
Hopefully this gives you some kind of idea of how you can abstract-away the heavy lifting if you’re making a game with tons of content involving validated user input (like ability casting). I’ll talk about this specific case study from my project on my blog some more if there’s enough interest.
Download an RBXM of the above system here!*
*Note: I’m only providing this for educational purposes ONLY. This is public, client-side code used in my game, which is copyrighted.
I am currently making a magic-based game game, I created 5 skills, one is a simple slow projectile, one is multiple faster projectiles, one uses raycasting, one uses AoE and one is also multiple projectiles but with a different casting position.
I created a module that contains data for each skills, the skills are dictionnaries, the latest index for it is a function that fires a different function in that module which creates the attack. (For exemple a fireball.)
A problem i’m having is some skills have unique effects, which makes the skill’s function be pretty long and relies less on the skill’s data (The AoE skill is a fire tornado casted around the player that grows in size and does repeated AoE damage.)
It seems the download link will not work.
So does this mean an exploiter can change anything anywhere, client and server? Say I put values in like the ServerStorage can an exploiter access supposed values and change them freely, or is it only if u were to like store values on the client like a folder in StarterGui or something?
Assuming that there are no backdoors, exploiters won’t be able to access stuff in ServerStorage due to the FilteringEnabled rules. Same thing goes for ServerScriptService. Exploiters can, hovever, modify values in their PlayerGui, PlayerScripts, etc.
Oh, I see so that is why they said it’s safer to store values via script than actual physical value objects as they are in risk of being manipulated through things like backdoors. Thanks for clearing that up for me and, for anyone else reading through this thread who probably had the same question.
Very helpful tutorial! I’ve never even thought about this. I always handle things like this only on the client.
I’ve been trying to do something similar with tweening doors, would it be beneficial to push the OpenDoor event to both the server and the client?
@SteadyOn has a great module for this. It does basic rendering on the clients and sets them on the server. You can read his more in depth post here: TweenService V2 Hope this helps (:
EXACTLY what I’m trying to do, thank you very much!
You have most likely heard the term “don’t trust the client”. I think it is just so much safer to store all the important data on the server. If it is an intvalue or boolean etc on the server, then it should be safe. The best place on the server to store this is serverStorage, or under the script using the values in ServerScriptService. It is just too risky to store any important data on the client.
Great post! The only thing I would add is that you can have the client predict whether a purchase will be successful without waiting for the server’s response. If the client knows it has enough money, it can display the “purchase successful” message before validating the request. The server will still be in charge of authenticating the purchase and awarding the product. The only reason the client would be wrong in its prediction is if you have mismanaged state, or the player is tampering with their state using exploits. If you have mismanaged state, that’s a problem on its own that should be addressed, and if the player is exploiting, that’s their own fault that their UI is now out of sync. If you wanted too though, you could reconcile the changes made after the server authenticates the request.