Basically they are all parts that have a server side script subscribed to a Touch event. When the event fires, the server does a bunch of logic to see if the touching part belongs to a player and, if so, damages them.
I’ve found through testing that this event is very chatty. Swinging a sword at a test dummy locally typically generates 15-20 Touch events in a row. My understanding is online this would look like this:
Local player (handling collision) fires 15-20 Touch events to the server
These are guaranteed delivery (guaranteed order?) updates, so very heavy
Server does damage logic 15-20 times
I know how to change my scripts to prevent #3. How do I stop #1 from happening, before these events generate network traffic spam? I really am only interested in TouchStarted. There is a TouchEnded event in the docs but no TouchStarted.
Can I write a LocalScript that filters outgoing Touch events? How? (Not like, logically how, I mean, where do I hook it in?)
To prevent the “chatty” behavior you are describing, I’d use a debounce to only allow one part per click. Once a part inside of a character is detected, use TouchedEvent:Disconnect() and reconnect it once they’re allowed to slash their sword again.
I think by the time the “disconnect” (hey client, it’s cool stop sending events) gets back to the client, the client will have already fired the 15-20 Touched events in my case.
I assume the client is generating Touch events at 30 or 60 Hz. Feels like 60.
You could rewrite your system to have the Touched event on the client, and then use a single RemoteEvent (with the same security logic you currently have in place, with some additional magnitude checks) in place of the server-side Touched event. Then use TouchedEvent:Disconnect() on the client, thus reducing network traffic.
You could use remotes instead, so, when a Touch event fires on the client, a remote is fired, you could add a rate limiter on the client and on the server to prevent it from firing too much.
e.g. if a remote is fired more than 20 times in a second, ignore the event/request for a short period of time.
I do debounces on both the server and the client. The remote event will only get fired once, unless the player is exploiting. That is why I also do it on the server, just to check that what the client sent is ok.
EDIT: I read that as remote events the first time, not touch events. But the same concept still probably applies.
If I’m understanding you correctly, you don’t want the sword to damage the player more than once. My proposed solution is to store all items in a table(You can also try using GetTouchingParts()):
local TouchedPlayers = {}
local function SwordSlash(Part)
if Part.Parent:FindFirstChild("Humanoid") and not table.find(TouchedPlayers, Part.Parent) then
table.insert(TouchedPlayers, Part.Parent)
-- rest of code
end
end
Part.Touched:Connect(SwordSlash)
Or, another solution is to use rays from the start position to the end position. So, it will only do damage when the attack ends:
local StartPosition = Character.Tool.Handle.Position
Animation:Play()
Animation.Completed:Wait()
local Ray = Ray.new(StartPosition, Character.Tool.Handle.Position)
workspace:FindPartOnRay(Ray)
It would be very strange but you could try doing something where after the first Touch event it deletes the hitbox and then you can generate a new hitbox when you can do damage again. I don’t know if this would work well though.
Well theres definitely multiple objects which could trigger the event, and you don’t want to mute the touched event if the sword first hits a wall prior to any limb of a character. It’d be interesting if the touchInterest took in a whitelist, but I also liked @VegetationBush’s idea on using a table. You could use it to filter out multiple triggers on the same part, but I wouldn’t go as far as disconnecting the event in case the sword hits non-character objects on its swing.
local activeParts={}
local characterDebounce=false
sword.Touched:Connect(function (part)
if (activeParts[part] and activeParts[part].status()~='dead') or characterDebounce then
return -- Part already being handled
end
activeParts[part]=coroutine.running()
if isInCharacter(part) then
characterDebounce=true
end
-- Code
end)
How about just a simple debounce like I use for my scripting involving touch events.
Debounce = false
function Touched ()
debounce = true
if debounce then return end
[CODE]
wait(TIME IN SECONDS)
debounce = false
end
script.Parent.Touched:Connect (Touched)
I use that general debounce idea for simple things such as regen buttons etc. The idea could be adapted for use in swords.
Every 0.1 seconds you generate a new table, and all hits that happen in that 0.1 seconds are put into it.
if the table has stuff in it, it’s sent to the server in a batch, and it goes through all entries for sanity checks.
Hits={}
Tick = 0
Sword.Touched:connect(function(hit)
if Hitcheck(Hit) then
table.Insert(Hits,Hit)
end
end)
game["Run Service"].RenderStepped:connect(function(delta)
Tick = Tick + 1
if Tick == 10 then
Tick = 0
if Hits[1] then
-- send to server
Hits = {}
end
end
end)