Greetings Robloxians. Recently I have been working on a third-person shooter, but latency between the server and client has been, horrid, to say the least. I am working on an automatic rifle that is supposed to fire every 0.15 seconds, but instead it jankily shoots every now and then, with sometimes latency making you have to wait a whole second to 2 seconds for the next fire, horrible UX.
I somewhat circumvented this by having the shooting player have bullets perfectly in-sync on their side, but in reality the gun can randomly stop working and not a single bullet would legitimately be fired, until you stop shooting and start shooting again.
Why this happens boggles my mind. My first guess is that 0.15 seconds just might be too much data too fast for Roblox’s server-client boundary to handle, but that is straight up pathetic if so, and not to mention that I’ve seen more games than I can count on Roblox that have faster guns than what I have.
The shooting and bullets themselves is created immediately when the gun shoots on the client, so there is never any lag with those, but the gun gui that displays the ammo and rounds does not update until the server fully comprehends that the bullet was shot, so you can gauge what other people see via the ammo amount in the bottom of the screen.
Here is a video of the lag in action:
As you can see, there is a large delay when at 20 bullets and a small lag spike happens again at 9. This is a much better version of the lag, when I have 4 people in the server shooting (the game is designed for 8) the lag gets much more unbearable.
This can be a huge issue because the player could be shooting straight onto an enemy for upwards of 5 seconds, but none of them will technically register, and their ammo will never run out.
Now the question is how in the world would I fix this latency?
To worsen this, I have to switch hit detection from the client side to the server side to prevent easy exploits from occuring, which I would have reason to believe the lag will only worsen. Thus I have two questions:
1: How do I optimize the code of my gun to end this horrid latency? (client and server sided code below)
2: How would I create the ray of the bullet on the server side, now that I can’t use mouse.hit.p?
CLIENT CODE:
local function shoot()
local hold = true
repeat
if userInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) or userInputService:IsGamepadButtonDown(Enum.UserInputType.Gamepad1, Enum.KeyCode.ButtonR2) then
hold = true
else
hold = false
end
if CanShoot == true and tool.Ammo.Bullets.Value > 0 and hold == true then
CanShoot = false
local ray = Ray.new(tool.WeaponModel.Hole.CFrame.p, (mouse.Hit.p - tool.WeaponModel.Hole.CFrame.p).unit * weapon.Stats.MaxRange.Value)
local ignore = {player.Character, table.unpack(tool:GetChildren())}
for i, v in pairs(game.Players:GetChildren()) do
if v.Character then
if v.Character:FindFirstChild("Hat") then
if v.Character.Hat:FindFirstChild("Handle") then
table.insert(ignore, v.Character.Hat.Handle)
end
end
end
end
for i, v in pairs(game.Workspace.Map.Tools:GetDescendants()) do
table.insert(ignore, v)
end
for i, v in pairs(game.Workspace.Map.Buffs:GetDescendants()) do
table.insert(ignore, v)
end
for i, v in pairs(game.Workspace.Map.Spawns:GetDescendants()) do
table.insert(ignore, v)
end
local hitPart, position = workspace:FindPartOnRayWithIgnoreList(ray, ignore, false, true)
if hitPart then
local hitpoint = mouse.Hit.p
tool.Events.Fire:FireServer(hitPart, hitpoint)
end
game.ReplicatedStorage.Events.PlayerEvents.GunScreen:Fire()
recoilTrack:Play()
local weaponName = character.Tool.WeaponName.Value
local weaponFolder = game.ReplicatedStorage.Gamemodes.Mode.Weapons[weaponName]
local tool = character.Tool
-- bullet
local bullet = weaponFolder.Bullet:Clone()
collectionService:AddTag(bullet, "bullet")
bullet.CFrame = CFrame.new((tool.WeaponModel.Hole.CFrame * CFrame.new(0,0,0)).p, mouse.Hit.p)
local rotate = CFrame.Angles(0, math.rad(-90),0)
bullet.CFrame = bullet.CFrame:ToWorldSpace(rotate)
local bodyV = Instance.new("BodyVelocity", bullet)
bodyV.Velocity = CFrame.new(bullet.Position, mouse.Hit.p).LookVector * 260
bullet.Transparency = 0
bullet.PlayerValue.Value = player.Name
bullet.Parent = game.Workspace.Current
player.PlayerScripts.Weapons.Bullet.Launched:Fire()
tool.Handle.Fire:Play()
wait(cooldown)
CanShoot = true
else
hold = false
end
until hold == false
end
tool.Equipped:Connect(function()
gunGui.Ammo.Ammo.Text = tool.Ammo.Bullets.Value
gunGui.Rounds.Rounds.Text = tool.Ammo.Rounds.Value
local tweenInfo = TweenInfo.new(0.3, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out, 0, false, 0)
local goal = {}
goal.FieldOfView = 70
local tween = tweenService:Create(workspace.Camera,tweenInfo, goal)
tween:Play()
aimBool.Value = false
local tweenInfo = TweenInfo.new(0.75, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out, 0, false, 0)
local goal = {}
goal.Position = UDim2.fromScale(0.412, 0.85)
local tween = tweenService:Create(gunGui,tweenInfo, goal)
tween:Play()
equipTrack:Play()
equipTrack.Stopped:Wait()
CanShoot = true
mouse.Icon = "rbxassetid://6945978207"
holdTrack:Play()
actionService:BindAction("MouseFire", shoot, false, Enum.UserInputType.MouseButton1)
actionService:BindAction("ControllerFire", shoot, true, Enum.KeyCode.ButtonR2)
actionService:BindAction("MouseReload", reload, false, Enum.KeyCode.R)
actionService:BindAction("ControllerReload", reload, true, Enum.KeyCode.ButtonX)
actionService:BindAction("MouseAim", aim, false, Enum.UserInputType.MouseButton2)
actionService:BindAction("ControllerAim", aim, true, Enum.KeyCode.ButtonL2)
end)
tool.Events.Fire.OnClientEvent:Connect(function(ammo)
gunGui.Ammo.Ammo.Text = ammo
-- this gets fired back by the server, so the gunGui text will not change until the server registers the bullet, thus you can detect lag with the gunGui on the bottom of the screen.
end)
SERVER SIDED CODE:
events.Fire.OnServerEvent:Connect(function(player, hitPart, hitpoint)
if tool.Ammo.Bullets.Value > 0 and CanRun == true then
local localPlayer = players:GetPlayerFromCharacter(tool.Parent)
CanRun = false
game.ReplicatedStorage.Events.GameEvents.WeaponEvents.Bullet:FireAllClients(player, hitpoint, tool)
tool.Ammo.Bullets.Value = tool.Ammo.Bullets.Value - 1
events.Fire:FireClient(player, tool.Ammo.Bullets.Value)
if hitPart ~= nil then
local part = hitPart
local humanoid = part.Parent:FindFirstChild("Humanoid")
if humanoid then
local char = part.Parent
local plr = players:GetPlayerFromCharacter(char)
if plr ~= localPlayer then
local damage = weapon.Stats.Damage.Value
if hitPart.Name == "Head" then
damage = weapon.Stats.Damage.Value * weapon.Stats.HeadshotMult.Value
end
local changeType = "Damage"
game.ReplicatedStorage.Events.PlayerEvents.HealthChange:Fire(player, plr, damage, changeType, game.ReplicatedStorage.Gamemodes.Mode.Weapons[tool.WeaponName.Value].DisplayName.Value)
end
end
print("finished!")
end
CanRun = true
end
end)