Projectile Damage Replication

I forgot how to use this Forum, so hopefully I can remember what to do here

I’ve always enjoyed creating Projectiles, specifically when handling them with the client/server side & vice versa via Replication but there was always one thing I struggled with whenever creating them

Now, say I create a Enemy (Not a Player) Projectile that I handle Hit Detection on the Client, and when it gets Touched, it deals damage on the server

-- Server Side
local ProjectileHandler = game.ReplicatedStorage:WaitForChild("RemoteEvent")

ProjectileHandler:FireAllClients()

ProjectileHandler.OnServerEvent:Connect(function(Plr)
    local Char = Plr.Character
    local Hum = Char:WaitForChild("Humanoid")

    Hum:TakeDamage(25)
end)
-- Client Side
local ProjectileHandler = game.ReplicatedStorage:WaitForChild("RemoteEvent")
local LocalPlayer = game.Players.LocalPlayer

ProjectileHandler.OnClientEvent:Connect(function()
    local Projectile = game.ReplicatedStorage.Projectile:Clone()
    Projectile.Parent = workspace

    local Connection
    local function Touched(Hit)
        local Plr = game.Players:GetPlayerFromCharacter(Hit.Parent)

        if Plr and Plr == LocalPlayer then
            Connection:Disconnect()

            Projectile:Destroy()
            ProjectileHandler:FireServer()
        end
    end

    Connection = Projectile.Touched:Connect(Touched)
end)

This is a pretty straight-forward script that detects when a Projectile is hit by the LocalPlayer, and deals Damage afterwards

But say you have an Objective, or rather an NPC that’s also the same thing but isn’t a valid Player object and the Projectile is now moving at a certain distance via Physics

Now, a couple options from what I could do is check for the Hit.Parent.Name of it, or check for a Value Object/Attribute inside it, but there’s one main thing that’s an issue: That NPC/Objective will take damage based on how much total players there are when FireAllClients() is called

Cause as I’m trying to replicate how the Projectile & its effects whenever it gets touched, if it so happens to hit that NPC, it’ll fire “X” amount of times & deal “Y” total damage based on how much players there are overall

I’m sure for all that I know it could just be a simple solution, but I’ve struggled with trying to find the best outcome for it here, and if anyone has an idea feel free to share it out :slightly_smiling_face: (I’m not worried about the exploits nor security, but rather I just want to get a general/good idea on how I want to accomplish this)

Things I’ve tried but didn’t work out well for reasons:

  • Creating a Global Debounce for the NPC whenever it got damaged, and when not to take damage from FireAllClients()
  • Handling Projectiles on the Server (Yeaaaah no)
4 Likes

I am not quite sure if I understood your issue correctly, but I guess you meant that every client checks whether it hit the Objective/NPC you spoke of.

The issue with multiple detection fires will be an issue if clients control hit registration. A workaround could be to assign a unique name to each and every projectile. A very simple solution would be to simply name the first projectile “1” and the 13th one “13” and so on. That way you can, server side, check if projectile “2” already has hit the target.

There are issues with this, one major is the fact that you need to store the names of projectiles which has hit it. This might take up large amounts of storage depending on required hits to kill it. I’m sure you are able to come up with a better solution, but this might be a good starting point just to get it off the ground if nothing else.

Keep in mind that exploiters may abuse the fact that your hit registration is based on client-side checks.

2 Likes

Same thing here, or it’s that I just woke up lol

Please explain a little bit more and I will try and help.

I’m not sure i quite understand what you’re trying to achieve but I’ll try my best, so from what I’ve understood is that you need to fire all clients to get the number of players in the game? You can just count the number of players from the server like this: #game:GetService(“Players”):Getchildren(), also I recommend you move the Touched function to serverside so the function only fires once instead of everytime a player touches it, it fires for everyone.

Put an ObjectValue instance inside the projectile (server-side) which points to the player which fired it then inside the local script check if that ObjectValue’s value matches the local player of which that script is executing for (when the Touched RBXScriptSignal object is fired).

It’s probably an uncommon thing which is fine, but these 2 GIF’s should further explain it

So in the Original Post I provided with the script, now the Projectile’s moving at a specific direction I want it to move at, and let’s say it targets the NPC (Non-Player Character) and attempts to deal damage while replicating to every client

Nothing seems to be wrong with the first GIF I’m about to show, but the second one is where I’m struggling here:

This is with 1 singular player on a server:
Image from Gyazo

Now, this is with 4 on the exact same script:
Image from Gyazo

GIF #1 only deals about 25 worth of damage, while GIF #2 then deals damage based on the amount of Players there are (25 Base Damage X 4 Players in the Server which is basically an instakill), whereas I only want it to deal its original base damage whilst making it replicated to every client

I’ve also tried replicating a invisible projectile on the Server and its movement on the Client but that didn’t seem to work unfortunately

@MCNightwatcher @Forummer I’m talking about a Client-Sided projectile hitting an NPC, whilst keeping its original Base Damage without it multiplying at all, and having to handle Touched Events on the Server while I have like a decent amount of Projectiles really affects a lot of performance (Plus it’s wonky at times and not precise)

2 Likes

Yes, that does explain it.

The reason is, you’re firing it on all clients, meaning if there’s 5 players, all 5 of them FireServer, making it lose 25 x 5 HP.
I would say dont FireAllClients on the very start of the ServerScript, just leave that out and remove the OnClientEvent from the LocalScript, because it’s firing when the game launches anyways.

Your gif’s were very informative. Thank you for this addition.

I am however not 100% sure I understand what causes it. Your supplied code does not report hit NPC’s to the server. It looks and seems like the issue stems from each and every Player reporting the NPC as hit.

Using my solution of naming all projectiles will prevent them from being counted more than once. However it will need to store several strings (or numbers depending on how you structure it).

@pyxfluff The issue though is that if I remove my RemoteEvent functions and handle them all on the server, it’s gonna create a ton of performance issues both on my client, and the server’s end

That’s actually not the case as shown in the GIF’s I provided, a ClickDetector is activated to call FireAllClients() whenever it gets activated, so I could literally click it whenever I wanted to

@ifkpop

I used a different code to provide the examples shown in the GIF, there isn’t really much difference apart from these small changes:

-- Server Side --
local Event = game.ReplicatedStorage:WaitForChild("RemoteEvent")
local Part = script.Parent

Part.ClickDetector.MouseClick:Connect(function()
    Event:FireAllClients()	
end)

Event.OnServerEvent:Connect(function(Plr, Target)
	Target.Humanoid:TakeDamage(25)
end)
-- Client Side --
local Event = game.ReplicatedStorage:WaitForChild("RemoteEvent")

Event.OnClientEvent:Connect(function()
	local Projectile = game.ReplicatedStorage.Projectile:Clone()
	Projectile.Parent = workspace
	
	local Speed = Instance.new("BodyVelocity")
	Speed.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	Speed.Velocity = Vector3.new(0, 0, 20)
	Speed.Parent = Projectile
	
	local Connection
	local function Touched(Hit)
		local Target = Hit.Parent
		local Hum = Target:FindFirstChildOfClass("Humanoid")
		
		if Hum then
			Connection:Disconnect()

			Projectile:Destroy()
			Event:FireServer(Target)
		end
	end

	Connection = Projectile.Touched:Connect(Touched)
end)

For now I’ll just keep experimenting on what workarounds I can handle with these Projectiles :thinking:

1 Like

Best of luck. One parting comment though;

BodyVelocity was deprecated not too long ago. I would recommend using it’s replacement LinearVelocity (roblox.com).

1 Like

I went ahead and came up with a temporary solution for the time being

It’s not perfect but it’s the least I can think of, after configuring with Replication for a while

What I went ahead and did is fire the RemoteEvent as per usual, except instead of it dealing its original Base Damage X Number of Players in the Server, I went ahead and divided the amount by the same amount of Players in order to keep its Original Damage (It might sound confusing but this is what I did):

-- Server Side --
local Event = game.ReplicatedStorage:WaitForChild("RemoteEvent")
local Part = script.Parent
local Plrs = game:GetService("Players")

Part.ClickDetector.MouseClick:Connect(function()
    Event:FireAllClients()	
end)

Event.OnServerEvent:Connect(function(Plr, Target)
    local CurrentPlrs = #Plrs:GetPlayers()
    local ActualDamage = (25 / CurrentPlrs) -- This would fire 4 times so 25 / 4 = 6.25

	Target.Humanoid:TakeDamage(ActualDamage)
end)

Image from Gyazo

I know this has some callbacks but it seems to work pretty well based on the times I’ve tried it on, for now though I’ll “temporary” mark this as the solution (Anyone else is welcome to add suggestions for this issue I’m facing with)

1 Like

I actually like this solution exploit-wise. Having all players calculate whether something hit or not makes exploits less effective.

I might borrow this solution in the future!

1 Like

What I did was shoot a projectile hidden in the server for damage check, and 1 in each client for visuals, is this bad? This was the result btw.
https://gyazo.com/6031b0187c10868a2a2ed39ca885d167