How would I add server-side hit detection to my Volleyball game?

  1. What do you want to achieve? Keep it simple and clear!
    I want to achieve server-sided hit detection with a hitbox I’ve already made.

  2. What is the issue? Include screenshots / videos if possible!
    For the smoothest-looking physics I’ve done client → server → other clients replication. e.g. Player X has inputted G to spawn the volleyball on their client, then the ball info gets sent into the server, and then the ball is copied over to the other clients to Player X’s position.

    My issue with this now, is how do I get all these players to get to collide with this ball through the server, and have the ball still move smoothly across each client via the replication? I’m trying not to use the server to spawn the ball because the Physics become too choppy, and I’m trying to create a similar system in “Volleyball 4.2”, where the ball looks like it moves smoothly across each client, where the hit detection is handled by the server, but balls spawn on each individual client for each player.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I haven’t tried any solutions since I’ve never done this before and this is too specific to research, but currently the ball spawns on each client and not the server, so do I need to spawn the same ball on the server as well for hit detection? Network ownership I’ve tried before and is unrelated to this topic, as each client is handling the same ball separately on their own clients.

The codes (local script + server script):

--- the local script to spawn the ball on input and send info to server
local function createBall(user)
	local p = vbClone:Clone()
	local bsClone = ballShadow:Clone()
	local court = game.Workspace.Template.Flooring.Part 
	local ballTween = TS:Create(p, tweenInfo, Orientation)
	p.Position = user.Character.Head.Position +, 15, 0) -- ball spawns 15 studs above players head
	p.Parent = workspace
	bsClone.Parent = workspace
	local shadowcoroutine = coroutine.create(function() --[[this is for that 
expanding ball-shadow provided in the video]]
		while RS.Stepped:Wait() do
			bsClone.Position =, court.Position.Y + 0.28, p.Position.Z)
			local bsCloneY = bsClone.Position.Y
			local vbCloneY = p.Position.Y
			local mag = vbCloneY - bsCloneY
			bsClone.Size =, mag, mag)
	p.UpPower.Enabled = false --[[this disables since upon spawning the ball, 
i want it to go upwards for a little while so i 
add a little force before disabling it]]
	p.VectorForce.Enabled = true -- this is a force to make the ball feel more floaty

UIS.InputBegan:Connect(function(Input, gameProcessedEvent)
	local currentPwr = x / maxpwr --[[ ignore these 2, they're used to 
determine the power of the hit/receive]]
	local isBounced = false
-- spawn ball when pressing G
	if Input.KeyCode == Enum.KeyCode.G and not gameProcessedEvent and not spawnDebounce then
		spawnDebounce = true -- debounce
		remoteEvent:FireServer() -- fire the event to server
		createBall(player) -- creates the ball for the player using function
		spawnDebounce = false

Moving on to what happens on the server:

local function spawnRemote(player, user)
	for i,v in ipairs(game.Players:GetPlayers()) do --getting all the players in the game

		if v ~= player then --[[ all players BUT the one who spawned 
the ball, (they already ran it on the client 
in the earlier code at createBall(player) after remoteEvent:FireServer() ]]
			user = v
			remoteEvent:FireClient(v, player)


My current server-script that’s below that one that spawns a hitbox that doesn’t work since the ball isn’t spawned on the server:

touchedEvent.OnServerInvoke = function(player, horizontalValue, verticalValue)
	--local isHit = false
	if not IsTouched then
		IsTouched = true
		local hitbox = game.ReplicatedStorage.hitbox
		local hitboxclone = hitbox:Clone()
		local char = player.Character
		local torso = char:WaitForChild("HumanoidRootPart")
		local Humanoid = char:WaitForChild("Humanoid")
		local Weld ="WeldConstraint", torso)
		local Debris = game:GetService("Debris")
		hitboxclone.CFrame = torso.CFrame -, 0, 0.5)
		Weld.Part0 = torso
		Weld.Part1 = hitboxclone
		Weld.Parent = torso
		local angVel = hitboxclone.AssemblyAngularVelocity
		hitboxclone.Parent = player.Character
		hitboxclone:SetNetworkOwner(player) -- sets hitbox network owner to the player
		Debris:AddItem(hitboxclone, 2)

		local recAnim = Humanoid:LoadAnimation(rec_Animation)

		local function ball(hit)
			if hit.Name == "Volleyball" and not isHit and recAnim.IsPlaying and isBounced == false then
				if isHit == true then
				local hitSound = hit:FindFirstChild("Sounds").rec
				isHit = true
				hit.Velocity = hitboxclone.CFrame.LookVector * horizontalValue + hitboxclone.CFrame.UpVector * verticalValue
				local angVel = hit:FindFirstChild("AngularVelocity")
				angVel.AngularVelocity = hitboxclone.CFrame.RightVector * -1 * horizontalValue/4
				angVel.Enabled = true
				isHit = false
		IsTouched = false

I’d recommend you to use another method.

You can try Ball:SetNetworkOwner(nil)
It is smooth and exploiters will not be able to change its position.

1 Like

Doesn’t setting it to nil still make the server still handle the physics? I’ve used this before and the server’s physics just seem to look choppy still, where the ball is jittering in the air, because the server’s tick rate is different compared to a clients. You can test this by using an FPS unlocker, and looking at the game in over 60 FPS. Since the server is locked to 60, and the client uses a higher value like 80 FPS, you start getting the jitter. Another problem is that if it’s set to the player instead, the other players will no longer be able to interact with their hitboxes on the ball. That is precisely why I’m trying to replicate through the client instead, so that the ball looks smooth throughout higher FPS values on each client.