Object not spawning at correct position when the player is moving

Hello Devs,
I have recently been making a game and I have made a script that is able to throw an object and damage players. The script I have created works fine, However when the object is thrown it is quite delayed from where the object is and where the player is. I imagine that it has something to do with the spawn position lagging behind, but I am unsure on how to fix it. Apologies in advance for my terrible and buggy code, It was the best thing I could come up with.

Here is an example of my issue:

Here is an image of the tool for reference:
image

Local Script:

local Tool = script.Parent
local Player = game:GetService("Players").LocalPlayer
local Remote = Tool:WaitForChild("Trigger")
local db = false
local Idle = Tool.Handle:WaitForChild("IdleAnim")
local Track

local function GetThrowDirection()
	local Camera = game.Workspace.CurrentCamera.CFrame
	if Camera then
		local ThrowDirection

		local Yvector = Camera.LookVector.Y

		if Yvector <= -0.85 then
			Yvector = -0.6
		end
		
		ThrowDirection = Vector3.new(Camera.LookVector.X,  Yvector, Camera.LookVector.Z).Unit

		print(Camera.LookVector.X.. ": X")
		print(Yvector.. ": Y")
		print(Camera.LookVector.Z.. ": Z")

		return ThrowDirection
	end
end

Tool.Activated:Connect(function()
	if db == false then
		db = true
		if Track then
			Track:Stop()
		end
		
		local throwAnim = Tool.Parent.Humanoid:LoadAnimation(script.Throw)
		throwAnim:Play()
		
		task.wait(0.2)
		local ThrowDirection = GetThrowDirection()
		Remote:FireServer(ThrowDirection)

	end
end)

Tool.Equipped:Connect(function()
	
	Track = Tool.Parent.Humanoid:LoadAnimation(Idle)
	
	Track.Priority = Enum.AnimationPriority.Idle
	Track:Play()
end)

Tool.Unequipped:Connect(function()
	if Track then
		Track:Stop()
	end
end)

Server Script:

local db = false
local db2 = false
local db3 = false

local Force = 50
local Damage = 70

local Tool = script.Parent
local Idle = Tool.Handle:WaitForChild("IdleAnim")
local Track

local Remote = Tool:WaitForChild("Trigger")

local camera

Remote.OnServerEvent:Connect(function(player, throwDirection)
	if db == false then
		db = true
		local OriginalPlayer = script.Parent.Parent
		
		-- Play the animation
		local humanoid = OriginalPlayer:FindFirstChild("Humanoid")
		local Tool = script.Parent
		local HRP = Tool.Parent.HumanoidRootPart

		local Handle = Tool.Handle:Clone()
		
		Tool:Destroy()

		local Clone = Instance.new("Model")
		Clone.Parent = game.Workspace.Objects
		Handle.Parent = Clone
		Clone.PrimaryPart = Handle
		Clone.PrimaryPart:SetNetworkOwner(nil)

		Clone.Name = "FlyingStool"

		for _, v in ipairs(Handle.Parts:GetDescendants()) do
			if v:IsA("BasePart") then
				v.Parent = Clone
			end
		end
		
		for _, v in ipairs(Clone:GetDescendants()) do
			if v:IsA("BasePart") then
				v.CollisionGroup = "Chair"
			end
		end
		
		Handle.Parts:Destroy()

		local velocity = Handle.LinearVelocity
		local attachment = Instance.new("Attachment")
		attachment.Parent = Handle
		velocity.Attachment0 = attachment
		Clone.PrimaryPart.Position = Handle.Position
		velocity.VectorVelocity = throwDirection * Force + Vector3.new(0, 10, 0)
		
		task.wait(0.1)
		
		Handle.Throw:Play()
		
		Clone.ClickPart.Touched:Connect(function(hit)
			if db2 == false then
				print(db2)
				Clone.ClickPart.CanCollide = true
				attachment:Destroy()
				velocity.VectorVelocity = Vector3.new(0, 0, 0)
				print(hit.Parent)
				if hit.Parent:FindFirstChild("Humanoid") or hit.Parent:IsA("Accessory") then
					local Humanoid

					if hit.Parent:FindFirstChild("Humanoid") then
						Humanoid = hit.Parent:FindFirstChild("Humanoid")
					else 
						Humanoid = hit.Parent.Parent:FindFirstChild("Humanoid")
					end
					if Humanoid.Parent.Name ~= OriginalPlayer.Name then
						if db3 == false then
							db2 = true
							db3 = true
							print("Damage")
							for _, v in ipairs(Clone:GetDescendants()) do
								if v:IsA("BasePart") then
									v.CanCollide = true
								end
							end
							for _, v in ipairs(Clone.WeldHolder:GetDescendants()) do
								v:Destroy()
							end
							Handle.Breaking:Play()
							
							Humanoid:TakeDamage(Damage)
							task.wait(5)
							Clone:Destroy()
						end
					end
				else
					db2 = true
					db3 = true
					Clone.ClickPart.ClickDetector.MaxActivationDistance = 5
				end
			end
		end)

		task.wait(0.1)
		
		Clone.ClickPart.CanCollide = true
		attachment:Destroy()
		velocity.VectorVelocity = Vector3.new(0, 0, 0)
	end
end)

If someone could please help me solve this issue or at least point me in the right direction, that would be greatly appreciated. If you have any questions on why I put certain things in my code, please let me know.

Test it one more time, but make longer break before throwing. Move to some position, stay there for 5 seconds and then throw and check if the position is still weird.
Roblox has this “bad” player position synchronization system where player’s are mostly behind for the server. Server probably had old player’s position from where it was spawned.

You could send the position from where the player is throwing to the server, however this would open an exploit, which could be solved by calculating distance between server’s position and throw position, and a maximum desync position distance and validate the input.

First of all, the issue only occurs when the player is moving. If the player throws the object when standing still, no position issues occur, no matter where the player is standing.

Secondly I am calculating the direction of throwing in the client and then sending that information through a remote event to the server. My thought behind this is that the client can process this much faster compared to the server. I have tested this by putting the direction calculation on the server and it seems to make the whole thing more laggy, but this could just be me.

I have considered using the players speed and direction to calculate where the player is going to be before they reach that point and then sending that to the server instead. This should work in theory, but what if the player stops moving after calculating the position with the players speed and direction in mind? My best guess would be that it would end up going further away instead of lagging behind.

So tbh Im not quite sure how to tackle this issue.

What I had on mind is that the client will send its position the server and server will validate if it’s possible for the client to be at that position before throwing the object.

As I said before, there is position synchronization lag, and server is always behind. This implies, that even if the player is standing, this could still occur if the player stands only for few milliseconds.

Alright I’ll test a bit further. Also would it just be possible to do all the throwing visuals on the client and just replicate it to the server? Would that solve the issue?

I don’t think this is possible because client cannot be trusted. You could do the visuals on client, but the throw decision making still must be done on server just to prevent exploiters from abusing it.
So possibly server will send an event to all players that throw is done, so player’s will spawn the stool themselves and simulate the physics to make it smoother. However if there are any hit checks, server must somehow simulate the physics as well. There’s probably the possibility to throttle physics to 30FPS, so server doesn’t have to work that much and since clients simulate their own physics, they wouldn’t feel it.

1 Like

Alright I’ll experiment with that, thank you for your suggestion.