So I send client → server via local script when tool is used. And then on the server script I create a pie that gets thrown. As you can see there is a lag before it gets thrown. How can I remove this lag?
you can either
set network ownership of the pie to the client
or
instead of creating the pie on the server make it fire a event to every client and create the projectile on them while handling stuff like hit detection on server
Typically, visuals should be handled on the client.
You would create the pie on the client who activated it and send info to the server about the pie. On the server, you would calculate where the pie should be and handle any hit detection or important game logic. The server would then send the info about the pie to all the other clients which would calculate where the pie should be and create it on their client.
This system makes it so the pie is visible to all players and most importantly, the position of the pie is accurate for everyone.
When you say “fire an event to every client and create the projectile on them”
Is it possible to send a message from client → client, or do I need to fire event from client to server and then back?
And if the pie is made on client, how can I move it through the server script? Aka how can the server detect if it hit the player if it only exists on the client?
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local RemoteEvent = ReplicatedStorage.RemoteEvents:WaitForChild("ClownPieThrow")
-- SETTINGS
local FORWARD_POWER = 80
local UPWARD_POWER = 55
local ROTATION_FORCE = 0
local TEMPLATE = ReplicatedStorage
:WaitForChild("ClownEquipment")
:WaitForChild("PieTemplates")
:WaitForChild("Pie")
local function createAndLaunchPie(player, originCFrame)
local pieClone = TEMPLATE:Clone()
pieClone.Parent = workspace
for _, obj in ipairs(pieClone:GetDescendants()) do
if obj:IsA("Part") then
obj:SetNetworkOwner(player)
end
end
local mainPart = pieClone:FindFirstChild("Handle")
if not mainPart then
warn("Pie template missing Handle part")
pieClone:Destroy()
return
end
for _, obj in ipairs(pieClone:GetDescendants()) do
if obj:IsA("Weld") or obj:IsA("WeldConstraint") then
obj:Destroy()
end
end
for _, part in ipairs(pieClone:GetChildren()) do
if part:IsA("BasePart") and part ~= mainPart then
local weld = Instance.new("WeldConstraint")
weld.Part0 = mainPart
weld.Part1 = part
weld.Parent = mainPart
end
end
local spawnCF = originCFrame * CFrame.new(0, 0, -2)
mainPart.CFrame = spawnCF
for _, part in ipairs(pieClone:GetChildren()) do
if part:IsA("BasePart") then
part.Anchored = false
part.CanCollide = true
end
end
local forward = originCFrame.LookVector
local velocity =
forward * FORWARD_POWER +
Vector3.new(0, UPWARD_POWER, 0) -- upward arc
mainPart.AssemblyLinearVelocity = velocity
mainPart.AssemblyAngularVelocity = Vector3.new(
math.random(-ROTATION_FORCE, ROTATION_FORCE),
math.random(-ROTATION_FORCE, ROTATION_FORCE),
math.random(-ROTATION_FORCE, ROTATION_FORCE)
)
game.Debris:AddItem(pieClone, 10)
end
RemoteEvent.OnServerEvent:Connect(function(player, hrpCFrame)
if typeof(hrpCFrame) ~= "CFrame" then return end
createAndLaunchPie(player, hrpCFrame)
end)
Create the pie on the client of who activated the tool.
Using a synced timer, send the info about the pie to the server.
→ Ideally, you should minimise the amount of user input as exploiters can spoof the data sent through remotes on the client.
On the server, calculate where the pie should be and handle any hit detection or important game logic here.
The server would then send the info about the pie to all the other clients which would recalculate the position of the pie and create it on their client.
This system is clean and scalable and furthermore, this is the type of system most devs go for.
Using network ownership is hard to scale and becomes a headache when there’s multiple different tools being used.