R-Type
Distributed multiplayer game engine in C++
Loading...
Searching...
No Matches
ServerLoop.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** Created by hugo on 12/12/2025
4** File description:
5** ServerLoop.cpp - Fixed timestep game loop implementation
6*/
7
9#include <algorithm>
12
13namespace server {
14
15 ServerLoop::ServerLoop(std::unique_ptr<IGameLogic> gameLogic, std::shared_ptr<EventBus> eventBus,
16 float gameSpeedMultiplier)
17 : _gameLogic(std::move(gameLogic)),
18 _eventBus(eventBus),
19 _gameSpeedMultiplier(std::clamp(gameSpeedMultiplier, 0.25f, 1.0f)),
20 _scaledTimestep(BASE_FIXED_TIMESTEP * _gameSpeedMultiplier) {}
21
25
27 if (_initialized.exchange(true)) {
28 return true;
29 }
30
31 LOG_INFO("Initializing deterministic game loop...");
32
33 try {
34 if (!_gameLogic->initialize()) {
35 LOG_ERROR("Failed to initialize game logic");
36 _initialized.store(false);
37 return false;
38 }
39
40 LOG_INFO("✓ Game logic initialized");
41 LOG_INFO("✓ Fixed timestep: ", BASE_FIXED_TIMESTEP, "s (", 1.0f / BASE_FIXED_TIMESTEP, " Hz)");
42 if (_gameSpeedMultiplier < 1.0f) {
43 LOG_INFO("✓ Game speed multiplier: ", static_cast<int>(_gameSpeedMultiplier * 100), "%");
44 LOG_INFO("✓ Scaled game time per tick: ", _scaledTimestep * 1000.0f, "ms");
45 }
46
47 return true;
48 } catch (const std::exception &e) {
49 LOG_ERROR("Initialization failed: ", e.what());
50 _initialized.store(false);
51 return false;
52 }
53 }
54
56 if (_loopThread.joinable()) {
57 LOG_WARNING("Game loop already running");
58 return;
59 }
60
61 LOG_INFO("Starting game loop thread...");
62
63 try {
64 _timeAccumulator = 0.0;
65 _frameCount = 0;
67
69 _loopThread = std::jthread([this](std::stop_token st) { _gameLoopThread(st); });
70
71 LOG_INFO("✓ Game loop thread started");
72 } catch (const std::exception &e) {
73 LOG_ERROR("Failed to start game loop: ", e.what());
74 throw;
75 }
76 }
77
79 LOG_INFO("Stopping game loop...");
80 _loopThread.request_stop();
81 }
82
83 uint32_t ServerLoop::getCurrentTick() const {
84 return _frameCount;
85 }
86
87 std::shared_ptr<ecs::wrapper::ECSWorld> ServerLoop::getECSWorld() {
88 if (auto *gameLogic = dynamic_cast<server::GameLogic *>(_gameLogic.get())) {
89 return gameLogic->getECSWorld();
90 }
91 return nullptr;
92 }
93
94 void ServerLoop::_gameLoopThread(std::stop_token stopToken) {
95 LOG_DEBUG("Game loop thread started");
96
97 while (!stopToken.stop_requested()) {
98 try {
99 double frameTime = _frameTimer.tick();
100
101 // Cap frame time to prevent spiral of death (lag recovery)
102 if (frameTime > 0.1) {
103 LOG_WARNING("Frame time exceeded 100ms (", frameTime * 1000.0, "ms)");
104 frameTime = 0.1;
105 }
106
107 _timeAccumulator += frameTime;
108
109 int frameSkips = 0;
110 while (_timeAccumulator >= BASE_FIXED_TIMESTEP && frameSkips < 5) {
111 _fixedUpdate();
113 _frameCount++;
114 frameSkips++;
115 }
116
117 if (frameSkips > 1) {
119 }
120
122
123 } catch (const std::exception &e) {
124 LOG_ERROR("Thread exception: ", e.what());
125 LOG_ERROR("Continuing...");
126 }
127 }
128 }
129
131 std::scoped_lock lock(_stateMutex);
132
133 try {
134 // Pass scaled timestep to systems so game time passes slower
135 // when gameSpeedMultiplier < 1.0
137 } catch (const std::exception &e) {
138 LOG_ERROR("Game logic update failed: ", e.what());
139 }
140 }
141
142} // namespace server
#define LOG_INFO(...)
Definition Logger.hpp:181
#define LOG_DEBUG(...)
Definition Logger.hpp:180
#define LOG_ERROR(...)
Definition Logger.hpp:183
#define LOG_WARNING(...)
Definition Logger.hpp:182
double tick()
Get elapsed time and automatically reset (optimized for game loops)
static void sleepMilliseconds(int milliseconds)
Sleep for specified milliseconds (centralized time management)
void reset()
Reset the timer to the current time.
Deterministic, authoritative server game logic.
Definition GameLogic.hpp:57
uint32_t getCurrentTick() const
Get the current server tick.
std::shared_ptr< ecs::wrapper::ECSWorld > getECSWorld()
Get reference to ECS world from GameLogic.
void _fixedUpdate()
Process a single fixed timestep update.
~ServerLoop() override
Destructor - ensures clean shutdown.
void _gameLoopThread(std::stop_token stopToken)
Main game loop function (runs in thread)
void stop() override
Stop the game loop (IServerLoop implementation)
ServerLoop(std::unique_ptr< IGameLogic > gameLogic, std::shared_ptr< EventBus > eventBus, float gameSpeedMultiplier=1.0f)
Constructor.
std::unique_ptr< IGameLogic > _gameLogic
bool initialize()
Initialize the game loop.
FrameTimer _frameTimer
std::jthread _loopThread
void start() override
Start the game loop (IServerLoop implementation) Runs in background thread.
std::atomic< bool > _initialized
static constexpr float BASE_FIXED_TIMESTEP
std::mutex _stateMutex