Help with Synchronizing the Client and Server

So I have a script in ServerScriptService,

local RS = game:GetService("ReplicatedStorage")
local rE = RS:WaitForChild("RemoteEvent")

local SS = game:GetService("ServerStorage")
local sphere = SS:WaitForChild("Part")


while true do
	task.wait(5)
	
	for i, v in pairs(game:GetService("Players"):GetPlayers()) do
		local char = v.Character or v.CharacterAdded:Wait()
		local hrp = char:FindFirstChild("HumanoidRootPart")
		
		local clonedSphere = sphere:Clone()
		clonedSphere.Parent = workspace
		
		clonedSphere.CFrame = workspace.cylinder.CFrame
		
		task.wait(0.1)
		
		rE:FireAllClients(clonedSphere, hrp)
		
		task.wait(1)
		
		clonedSphere.CFrame = hrp.CFrame
		
		task.wait(0.3)
		
		clonedSphere:Destroy()
		
		
	end

end

And I have a local script in the StarterCharacterScripts

local RS = game:GetService("ReplicatedStorage")
local rE = RS:WaitForChild("RemoteEvent")

local TS = game:GetService("TweenService")

local function tween(object, charHrp)
	
	local tweeningInfo = TweenInfo.new(1.5)
	
	local goal = {CFrame = charHrp.CFrame}
	
	
	TS:Create(object, tweeningInfo, goal):Play()
	
	
end

rE.OnClientEvent:Connect(function(object, charHrp)
	tween(object, charHrp)
end)

So I was just testing on making boss fight attacks, I want to make the tween smooth but also no delay between the server and the client when the sphere moves. I searched up on Youtube and people tween it on the server so this won’t be a problem for them, but the tween will definitely lag.

Like when tweening the size of an expanding disc that damages players.

robloxapp-20231216-1443444.wmv (2.2 MB)

I also have a damage script in the sphere, and you can see that I need to synchronize between the server and the client for when the sphere moves. I literally got hit when I’m far away. I was wondering of using a remote function to fire from the server to client to server, but it is dangerous. Or I could tween it on the server? but it lags.

1 Like

Synchronizing between the client and the server is tricky because there are many things to take into consideration. What I would do is:

Instead of the server cloning the sphere I would have the client clone it, although for security the server will still need to calculate the origin and destination for the sphere and send them to every client using FireAllClients

You’re already playing the Tween in the client which is good practice and will easily allow you to implement the suggestion above

The benefit of my suggestion is the more you find ways to lessen the load on the server and the less data you need to send, the smaller the delay you will have between the client and the server. My suggestion isn’t 100% perfect though as if a client has a poor network connection, they will still experience a noticeable delay before the sphere is created and tweened, but fortunately on average there shouldn’t be much issues

1 Like

Wait, if I were to have an expandable damaging disc that is tweened on the client, I don’t want to depend the position of the disc on the client. Because, it is not moving at all on the server, not expanding too. So, what should I do on the server to get it work?

1 Like

There’s no need to create the sphere in the server if you create it for each client. I’ll try to write an example code but I need to know what triggers the sphere to be created in the first place

1 Like

Oh if it was created on the server I mean. So it is created on the server, but tweened on for all clients.

1 Like

Yes I’m aware that it’s currently being created on the server. I’ll write an example of what I mean

1 Like

Server Script:

-- When the server recieves the signal to send sphere data
local players = game:GetService"Players"

local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent

local origin = -- Where you want the sphere to originate from
local target = -- Where you want the sphere to go

for _, player in players:GetPlayers() do
	remoteEvent:FireAllClients(player, origin, target)
end

LocalScript:

local tweenService = game:GetService"TweenService"

local tweenInfo = TweenInfo.new(1.5)

local sphere = script:WaitForChild"Sphere" -- The client won't find the sphere if it's left in ServerStorage!

game:GetService"ReplicatedStorage".RemoteEvent.OnClientEvent:Connect(function(origin, target)
	local sphereClone = sphere:Clone()
	sphereClone.CFrame = origin
	sphereClone.Parent = workspace

	local tween = tweenService:Create(sphereClone, tweenInfo, {CFrame = target})
	tween:Play()
end)
1 Like

Oh yeah, btw it’s fine not to use WaitForChild() if we know the instance is gonna be there right?

2 Likes

And why is it better to just clone the sphere in the client?

1 Like

I always recommend using WaitForChild in LocalScripts, unless you’re accesing something from ReplicatedStorage (I’m not 100% sure on this, so take caution but it’s working at the moment because Instances in RS seem to load first the moment the client joins the game) or your using script.Parent because the script won’t run if it’s ancestors haven’t loaded yet

1 Like

The reason why it’s better to clone the sphere in the client is because for the server it requires more data and processing to send information about the sphere like color or size to every client instead of 2 CFrame values of where the sphere needs to start and go which will cause a delay depending on how many players are in your game

2 Likes

Does instances like scripts, module scripts load quickly to the game?

1 Like

Depends on where they are, if the ModuleScript is inside ReplicatedStorage then yes but if it’s a child of a LocalScript that’s a child of a Gui for example then you will need to use WaitForChild to retrieve it

For the server though it’s a different story because everything is loaded the moment the game starts unless the object it needs to access is created by the client (in this case the server will never see it), created by a server script or has the chance of being destroyed when the script needs to access it so sometimes you need to use FindFirstChild

I don’t recommend using WaitForChild in server scripts because it could cause a ton of problems

1 Like

And we don’t have to move the object on the server?

1 Like

No, there’s no need to move or tween the object on the server. As a matter of fact, the server won’t even know the sphere clone has been created but there’s no need to worry about this in your case

1 Like

There won’t be problems with hackers or anything? like hackers deleting the object, like since the server ain’t seeing anything.

2 Likes

Since the object is being created on each client, if an exploiter deletes the sphere it will still exist unaltered for the other players, they’d only delete their own sphere

1 Like

But they won’t get damaged by the sphere

1 Like

This is what I’m doing to test:
Server:

local origin = CFrame.new(0,4,0)
local target = CFrame.new(0,4,-28)

local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent

remoteEvent.OnServerEvent:Connect(function()
	local result = workspace:Spherecast(origin.Position, 4, target.Position) -- Change 4 to radius of your sphere

	if result then
		local humanoid = result.Instance.Parent and result.Instance.Parent:FindFirstChildWhichIsA"Humanoid"

		if humanoid then humanoid:TakeDamage(20) end

		remoteEvent:FireAllClients(origin, CFrame.new(result.Position))
	else
		remoteEvent:FireAllClients(origin, target)
	end
end)

Client:

local tweenInfo = TweenInfo.new(1.5)

local tweenService = game:GetService"TweenService"
local debris = game:GetService"Debris"

local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent

game:GetService"ContextActionService":BindAction("TEST", function(_, is)
	if is == Enum.UserInputState.Begin then
		remoteEvent:FireServer() -- Signal for server to start process
	end
end, false, Enum.UserInputType.MouseButton1)

local sphere = script:WaitForChild"Part"

remoteEvent.OnClientEvent:Connect(function(origin, target)
	local sphere = sphere:Clone()
	sphere.CFrame = origin
	sphere.Parent = workspace

	local tween = tweenService:Create(sphere, tweenInfo, {CFrame = target})
	tween:Play()
	tween.Completed:Wait()
	debris:AddItem(sphere, 0)
end)

Working on a way to delay the raycast to match the tween duration though

Oof I accidentally caused a chicken and the egg situation where I need the raycast data before the delay but the raycast needs to happen after the delay. The only way to solve this is to raycast twice and damage the humanoid in the second cast but it’s not an ideal solution

local origin = CFrame.new(0,4,0)
local target = CFrame.new(0,4,-28)

local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent

remoteEvent.OnServerEvent:Connect(function()
	local result = workspace:Spherecast(origin.Position, 4, target.Position) -- Change 4 to radius of your sphere
	remoteEvent:FireAllClients(origin, result and CFrame.new(result.Position) or target)

	task.wait(1.5) -- Needs to match tween duration

	local result = workspace:Spherecast(origin.Position, 4, target.Position) -- Same as above

	if result then
		local humanoid = result.Instance.Parent and result.Instance.Parent:FindFirstChildWhichIsA"Humanoid"

		if humanoid then humanoid:TakeDamage(20) end
	end
end)
1 Like