Client-Server Hitbox CFrames

I am writing a combat system and am having an issue with the melee weapon hitboxes (though this also applies to ranged weapons as well). There is a discrepancy between the server’s CFrame and the client’s CFrame. The green part is spawned locally using the client’s CFrame while the red one is spawned on the server through a RemoteEvent. I would’ve assumed that by the time the RemoteEvent reached the server, the server’s character CFrame would’ve matched up the location of where it was when the client clicked, however there is a bit of an offset.


The offset does not seem to vary with latency. Here are two videos with a higher player speed. The first is with 40ms latency (0 incoming replication lag) the second is with 850ms latency (0.4 incoming replication lag. The offset is just about the same in each.

So I am wondering what I could do to remedy this. The easiest solution would be to just send the client’s aim CFrame with the fire remote and use that with some magnitude checks, but that still seems pretty exploitable (can use it to place the aim CFrame through walls, etc). I was wondering if anyone has experience writing secure, fast paced combat system and would be able to offer some advice?
Here is the place file with two simple test scripts:
HitboxTesting.rbxl (21.8 KB)
The scripts are also pasted identical below

Client Code (in StarterCharacterScripts)

local UserInputService = game:GetService("UserInputService")

local HitboxRemote = ReplicatedStorage:WaitForChild("HitboxRemote")
local testPart = ReplicatedStorage:WaitForChild("TestPart")

local Parent = script.Parent
local Player = game.Players:GetPlayerFromCharacter(Parent);
local Character = Player.Character or Player.CharacterAdded:Wait()

UserInputService.InputBegan:Connect(function(input, gameProcessed) --spawn a "slash" on the client representing an attack hitbox
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		local frame = Character.HumanoidRootPart.CFrame
		local part = testPart:Clone()
		part.Parent = game.Workspace
		part.Color = Color3.new(0, 1, 0)
		part.CFrame = frame
		HitboxRemote:FireServer()
		wait(1)
		part:Destroy()
	end
end)

Server Code (in ServerScriptService)

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local HitboxRemote = Instance.new("RemoteEvent")
HitboxRemote.Name = "HitboxRemote"
HitboxRemote.Parent = ReplicatedStorage
local testPart = Instance.new("Part")
testPart.Anchored = true
testPart.CanCollide = false
testPart.Name = "TestPart"
testPart.Size = Vector3.new(1,1,5)
testPart.Parent = ReplicatedStorage


HitboxRemote.OnServerEvent:Connect(function(Player) --spawn a "slash" on the server representing an attack hitbox
	local Character = Player.Character
	local frame = Character.HumanoidRootPart.CFrame
	local part = testPart:Clone()
	part.Color = Color3.new(1, 0, 0)
	part.Parent = game.Workspace
	part.CFrame = frame
	wait(1)
	part:Destroy()
end)

That lag between client and server will always exist (or at least until roblox becomes something like Stadia), so there is no solution as far as I know. What you can do is focus on providing the best possible player experience, which would mean that many things have to be done on the client side. As for the exploiters, it’s premature to worry about them in development time. Maybe the only thing you should do is protect your Datastore from generating huge stats. Once your game has a certain amount of users and you have enough feedback to say that your game will not have any more significant changes, that’s when you should think about anti exploits. This is my personal opinion.

2 Likes

As far as i know. The best you can do is detect the hit with the client then make the same check on the server. If they match up that constitutes a hit. This discrepancy can only grow as the game gets more complex and gets more players.

The problem is I can’t make the same checks on the server because both the player and enemy’s CFrame will differ from what is seen on the client. I already have the enemy position problem figured out by using a lag compensation rewind system. The remaining problem is the player’s CFrame needing to be extrapolated or something so I don’t have to give the client authority to send whatever aiming CFrame they want. Though right now it seems trusting the client with a magnitude check between server and client CFrames is the easiest option

while the client can’t fool the magnitude check on it’s side once it fires on the server and repeats the check it will fail or am i missing something?

Instead of doing only magnitude checks, do a raycast to, to check if it’s behind a wall. I think client detection with verification on the server is the best way to approach this problem.