Hi! I have a game where you can shoot spells. To prevent exploits, I handle the spells on the client. What I do is the client send remote event to server, the server creates the spell, starts a loop (with a task.wait() inside), and inside the loop it first calculates the cframe, then change the cframe of the spell to 2 studs forward, and repeats until it hits an object. However, when I shot like 20 Spells, the game starts to lag and the performance of the script reached 10%. I think I might need to put some code in the client side but I don’t want to allow exploits. How can I reduce lag while not allowing exploit? (Many other shooting games like jailbreak, paintball, arsenal, bedwars, and a lot of them have exploit issues) Thanks!
The Code
while task.wait() do
for i = 1, 4 do
--detect if this spell touched something
local rayOrigin = model.PrimaryPart.Position
local rayDirect = getVector(model,i)
local rayResult = workspace:Raycast(rayOrigin,rayDirect,raycastParam)
if i == 1 then
model.PrimaryPart:PivotTo(model.PrimaryPart.CFrame + model.PrimaryPart.CFrame.LookVector * model.PrimaryPart.Size.Z )
--moves the spell in the direction its facing by a little
end
if rayResult then
local v = rayResult.Instance
if v then
--this spell touched a part
hit = true
end
end
if hit then break end
end
if hit then break end
end
model:Destroy()
return
Thanks if you can help! I really need to lower the lag in my game and also not allow exploit at the same time.
I think you’d be better off using a different method to update the position than a while loop. You can try using RunService.Stepped, or mover constraints like LinearVelocity.
This is already a simple code, there is nothing to optimize here, you will have to do it on the client, but on the server you can still put various checks against esploitors, for example, checking the distance, checking that the player can use this skill, and so on, and all the visual part needs to be done on client so that the player does not feel the delay
I agree with what you say about putting the visuals on the client. I will put all the checks like the player being allowed to use this skill, but how do I check if the bullet/projectile touches a player from the server? Because if the bullet is handled on the client, how can the server know where the position of the bullet is at?
bullet hit should be done through raycast, or if you need area damage, then through GetPartBoundsInBox, and in parallel do a visualization of the bullet flight to the point where the raycast hit
oh ok, but doesn’t that mean the server will detect the player who got hit by the bullet before the bullet visual on the client hit the player? because it takes time for the bullet to travel, but the server just did a raycast and raycast is instantaneous, so if the player who was about to get hit by the bullet moved out of the way when he saw it, he will still get hit even if the bullet didn’t touch him. Sorry if I’m not clear enough but do you know how to fix this issue? If there’s any part of what I said confusing please let me know, thanks!
client starts a spell. and sends it to server
server checks if the player is allowed to shoot the spell.
server hands out client events with the start and end location.
client recieves start and end location and calculated tje desired effect by those position.
you’re probably having lag because the server ks getting many tasks. you need to see it this way. each time something on the server changes. it has to calculate and replicate to all players. but when a client does something its just calculate and render. way less expensive.
thank you for the detailed explanation! I would be more than happy to set it as the solution, but I have just one question: How will the server detect if they spell touched another player? Because as you said, the spell part in workspace is on the client side, so how does the server detect if it touched another player?
What is the purpose of the i = 1, 4 loop? It looks like you’re only moving the model once for every 4 identical calculations? If that’s the case, why are you running 4 identical calculations for every step?
Any code with while task.wait() do is flawed. There is your issue, there are better and more efficient ways to do anything and everything without ever using that line of code above. Ray casting 4 times every 4ms will cause lag, even on Skynet!
well, you check if a player is hit on the server. only the effect itself is made on the clients side.
edit:
you can use raycasting if the hit is suposed to be like a bullet (single hit)
or getPartsWithinPart to check if any players are in it.
usefull for explosions. (whitelist only player root parts)
I have 2 modules u can use for both scenarios.
I’ll list them when I’m near my laptop again.
edit2:
if you are calculating the position of your magic trail every x seconds. you can pass a remote event when you create it and pass it to the client. when ever you update the position on the server you fire the event you gave to the magic. giving it its current position. then you can use that info on the client to change the effects position. using tween or something.
to be clear you dont pass the event to the xlient on each update just on creation of the magic.
I’ve tried them both (in a while task.wait() do code), and it lags when there are like 20 or more bullets moving at the same time. How often should I use GetPartsInPart() and workspace:RayCast() ? As of right now, I’m using it like 30 times a second because while task.wait() do
while task.wait() do
for i = 1, 4 do
--detect if this spell touched something
local rayOrigin = model.PrimaryPart.Position
local rayDirect = getVector(model,i)
local rayResult = workspace:Raycast(rayOrigin,rayDirect,raycastParam)
if i == 1 then
model.PrimaryPart:PivotTo(model.PrimaryPart.CFrame + model.PrimaryPart.CFrame.LookVector * model.PrimaryPart.Size.Z )
--moves the spell in the direction its facing by a little
end
if rayResult then
local v = rayResult.Instance
if v then
--this spell touched a part
hit = true
end
end
if hit then break end
end
if hit then break end
end
model:Destroy()
return
it doesn’t lag when there is only one or two spells, it starts to lag when there are over 30 spells in the air. The script performance tab showed 10% performance when there are over 30 spells. Here is the code for getVector()
local function getVector (model,num)
if num == 1 then
return model.PrimaryPart.CFrame.LookVector*model.PrimaryPart.Size.Z
end
if num == 2 then
return model.PrimaryPart.CFrame.LookVector*-1*model.PrimaryPart.Size.Z
end
if num == 3 then
return model.PrimaryPart.CFrame.UpVector*model.PrimaryPart.Size.Y
end
if num == 4 then
return model.PrimaryPart.CFrame.UpVector*-1*model.PrimaryPart.Size.Y
end
if num == 5 then
return model.PrimaryPart.CFrame.RightVector*model.PrimaryPart.Size.X
end
if num == 6 then
return model.PrimaryPart.CFrame.RightVector*-1*model.PrimaryPart.Size.X
end
end
I’m pretty sure getVector() isn’t the problem for the lag, I think it’s the while loop.
For run service is it possible to make it fire at a constant speed? Because I want the spell to travel at a constant speed or else it won’t match the speed of the bullet on the client side, which could result in desynchronization of the server bullet and the client bullet. How do I make .stepped constant?
if you use deltatime and do speed * deltatime. if a frame skips it will go at a faster speed. so it wil be consistent yeah.
you can even use tick() to check when it was fired on the server. then when you se the bullet for the first time. you do position + (speed * direction * (tick() - tickfromserver)) then it wil go as far as it wouldve gone without delay. only problem is you dont see the magic leave the staff because of the delay. you can prevent that by tweening the clients bullet on each updat. it makes for a slight delay but it does make it look better.
wow what you said make a lot of sense, for the tick() how does it work exactly? i’m not trying to ask you to spoon feed me but maybe can u provide pseudo code for what you just said? (pseudo code is basically code that is all in comments) If not it’s fine, I just wanna understand how to use tick(). Thanks!
Edit: Ok turns out tick() is just os.time()
but i dont understand completely what you said. Do you mind explaning it to me in another way? Do I keep using FireAllClient on every stepped? And does the client keep making a new tween assuming FireAllClient() is constantly firing? Thank a lot for your reply and explanation!