Verifying part touches via the client for client-side interactions

Every now and again, a question pops up on Scripting Support regarding handling part touches from the client. Two points are usually raised in these threads: the events are supposedly replicating despite being connected from the client, or that everyone’s seeing client-connected events.

The intention of this thread is to clear the air about client-based part touch detections, look into some explanations and hopefully provide some useful knowledge.

Before you jump in…

There is an expectation that you have some knowledge about some key terms raised while going through this thread. There are terms such as event, client and replication used which may not be fully understood by new developers. Make sure to search for what you don’t know!

At the time of writing, there are no links attached for any items or references in this article. This is to avoid a mass of blue text taking away from the writing. Once again: make sure to search for what you don’t know!


What is Touched?

Touched is a physics-based event that is fired whenever two BaseParts come into contact with each other. In respect to it being based on physics, this means that one of the parts must be physically simulated. A part enters physics simulation when it is not anchored. CFraming is not physics simulation and CFraming parts into each other will not fire Touched!

In a typical scenario, you will usually use Touched to determine when two parts make contact so you can set up events that happen at the time. One of the most common use cases for Touched is to set up player interaction. For example, when you walk up to a booth, you want the player to see a shop. This is a relevant example for later so keep this in mind.

This event is part of the BasePart class, which means that any kind of part can have functions listen to when this event fires. Here’s an comprehensive list of BaseParts you can use Touched with in alphabetical order:

  • CornerWedgePart
  • FlagStand (deprecated; was an engine-managed object for CTF games)
  • MeshPart
  • Part
  • PartOperation (Unions and Negations) (for Blender users, equivalent of a BooleanOperation)
  • Platform
  • Seat
  • SkateboardPlatform
  • SpawnLocation
  • Terrain (surprisingly, yes, Terrain can be touched!)
  • TrussPart
  • VehicleSeat
  • WedgePart

Where can Touched be used?

Touched can be used anywhere at any time. Both the server and the client are able to have functions listen to when Touched fires. Be wary of their respective limitations however! Replication and script locations are very important to remember. The server cannot use Touched for a part created by the client, but both the server and client can use it for server-created parts. LocalScripts also only run in very specific places, which are specified on its documentation page.

Depending on your use cases and regard for performance and efficiency, this may change where you’re using Touched from. A lot of users have opted to or have been recommended to use the client to listen to Touched for various actions such as showing a shop Gui. That’s where this thread enters.

Why use Touched on the client?

Several points have been raised above which provide a basis for why you might be using Touched on the client, or rather why you should be. Let’s look over those again briefly.

  • Physics-based: The client can run things faster than the server can. When it comes to physics you want accuracy, near-instantaneous feedback and less strain on the server to prevent lag. With this in mind, you’ll want the client to do what it can.

  • Replication: A part may be created by the client and you may want to detect touches on it. The server will not be able to see this part, so the only answer is to have the client handle it!

  • Performance: As stated in the first point of physics, you’ll want to hold the server from handling small actions that the client can do, especially if they hold no importance and the action is ultimately only relevant to the client.

In addition to these three points, there is one that hasn’t been addressed: networking. Let’s summarise that similarly to the points above and then break it down.

  • Networking: When you involve remotes, you start piling up network traffic. For Touched and Humanoids, the event could easily fire off many times and you could end up sending a huge amount of traffic to the client where not necessary.

When looking to fix issues with Touched events and the clients, there are often developers who recommend using remotes. The server listens for Touched and if it is able to find a player, then it sends a signal to them via a remote. This over using the client alone is bad in many ways. Here are two.

  • Traffic: As stated above, you could potentially rack up several tens of requests unnecessarily to the client. This slowly leads into an programming anti-pattern called a code smell. Anti-patterns move from a problem to a bad solution; a code smell is indicative of weak structure. Code smells are not bugs and don’t harm functionality, but they can slow down development.

  • Oversights and traps: The mindset of needing the server to interface all world interactions can be carried over into other works where doing so is not necessary. It’s also easy to get led into code oversights and in some cases, tank on maintainability. Whereas you can have two scripts with a network bottleneck, you can have one script or a chunk in a larger script that handles the entire workflow for the touch event.

How do I use Touched on the client?

There’s only one way to have a function listen to the Touched event. The same way you’d do it for the server works for the client as well. As a little callback to the basics, you can listen directly with an anonymous function or hook a function declared earlier.

Declared:

local function onTouch(otherPart)
    print("Part that touched this one:", otherPart)
end

thisPart.Touched:Connect(onTouch)

Anonymous:

thisPart.Touched:Connect(function (otherPart)
    print("Part that touched this one:", otherPart)
end)

Wait - other clients see my actions!

Which is the point of this thread! Understand this: the connection you establish using the above code is still local. With the way Touched works however, that means that the client is actively monitoring any part touches. It’s not that there’s any replication going on here: the client sees that a part touched something and so the signal is fired.

In order to prevent other clients from seeing your actions, you need to verify within the function that the part that touched the object is part of the player’s character. This is only if you’re making local interactions, such as showing shop interfaces like the example a ways above.

To check a part’s association with the character, we simply need to run IsDescendantOf. If you consider the parent-child hierarchy for Roblox instances, we can use this to check if the part is part of the character’s ancestry.

local LocalPlayer = game:GetService("Players").LocalPlayer

shopPart.Touched:Connect(function (part)
    -- Let's check that a Character is valid as well
    if LocalPlayer.Character and part:IsDescendantOf(LocalPlayer.Character) then
        print("Our character touched the shop part!")
        shopGui.Enabled = true
    end
end)

And just like that, you’re set! It’s all about verifying that the touched part is part of the LocalPlayer’s character, if one exists, then going ahead with the rest of your functions. No remotes, no network traffic, all client-side.


That would be all. If you have questions, comments, general feedback, tips for others or add-ons to provide, feel free to post away! I may have missed some things or trailed off where there could be some more detail, so pointing those out would help me and others.

Let’s keep all replies constructive and relevant. Happy developing!

52 Likes

This is a neat explanation, personally, I avoid using the Touched event because of physics, like you said, you cannot use a Touched event on anchored parts, CFraming a part is more efficient than letting physics move it.

4 Likes

I apologize for bumping this topic

I’d like to ask, how can exploiters use client-side interactions? If there is any way possible, is there any way to validate that data (there could be network traffic, which is one of the reasons for client-side Touched events)?

Thanks.

I’m not too sure I understand the question at hand. Are you referring to the exploitability of client-based touch interactions? That’s dependent on your implementation and use case.

This thread hones in exclusively on the client and touch interactions, as well as situations where the server should not be involved in handling this workflow at all. Guis are purely client-sided, so the server would have no reason to process the touch for the client and incorporate a remote, the client can handle all of that on their own machine.

When it comes to using the client to detect touches and influence a game action (such as client-based hit detection for projectile weapons or melee weapons), that’s a whole different ballpark which requires security implementations on your end through the server which work to validate if a shot is possible. Naturally you’ll have to rely on the client some for these types of actions so instead of checking for an explicit set of instructions, you check if something should be possible.

Sanitisation or modification of sent data is acceptable. For example, if the client is sending a position, the server can add an offset to make recoil and conduct the shot accordingly.

2 Likes

Can the client use .Touched on server created parts with networkowner set to server?
I was making so a bullet stops when .Touched from client but it seemed not to work everytime, so when I went to my serverscript and put :SetNetworkOnwer() to see if it would fix the problem the script stopped working forever.

I wanted to make the bullet precise, so I wanted to check collision from every client and make an average of the positions to rule out exploiters that sent absurd positions, as server seems to fire .Touched about 10 studs before actually colliding with other parts, but it seems that it wont work like that.

I once tried shooting a bullet for every client (Lime green = serverbullet, pink=clientbullet) too, but roblox physics aren’t precise… sometimes a ball bounces to the left and sometimes to the right, even if shot position, physicalproperties direction and velocity were the same.

Also tried creating my own physics specificly for moving balls, but I can’t figure out how to make them have friction/slide.

I haven’t tested that, that’s something you might want to have a look into testing out. I do believe that Touched does replicate but if the server owns the physics of the part then Touched will be firing on the server’s terms, meaning that there might be a discrepancy in when the client fires Touched listeners and where the part is according to the server.

Sorry, but I don’t have much knowledge when it comes to physics and projectiles. This tutorial was primarily created in relation to users finding that everyone was affected by a Touched event connected on the client primarily because they didn’t check if the touching character belonged to the client. It wasn’t quite intended for topics like projectile latency and such.

1 Like