ClientCast - A Client-based, Idiosyncratic Hitbox System!

I have an issue with disconnecting the custom ontouch event provided by the module. My debounces are disregarded by the function. Here is an example gif of me trying to create a repro. I am clicking multiple times before rotating my character. As soon as I stop clicking i move my sword onto the target. You can see that (3x) are running even with the debounces present.

https://gyazo.com/7a6aa662a0ecf10c1108165acb47eb19

My thoughts are as following:

  1. My debounce function is done incorrectly (I dont think it is)
  2. The customised event is not disconnect with ClientCaster:Stop() function
  3. I might have misunderstood the api function.

I am connecting this event whenever the player is attacking (left mouse button down). ClientCaster is only created once outside of the attack remote.

local DamageDebounce = false
		
	
	-- Connect damage when hitting a humanoid --
	ClientCaster.HumanoidCollided:Connect(function(RaycastResult,HitHumanoid)
		if DamageDebounce == false then -- avoid damaging again 
			if RaycastResult and RaycastResult.Instance and RaycastResult.Position then
				if not RaycastResult.Instance:IsDescendantOf(char) then -- Avoid hitting yourself
					DamageDebounce = true
					print('should only print once pr attack | Event Number: ',EventNumber)
				end
			end
		end
	end)
	--------------------------------------------

is called whenever a player is clicking. There is a cooldown to the clicks and it prints out whenever the attack is being performed.

Here is a repro file:

Repro ClientCaster.rbxl (115.6 KB)

Edit:

Since I need the ontouch event in the attackremote, the best way is to create the clientcaster object on each attack and disconnecting it with clientcaster:Destroy(). This seems to have fixed the issue. I was trying to place the ClientCaster.HumanoidCollided event outside of the attackremote which I think would solve the issue, but there were too many important variables that I wanted locally for the attack function.

Great module, really helps when working with client side melee attacks.

1 Like

The issue here is that you are creating a debounce inside the OnServerInvoke function - if the remote is called 5 times, it would connect 5 different HumanoidCollided events and create 5 different debounces. You can see this by using the following debug code:


Which outputs the following:
image

The issue here isn’t with ClientCast but rather your code. ClientCast isn’t able to magically ignore your debounce.

1 Like

Yeah thank you for answering. Thats why I was wondering if perhaps the connection was not disconnected after the invoke. I am aware that I connect a new HumanoidCollided event on each invoke, but I thought it would be disconnected with ClientCaster:Stop(). To sum it up the debounce was intended for only that one event which i would disconnect after before remaking a new event. Just so there are no misunderstanding, do ClientCaster:Stop disconnect the HumanoidCollided event or do I need to call ClientCaster:Destroy() to do that. Anyways thank you for the amazing module

The ClientCaster::Stop method does not disconnect it for you - the reason as to why Destroy does it is because it fully cleans up everything in the object - for performance sake though, you should try using only one caster per weapon, and disconnect the events by yourself (or, connect an event outside of the remote and using variables to communicate).

1 Like

Im making a combat system with a L Hand attack and a R Hand attack. What would be the best way to set this up? Right now Im updating the Caster Variable everytime the player Left or Right Clicks.

you say " If you would like to take into account for the player’s ping, you can easily get their ping through ClientCaster:GetPing()" how exactly would you do this?

Is this module intended mainly for singleplayer games or similar designs, where exploiting isn’t a concern? I don’t see how trusting the client with handling hitboxes is a good idea.

It took me about 10 minutes to figure out how to abuse the remote event to damage any player on the map, I’m not sure if I’m using the module wrong (I used the example code provided in the github link, with the exception that i handled the hitbox to the client, obviously) or if I’m supposed to handle verifying the raycasts myself (which would beat the entire purpose of the module and i’d be better off doing hitboxes server side).

Please read the whole thread.

Let’s take a simple case scenario: your game has 10,000 active players. Only about 50 of them are going to be exploiters, and the rest are going to be players that could potentially have high ping, latency issues, and the like. I would much rather be 3-4 studs (smaller than you think!) more lenient with hitboxes (maybe even less depending on how you implement it!) with 0.5% of my playerbase, and let the other 99.5% enjoy a better combat experience.

Depending on your level of expertise, you can implement a complex backtracking system like I do for my game - I rollback animations, and thus am able to verify hits from the client within 0.1 stud accuracy. This thus completely mitigates any chances of exploiters being able to do anything - a best-of-both-worlds scenario.

How does that beat the purpose of the module? The purpose of the module is to take better account for ping & latency, which is simply something you cannot do with a server-based alternative.

2 Likes

You’re correct. After a while, I realized that adding a backtracking system would be enough to make it worth it.
Excuse my claims, I didn’t think it through at the time of writing my original response.

2 Likes

No problem. If you’ve issues with running it on the client, you can always just not set an owner! The owner is nil, I.e., the server, by default.

How do you get the name of the hitpart? I’ve already tried doing the raycastresult but there’s all kinds of unnecessary information.

RaycastResult{Torso @ 47.3109474, 3.95343065, -112.599541; normal = 0.389086038, 0, -0.921201527; material = Plastic}

You can see the properties of RaycastResult here: RaycastResult | Roblox Creator Documentation
So, to answer your question, you would do HitResult.Instance.Name.

1 Like

When I enable Debug in settings it works the first time but it doesn’t seem to after that. I think this has to do with setting self._Debug to false in DisableDebug() (which is called automatically in Stop()) which prevents StartDebug() from being called again, which also prevents it from telling the client to start debugging as well. All I did was remove the self._Debug = true and self._Debug = false in StartDebug() and DisableDebug(), but kept the ReplicationConnection updates in and it stayed debugging as expected.

Good catch! I’ve pushed an update to both the github & the Roblox module - could you update and confirm that this has been fixed on your end?

Seems to work, one other thing I wanted to mention is that Recursive doesn’t seem to replicate when you do SetRecursive. I was looking into the code and saw that a caster is only made on the client when Start is fired which means that you’ll need to start the hitbox at least once in order to apply updates to it (like setting recursive). I think it’d be beneficial to add caster creation to Update as well so that this works:

Hitbox = ClientCast.new(Tool, params, Player)
Hitbox:SetRecursive(true)

as right now this doesn’t because Caster is nil on the client until you start it once.

You already have the functionality to create a caster on the client if it doesn’t exist so it should just be able to be copy pasted into the update function as well.

Update 1.0.5.0

  • Casters on the client can now have their methods be updated without having to use the Start method prior
    cc. @steven4547466
1 Like

Update 1.0.6.0 [IMPORTANT UPDATE]

  • A bug has been fixed in regards to raycasts actually raycasting in the opposite direction - I frankly have no idea how this went by unnoticed for so long, but I have noticed this on accident and thus fixed it. Please update to make sure this bug is fixed for you all also.
1 Like

Update 1.0.7.0

  • Switched to " (double quotation marks) instead of ’ (single quotation marks) to stay consistent with Roblox code.

  • Switched out coroutine.resume for task.spawn, which does not break continuations.

  • GetPing is no longer divided by two, as ping is a two-way trip rather than one-way.

General tip:

if you’re calculating your hitbox on the client, please also validate on whether the hit-position is accurate, as exploiters could spoof this value whenever it’s being sent over to the server. There is no one reliable way to verify this, and as such you should program your own solutions depending on the context.

Update 1.0.8.0

  • Client no longer sends Humanoid along with the HumanoidCollided remote, but rather the server verifies whether the instance has one or not when it receives the data.

  • Collided & HumanoidCollided events are now destroyed when the Caster object is destroyed.

  • RaycastResult.Instance is now checked for whether it’s nil or not if sent from the client.