I’ve come across two problems with my enemy system that i would like to get help with fixing.
I’ve already made a post about this topic once, but after rewriting the system to work on the client it didnt really help with what i was trying to achieve.
In short, my system works like that: It spawns an enemy in a for loop, then waits a set amount of time and spawns another. Enemies are created and moved fully on the client, there’s ( currently ) nothing going on on the server.
Two problem i am currently facing is:
If the player lags, the enemies that should’ve spawned during that time will spawn later. This can be problematic for people with lower end devices, as they wont see where enemies are accurately.
I had a few solutions in mind, but none of them seemed to work, so im asking for help here. Im ready to provide snippets of code or a video recording of a problem if required!
Edit 1:
Here’s the function that handles spawning enemies on the client:
MODES.Start = function(modeName: string)
local gamemodeStructure = MODES[modeName]
if not gamemodeStructure then warn("no gamemode with name: "..modeName) return end
print(modeName.." selected! Game starts in 3 seconds!")
task.wait(3)
for waveNumber, Info in ipairs(gamemodeStructure.WAVES) do
print("wave: "..waveNumber)
for _, SpawnSequence in ipairs(Info.SPAWN_SEQUENCE) do
-- do it in a spawn function so that the delays work fine
task.spawn(function()
task.wait(SpawnSequence.Delay)
print("spawning "..SpawnSequence.Count.." zombies")
for i=1, SpawnSequence.Count do
task.spawn(function()
enemyModule.SpawnEnemy(
SpawnSequence.EnemyName,
waypoints
)
end)
task.wait(math.max(SpawnSequence.Interval,0.01))
end
end)
end
end
end
And here’s a video recording of the problem im talking about:
It’s hard to help accurately when you don’t provide any script reference, but from what you’re saying, all that comes to my mind is the possibility that you are not calculating in the deltaTime, I expect you’re moving all the enemies every hearbeat or similiar event, this even also returns an argument deltaTime, which represents the time in seconds since the last heartbeat was fired, using the deltaTime value in your movement calculations (multiplying the move vector with the value) should resolve the issue, considering that’s the root of the problem.
I use deltatime to calculate movement, the only problem i have is when a player lags, after the lag, the enemies that were supposed to spawn earlier now fall behind.
Also, sorry for not providing any code! Ill edit the post to have a few lines that may be helpful!
Well I recommend doing it on the server instead then, because right now the NPC’s movement is influenced by the client. This could also be a good thing for exploiters, for them. Not you though.
So basically, the fix for your problem would be to do these things on the server.
I think I can see the issue, it’s the task.wait() which has to wait until the player’s device unlags so roblox engine can resume the execution again. This leaves us with two solutions that come to my mind.
1: Start with replicating from the server. You said everything is currently clientside, that alone brings a ton of possibilities for desync, you should keep track of the enemy positions on server, and relay their position values to the client occasionaly, if a client detects that the position is too far off from the server one, it moves the enemy appropriately. This would fix the issue and you have to do it eventually to keep the integrity and be 100% sure its synced, as many things can happen on the client leading to desync.
2: Make a system for handling late spawns. To elaborate, basically some kind of data mod which keeps track of the desired spawn times for the enemies, and spawn them according to that. If the current time is too far from the desired spawn time - (lag spike happened and execution resumed too late) spawn the enemies in appropriate distance from their spawn location calculated from the delta of desired and actual time
The NPCs that are being created on the client are just a fake representation of what is going on the server. All of the data, all of the values are stored there. Exploiters can delete enemies on the client for themselves but it won’t really do anything besides ruining their own experience.
I tried sending data from the server to the client occasionally, but that had a few issues:
I need to account for the latency between the server and the client, and I’m yet to find an accurate solution to this problem. ( if i use tick() there will be around 0.1 second delay every time for some reason ). Also setting the position of an NPC like that will cause a lot of jittery enemy movement and i dont want that to happen
My game is specifically designed to handle a lot of enemies at once ( im talking around 1k enemies on the map at the same time without too much lag ) and if i send the data to the client every second or so that will cause ping spikes which will in turn make it even harder to account for the server-client latency.
I’ll go ahead and try the second solution. I’ll keep you all updated on if it works or not
First off, you know there’s a function that provides you with a client ping by calling :GetNetworkPing() on a player instance, and it’s pretty accurate aswell.
Second off, you can interpolate the npc movement to not make it jittery, its a common practice to lerp positions that you get from server, since networking is never reliable.
Third off, if you use Vector3int16() for the positions, which should be good enough for the small maps you have, one Vector3int16() has 6bytes, and roblox networking optimizes the instance even more when you send it (as far as I know), so even if you were to send positions of 1000 units at once, it would be only 5.86KB of data! (give or take) which is not really a lot, also not sending it all at once but gradually in smaller packets could resolve lag spikes if it were to cause them.