I am currently working on a weapon system and in that occasion, I am obviously using raycasts. I have read some contradicting information regarding this topic, so I reckoned my best shot would be to ask here. Essentially I want to make a responsive but also (as much as possible) exploiter proof.
I originally planned that to have the raycast be fired from the client and then do the raycasting (etc.) on the server. People told me to refrain from that since it could produce some latency and ruin the player experience, since the player wonāt take damage at once (maybe). So I then had to rethink this, I found that the best solution, and most used solution, would be to have the raycast on the client and then try to validate/verify the results on the server. So my question is basically what I should verify? What information should I send, and how would I verify the results?
I also had one other idea, and that would be to have a raycast on the server also that would check.
So, my idea would be as following:
Raycast on the client
Verify results and take damage.
Have a raycast (after damaging the player etc.) to double check, and if it turns out that raycast doesnāt give me the same results as the client one, give the health back to the player (revoke all the things made, annul the results)
Would that be a good idea?
Looking forward to your ideas and thoughts. (So please tell me some things I should verify, and if you want, also tell me if the idea with the second raycast is good or bad. Thanks)
Best regards!
(I know there are some post that look alike, but trust me Iāve researched and havenāt found the answer I am looking for, whilst I also want feedback on the idea that I had.)
On the client, you definitely want to show all the visuals. I wouldnāt worry about changing their health on the client, but an indication that they hit a player should be good.
On the server, it getās a bit more tricky. There are a lot of things you can check. The more, the better.
To give you an example: last month I created a traveling projectile. Here are the things I verify:
Start position of the throw. Is the player even near this position when they start throwing?
Throw Velocity. Is the velocity a realistic velocity?
Throw per second. Are they throwing 100 projectiles in a second when the client only allows them to throw once every 2 seconds?
In your case, instead of throw velocity, you would want to check two things: Where the ray hit and what it hit. Using this information, you can shoot your own ray on the server. If the server was able to reach that hit position, you can verify that there wasnāt a wall in the way.
Now this is where it gets tricky. You also need to check if what they hit is the same thing your server hit. However, do not forget that there is latency! On the client, a shot could have hit a player, but just barely miss on the server! This is where you have to decide if you want to compensate for that.
Trust the client more and just check if the player they hit is at least very close to where they said they hit it? Or donāt trust the client at all? They both have drawbacks. If you trust the client even a little bit, it can get abused. However, if you donāt trust the client, players with higher latency will almost always miss their shots on a moving player, even though on their client they are hitting their target every single time (this is super fustrating!)
There might be ways to get around this, but it isnāt trivial at all. One possibility is to essentially build a ātime machineā where you can verify results in the past second. Another one is to use the playerās velocity to help go back in time and see if it hit.
Anyway, the last bit might be a bit extra for Roblox. Good luck!
Thanks for your extensive answer, I appreciate that!
Regarding the above comment, if I give the exact same hit position and starting position what could result in 2 different ray casting results? How come the server might regard it as a miss (if they have the same parameters)
Thanks
Edit:
Also, could you explain a little more about latency? Do you mean latency from the client to the server, so if someone has a bad connection it will take time for the server to āreactā? Thank you.
Technically, you donāt shoot the ray just to the hit mark. You ray should be a normal length shot you would do on the client. However, the ray should at least reach where you hit.
Latency (aka lag) is the the delay from the client and server. An excellent connection would have a 20 ms delay. A terrible connection would be 200ms and more. If you click and shoot a player, it would take 200ms to reach the server. While on the client you hit the person right in the face, the server would regard it as a miss as it took 200ms for the server to do its own version of the shot. In 200ms, a player can travel a great distance (could be over a few studs!) which is enough for the server to regard it as miss. This is where the problem lies.
Many games combat this problem differently. Some games might have a 100ms charge time before a shot, giving the client enough time to tell the server āhey, Iām going to shoot this in this direction in this many seconds.ā Other games do traveling bullets (which is what I did.) With traveling bullets, you can have the server āspeed upā the shot once it receives it from the client, that way the client shot and server shot are in the same exact position. Itās not hard to do, but you have to synchronize a clock.
My first idea would be to have a raycast on the client that only gives visual feedback, like a hit icon on the player etc., and then fire the server which verifies and deals the damage. (And has itās on raycast) - (Hmm, does this even work, I feel like all my solutions are terrible)
I really canāt fully get a grasp of thisā¦ (although Iāve learnt a lot from you)
Sorry if you feel like youāre spoonfeeding me, but could you give me some really vague pseudo code:
like:
client detects
fires server
server verifies etc. etc.
Because I am really confused and itās such a hard topic, yet so important.
on click {
fire ray
tell server
do all visual actions
}
Server Script:
on client event {
verify_shot(info)
if verified {
do damage
tell other clients
}
}
func verify_shot(info) {
check if player has gun equipped
check if they have amo to make the shot
check if initial shoot position is near current gun fire position
check if fire rate isnt realistic - save the last tick() and compare it with the current tick
shoot ray
-- OPTION 1, NO TRUST
act based on shoot ray result, return true
-- OPTION 2, SOME TRUST
make sure it reaches to or past info.hitposition
check if info.hitpart is at least ~5 studs away from the info.hitposition
if both above are true, act based on info's hitpos and hitpart, return true
}
What would you say is a reasonable false margin, like, how far can the player move within the time it takes fro the serve rto verify it?
And also, with the hit position, if there is lag (as you wrote) 5 studs is probably good. The biggest hitbox a player could have then is:
(5 studs to each side, hence 10^3)
Could you explain a little bit about the exploiting possibilites? Like, can a person fake lag and then be able to shoot 5 studs off and still get awarded?
Thank you a lot so far, I am starting to make the sytem right now and I will share it as soon as itās done. Thanks @Ninja_Deer, you are truly a lifesaver
Since you are trusting the client a bit, then yes itās possible exploiters can do something if they understand your system. If I were you, if you see a player where many times the server rejects the client (especially when they shoot more than they have bullets or shoot through walls) you should either kick them or send a Mod to the server to investigate.
How would I do that? I am really out of ideas, so it would be grat if you could help me. Thanks!
And lastly, how would I do the server raycast comparison. Like, how would I see if the goes to the point (well that I know) but (say the player moved withing the latency time) and I have to check if it went to that position or further. Pure mathematically, how would I do that?
Since I obviously can do raycast[āPositionā] == localRaycastPosition
how would I check if it goes further? I am just unsure. Thanks. Or do I do .Magnitude? Not sureā¦
So when you fire, you send the start position of the bullet to the server. The server should check if that start position is anywhere near the gun would typically fire (so from the actual gun handle for example.)
You could shoot a ray on the server to hit the position described by the client. If thereās a wall in the way and itās not a moving wall, flag the player to get checked by a mod or potentially kicked. If the client said it hit x player and it actually did, go ahead and do damage. Otherwise, check if the position they claimed they hit is somewhat near the player using magnitude or whatever method you want. This is the risky part that can get abused, but you get better player feedback.
an exploiter could send one position to the server, and then another position to the raycastā¦ So I cannot trust the position that the client gives me (if you get my drift). Is there any way I can guarantee that the value being sent to me, is the samea s the value used in the LOCAL raycast? I hope you get what I mean.
I just have some issues with correlating the server and client positions. As you can see in the images, if the player that was shot at moves a little to the sides, the bullet will go further out and hence be more than 5 studs away from the player, although this should (in my opinion) give damage.
This is maybe what the client registers:
And this is what the server might register (from the same info):
Here you can see that the ray traveled further since it wasnāt intercepted by the character, hence, being technically more than 5 studs away. So in that case, you should measure the distance like this:
See the green arrow, somehow need to null one of the axis in order for only the āsideā distance to be measured.
What I basically have to find out, is, how to detect if the serverRay goes further than the localRay position and then only account for the side distance.
The issue though, is in order to remove the āfront distanceā, you have to know which one it is. Like, X,Y or Z.
Since you can technically shoot in the Z, X and Y direction.
I hope you get what I mean. Iām not very good at maths and such, so it would be a real life saver if you could help me on this also!
Man, I really canāt thank you enough!
Edit: I found out that I could use something called: ClosestPoint(). BUT: it only works with the deprecated Ray. (Ray) So, back to square one.
Sorry to necro-bump, but Iām also trying to follow along and use this thread for my own gun system.
Why donāt you just make the client pass the intersection point of the raycast from the client? The server can just raycast from the position from the gun to the intersection point and it wonāt extend pass the character. (Please correct me if I misunderstood)
Edit: Nevermind, using intersection seems to not work for me
Wouldnāt this be a risky check since the player can unequip or drop the gun?
Yeah ten days before I was thinking the player could shoot somebody, and then quickly unequip right after, breaking the script or something. Then I realized that would be impossible because the event literally fires instantly, so there would be no way that the check would even break from anything the player does right after.
Sorry for the logic I had, I can assure that I do check if the gun is equipped on the player now hehe