Synchronizing timers in multiplayer games requires careful consideration of network latency and state management. Here's a pattern that helps maintain consistency across clients while being resistant to network issues.
Instead of letting each client manage its own timers independently, we synchronize based on a shared start time:
#include <SDL.h>
#include <iostream>
class NetworkSpawner {
public:
struct SpawnConfig {
Uint32 StartTime;
Uint32 Interval;
int WaveSize;
};
void StartSpawning(
const SpawnConfig& Config) {
MyConfig = Config;
IsSpawning = true;
LastSpawnTime = Config.StartTime;
std::cout << "Starting wave at time "
<< Config.StartTime << "ms\n";
}
void Update() {
if (!IsSpawning) { return; }
Uint32 CurrentTime{SDL_GetTicks()};
Uint32 TimeSinceStart{
CurrentTime - MyConfig.StartTime};
Uint32 ExpectedSpawns{
TimeSinceStart / MyConfig.Interval};
// Spawn any missing enemies to catch up
while (SpawnCount < ExpectedSpawns &&
SpawnCount < MyConfig.WaveSize) {
SpawnEnemy(CurrentTime);
SpawnCount++;
}
// Check if wave is complete
if (SpawnCount >= MyConfig.WaveSize) {
IsSpawning = false;
}
}
private:
void SpawnEnemy(Uint32 Time) {
std::cout << "Spawning enemy at time "
<< Time << "ms\n";
LastSpawnTime = Time;
}
SpawnConfig MyConfig;
bool IsSpawning{false};
Uint32 LastSpawnTime{0};
Uint32 SpawnCount{0};
};
// Simulate network message received from server
void HandleServerMessage(
NetworkSpawner& Spawner) {
NetworkSpawner::SpawnConfig Config;
Config.StartTime = SDL_GetTicks();
Config.Interval = 1000;
Config.WaveSize = 3;
Spawner.StartSpawning(Config);
}
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_TIMER);
NetworkSpawner Spawner;
HandleServerMessage(Spawner);
// Simulate game loop
for (int i = 0; i < 4; ++i) {
Spawner.Update();
SDL_Delay(1000);
}
SDL_Quit();
return 0;
}
Starting wave at time 0ms
Spawning enemy at time 1008ms
Spawning enemy at time 2008ms
Spawning enemy at time 3009ms
Server Authority: The server sends:
Time Synchronization: Clients convert server time to local time:
Uint32 ServerToLocalTime(Uint32 ServerTime) {
return ServerTime + EstimatedTimeOffset;
}
Catch-up Mechanism: If a client falls behind (lag spike, etc), it can catch up by spawning multiple enemies in one frame, maintaining consistency with other clients.
State Verification: Periodically verify game state with the server:
void VerifyState(const ServerState& State) {
if (SpawnCount != State.ExpectedSpawns) {
// Reconcile difference
SpawnCount = State.ExpectedSpawns;
}
}
Remember to:
This approach provides a good balance between responsiveness and consistency while being resistant to network issues.
Answers to questions are automatically generated and may not have been reviewed.
Learn how to use callbacks with SDL_AddTimer()
to provide functions that are executed on time-based intervals