How do I make my pet follow system work on the server and not the client

I just made this pet follow system that will make pets follow the player in a circle
I made it work but its client sided and I want to change that.

I tried doing the same things from the local script but with Stepped instead of Heartbeat and it made my frames lower + it was not smooth
I also tried using a remote event every time heartbeat is fired and it’s that same result

here is the video of the pet follow system (also the pets are from a kit but the system is mine)
https://gyazo.com/ba807e60395e1bb4db7aa90009ab7b3a

local Character = script.Parent
local hrp = Character:FindFirstChild("HumanoidRootPart")

local Pets = {}
local numberOfParts = 8

wait(0.5)
for i, Pet in ipairs(script:GetChildren()) do
	table.insert(Pets, Pet)
end

for _, part in pairs(Pets) do
	if part:IsA("Model") then
		for _,ModelPart in ipairs(part:GetChildren()) do
			if ModelPart ~= part.PrimaryPart then
				ModelPart.Anchored = false
			else
				ModelPart.Anchored = true
			end
			ModelPart.CanCollide = false
		end
		part.Parent = workspace
	else
		part.Anchored = true
		part.CanCollide = false
		part.Parent = workspace	
	end
end

local function getXAndZPositions(angle)
	local x = math.cos(angle) * 5
	local z = math.sin(angle) * 5
	return x, z
end


game:GetService("RunService").Heartbeat:Connect(function()
	for i, part in pairs(Pets) do
		local angle = i * ((math.pi * 2) / #Pets)
		local x, z = getXAndZPositions(angle)
	
		local position = (hrp.CFrame * CFrame.new(x, 0, z)).p
		local lookAt = hrp.Position
		
		local tween = nil
		
		if part:IsA("Model") then
			tween = game:GetService("TweenService"):Create(part.PrimaryPart,TweenInfo.new(0.5),{CFrame = CFrame.new(position, lookAt)})
		else
			tween = game:GetService("TweenService"):Create(part,TweenInfo.new(0.5),{CFrame = CFrame.new(position, lookAt)})
		end
		
		tween:Play()
	end
end)

also, I didn’t make the full system, this is just for testing and that’s why the pets are the children of the script

Thanks for reading

2 Likes

Try handling the tween on the client then?
In the server, just set it to the CFrame/Position automatically, no need for tweening or animating.

Also,

I tried doing the same things from the local script but with Stepped instead of Heartbeat and it made my frames lower + it was not smooth

Have you tried using RenderStepped?

1 Like

I want everyone to see the tween, also what I meant was that I did Stepped in a server script and this is how it looks
https://gyazo.com/a91c2f35088d5facb38c7f82f24d9be8
as you see its not smooth at all and it causes frame rate issues

1 Like

You can handle it in the client, use RemoteEvents to make it work
Just send the necessary things to the client(s) like CFrame.new(position, lookAt) and the pet model
Just set the CFrame in the server

1 Like

I did, there is no frame rate issue but its still not smooth
https://gyazo.com/5defbce9a60e5dfdb26815e556231681

Try using SetNetworkOwnership?

A simple solution would simply be to have every client handle it separately.

I would say for the most part, the server should never have to handle anything visual. The client should deal with that. If you’re worried about syncing, just use remotes to communicate things to each client. If it’s just a matter of pets moving around a player, I doubt their exact position matters. Just that they’re there.

2 Likes

I just tried doing that with FireAllClients in the remote, it looks smoother but not 100% smooth and after something like 10 seconds you will get less frame rates and after 30 seconds or even less the game will not be playable

Sounds like you’re doing something inefficiently somewhere. Perhaps even replicating things. If handled properly, there shouldn’t be any issues like that.

local Character = script.Parent
local hrp = Character:FindFirstChild("HumanoidRootPart")

local Pets = {}

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(Character)
		wait()
		local hrp = Character:FindFirstChild("HumanoidRootPart")
		game:GetService("RunService").Stepped:Connect(function()
			if not Character then return end
			game.ReplicatedStorage.MovePets:FireAllClients(Pets, hrp)
		end)

	end)
	
end)

wait(1)

for i, Pet in ipairs(script:GetChildren()) do
	table.insert(Pets, Pet:Clone())
end

for _, part in pairs(Pets) do
	if part:IsA("Model") then
		for _,ModelPart in ipairs(part:GetChildren()) do
			if part.PrimaryPart ~= ModelPart then
				ModelPart.Anchored = false
			else
				ModelPart.Anchored = true
			end
			ModelPart.CanCollide = false
		end
		part.Parent = workspace
	else
		part.Anchored = true
		part.CanCollide = false
		part.Parent = workspace	
	end
end

server

local function getXAndZPositions(angle)
	local x = math.cos(angle) * 5
	local z = math.sin(angle) * 5
	return x, z
end


game.ReplicatedStorage.MovePets.OnClientEvent:Connect(function(Pets, hrp)
	for i, part in pairs(Pets) do
		local angle = i * ((math.pi * 2) / #Pets)
		local x, z = getXAndZPositions(angle)

		local position = (hrp.CFrame * CFrame.new(x, 0, z)).p
		local lookAt = hrp.Position

		local tween = nil
		if part:IsA("Model") then
			tween = game:GetService("TweenService"):Create(part.PrimaryPart,TweenInfo.new(0.5),{CFrame = CFrame.new(position, lookAt)})
		else
			tween = game:GetService("TweenService"):Create(part,TweenInfo.new(0.5),{CFrame = CFrame.new(position, lookAt)})
		end
		tween:Play()
	end
	print("Beat")
end)

client

Yeah so you’re having the server FireAllClients every step. What I meant was to simply tell all clients when a pet is equipped or unequipped. Then have each client handle movement completely. But there’s more than one way of doing that, depending on how you want to set things up.

Okay so I just did exactly that and I still get frame rate issues also the frame rate things only happends when there are multiple players

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(Character)
		wait()
		local hrp = Character:FindFirstChild("HumanoidRootPart")
		game:GetService("RunService").Stepped:Connect(function()
			if not Character then return end
			game.ReplicatedStorage.MovePets:FireAllClients(Pets, hrp)
		end)

	end)
	
end)

The reason why it’s unplayable is because of game.Players.PlayerAdded, each time a player joins, it creates a new thread of RunService.Stepped, why not put

game:GetService("RunService").Stepped:Connect(function()
    if not Character then return end
    game.ReplicatedStorage.MovePets:FireAllClients(Pets, hrp)
end)

Outside the PlayerAdded event, and store all the joining player’s character in a table?
Then on replicating the movements to the client:

local NEXT_PING_INTERVAL = 1
local n = 0
local sendToClients = Instance.new("BindableEvent")
game:GetService("RunService").Heartbeat:Connect(function(dt)
    n = math.min(n + dt, NEXT_PING_INTERVAL)
    
    if n >= NEXT_PING_INTERVAL then -- sending alot of remotes to the client will stress the network I think, professionals, please correct me if im wrong
       local hrps = {}
       for playerName, character in pairs(STORED_CHARACTER_TABLE) do
          if not character then return end
          table.insert(hrps, character.HumanoidRootPart)
       end
       sendToClients:Fire(hrps)
    end
end)

sendToClients.Event:Connect(function(hrps)
    MovePets:FireAllClients(Pets, hrps)
end)
--!! This code is not tested
1 Like

Let the server make the part network owner as client, and any local changes to the part position by a local script will affect the server, so you don’t need any complex system, however you can make it complex but easier would be easy