Syncing projectiles over clients

I have been having issues with syncing projectiles that need to be seen by both players,

The goal is to have both players see the Projectile, however I want the player firing it to get the feedback of the projectile (Example: If a magic game has someone cast a fireball, they should see their fireball appear as it is a graphical effect, the actual fireball is not used for any hit-detection.

The way that I originally tried it was by having the client make an un-replicated version which the Server would then create later (offset accordingly for lag-compensation), once the server sends back that it was done, the client simply deletes their version

-- ClientSide Code
local someRemoteFunction --target whatever RemoteFunction you use for Projectile effect Graphics

function fireProjectile(projectile, position, direction)
    local dummyProjectile = projectile:Clone()
    dummyProjectile.Parent = workspace
    dummyProjectile.AssemblyLinearVelocity = direction
    local completeSyncedProjectile = someRemoteFunction:InvokeServer(projectile, position, direction, tick())

-- ServerSide Code
function someRemoteFunction.OnInvokeServer(player, projectile, position, direction, timeStamp)
    local timeDiff = tick() - timeStamp
    local projectile_True = projectile:Clone()
    projectile_True.Parent = workspace
    projectile_True.Position = position + (direction * timeDiff)
    projectile_True.AssemblyLinearVelocity = direction
    return projectile_True

But I feel like this could be done way better, is there any advice on whether this is being made overtly-complicated?

Set some RemoteEvents in the Server for whatever projectiles can be made. Say, one called “Fireball.” Have it include parameters such as the caster, the target, etc.

The server will take care of values such as damage because it’s important information. For values that concern what it looks like, solely cosmetic stuff, have the server send that info over to all clients.

Have each client contain LocalScripts that contain code on what each projectile effect looks like. Pass parameters such as the name, the caster, and the target. Then just let each client create their own projectile animations from the info given.

Trying to perfectly sync stuff like projectiles can result in unwanted lag. This is the way I’d go about it as it makes it so that the syncing between players is solely dependent upon their Internet connection, a factor that we cannot control. If their Internet is fine, everyone should be synced pretty well if they’re all using the same script with projectile information.

Some pseudocode as an example:

Client (caster):



workspace.Attacks.Fireball.OnClientEvent:Connect(function(player, target)
    --Let the server determine any damage a target takes, say based on their level
    --Maybe also include an if statement that ensures the player is allowed to
        --use this attack, say if there is a wait involved
    game.ReplicatedStorage.Attacks:FireAllClients(caster, target, damage, nameOfAttack) --nameOfAttack would be Fireball

Each client:

game.ReplicatedStorage.Attacks.OnServerEvent:Connect(function(caster, target, damage, nameOfAttack)
    --Have each client activate some script or require a ModuleScript
        --containing each move as well as what it does while passing
        --the parameters