GUI Tweening Replication

First post, so I apologize if anything something breaks convention in this post.

TL;DR: I need help replicating GUI tweening animations to all clients. The GUI is a BillboardGui attached to a part that floats around the player. These parts and associated GUIs are created/cloned server-side and are currently styled client-side. This obviously does not replicate the styling to all players and I am unsure on how to address this. Solutions or resources greatly appreciated, code snippets below.

Hello, I’m pretty inexperienced developer and have recently had the pleasure of having to worry about the replication of client happenings to the server. Resources I’ve seen so far on the forums don’t seem to address this particular problem, generally seeming to be about the tweening part’s movement, or I have missed some fundamental concepts from them.

My problem is this - I need to replicate the styling of BillboardGUIs to every player in the experience. These GUIs are currently created by the server as an attachment to a part and set to follow the player around like your average pet collection game. For that I avoided worrying about replicating the part movement by using attachments, align position, and setting the network ownership of the part to the player when the part is created/cloned server-side. This “solution” is not applicable here, and I want to avoid doing the animations/tweening server-side for performance and visual reasons. The solutions I’ve seen frequently make use of FireAllClients, though I do not see how that would function in this use case.

Server-Side Creation

local globalCat = game:GetService("ReplicatedStorage"):WaitForChild("GlobalCatFunction")

--other irrelevant functions and requirements

function CatManager.CreateCats(player)
	local userId = player.UserId

	--destroys all cats that exist in the workspace for that player
	local cats = game.Workspace.Cats:FindFirstChild(userId):GetChildren()

	for _, cat in cats do
		cat:Destroy()
	end

        --currently used to style the cats locally 
	local copyCats = {}

        --Fetches the player's information to be used for styling, the "cat" is a table of values needed for styling
	local ECats = PlayerManager.GetEquippedCats(player)

	for _, cat in ECats do
		table.insert(copyCats, table.clone(cat))

		local copyCat = game.ServerStorage.WorldCard:Clone()
                
                --This parents the cat to a folder in the workspace, with the folder having child folders of that player's userid
		copyCat.Parent = game.Workspace.Cats:FindFirstChild(userId)
		copyCat.Position = player.Character.HumanoidRootPart.Position + Vector3.new(0, 3, 0)

		--Basic styling using the ECats

		copyCat:SetNetworkOwner(player)
	end

	return copyCats
end

--listens for the call from from the client
globalCat.OnServerInvoke = function(player)
	return CatManager.CreateCats(player)
end

Client-Side Handling/Styling

local globalCat = game:GetService("ReplicatedStorage"):WaitForChild("GlobalCatFunction")
local GuiManager = require(ReplicatedStorage:WaitForChild("GuiManager"))

--other irrelevant functions, requirements

--fires from the module that handles player data on join and when a cat is equipped/unequipped
equipEvent.OnClientEvent:Connect(function()
	--globalEvent:FireServer()
--currently required for the styling
	local ecats = globalCatFun:InvokeServer()
	task.wait(.1)
	local cats = game.Workspace.Cats:WaitForChild(playerId):GetChildren()

	--positioning logic

	--primarily used for positioning the parts the gui is attached to
	for i, cat in cats do
--currently styles and applies the animations for the gui, works great - locally :) not that I expected anything different
		GuiManager.CreateWorldCard(ecats[i], cat.BillboardGui.GenCard)

		--more positioning logic
	end
end)

Replicated-Storage Styling Module that is Effectively a Local Script

--Animation module that requires it be run from a local script as it requires a PlayerGui for many functions and bricks otherwise. Very bad planning in hindsight and makes this module effectively local as a result.
local AnimationModule = require(game.StarterGui.ScreenGui:WaitForChild("AnimationModule"))

function GuiManager.CreateWorldCard(cat : cat, uiElement : SurfaceGui)
--crucial styling function that requires cat to be passed
	local rarityArr = GuiManager.GetRarity(cat)
	
	--more styling that requires the cat to be passed here
       
--uses the animation module multiple times, and is the primary reason for this post, just some tweening of gui elements under the hood.
       GuiManager.AnimationByRarity(rarityArr, uiElement)
end

I apologize to all for having to have read that code and understand the structure, but that should be all relevant pieces. As for solutions - I have considered moving the GuiManager.AnimationByRarity Call to the the client-side script and then loop through all of the players in the cat folder and apply the animations through that, but this sounds both inefficient and impossible to implement due to the cat data being required for the styling.

Ended up fixing it myself, here’s the changes I made if it helps anyone:

Server-Side

local globalCat = game:GetService("ReplicatedStorage"):WaitForChild("GlobalCatFunction")
local globalEvent = game.GetService("ReplicatedStorage"):WaitForChild("GlobalEvent")

--Used in the new FireAllClients at the bottom of CreateCats
local globalEvent = ReplicatedStorage:WaitForChild("GlobalEvent")

function CatManager.CreateCats(player)
	local userId = player.UserId

	--destroys all cats that exist in the workspace for that player
	local cats = game.Workspace.Cats:FindFirstChild(userId):GetChildren()

	for _, cat in cats do
		cat:Destroy()
	end

	local copyCats = {}

	local ECats = PlayerManager.GetEquippedCats(player)

	for _, cat in ECats do
		table.insert(copyCats, table.clone(cat))

		local copyCat = game.ServerStorage.WorldCard:Clone()

--Added unique names for the cloned parts so they can be searched from the client
		copyCat.Name = userId .. "-" .. cat.Id .. cat.Rolls

		copyCat.Parent = game.Workspace.Cats:FindFirstChild(userId)

		--styling

		copyCat:SetNetworkOwner(player)

--Added a FireAllClients event that passes information needed to search for the cloned cat from the client
		globalEvent:FireAllClients(cat, userId, copyCat.Name)
      end

Client-Side

local globalCatFun = ReplicatedStorage:WaitForChild("GlobalCatFunction")
local equipEvent = ReplicatedStorage:WaitForChild("EquipEvent")

--Receiving event for the FireAllClients server-side 
local globalEvent = ReplicatedStorage:WaitForChild("GlobalEvent")

--Remains the mostly the same, now only handles positioning
equipEvent.OnClientEvent:Connect(function()
	--Positioning
end)

--The event that styles and applies the animations of all player cats for all players
globalEvent.OnClientEvent:Connect(function(cat, userId, name)
	GuiManager.CreateWorldCard(cat, game.Workspace.Cats:FindFirstChild(userId):WaitForChild(name).BillboardGui.GenCard)
end)

And that’s all the changes I made. May have negative performance implications with all the searching in the client event, but who’s to say.