R-Type
Distributed multiplayer game engine in C++
Loading...
Searching...
No Matches
Room.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** Created by hugo on 06/12/2025
4** File description:
5** Room.cpp
6*/
7
9#include <algorithm>
10#include <stdexcept>
18
19namespace server {
20
21 Room::Room(const std::string &id, const std::string &name, size_t maxPlayers, bool isPrivate,
22 float gameSpeedMultiplier, std::shared_ptr<EventBus> eventBus)
23 : _id(id),
24 _name(name.empty() ? id : name),
25 _state(RoomState::WAITING),
26 _maxPlayers(maxPlayers),
27 _isPrivate(isPrivate),
28 _gameSpeedMultiplier(gameSpeedMultiplier),
29 _hostPlayerId(0),
30 _gameStartSent(false) {
31
32 // Use provided EventBus or create a new one
33 _eventBus = eventBus ? eventBus : std::make_shared<EventBus>();
34 std::shared_ptr<ecs::wrapper::ECSWorld> ecsWorld = std::make_shared<ecs::wrapper::ECSWorld>();
35 std::shared_ptr<ThreadPool> threadPool = std::make_shared<ThreadPool>(4);
36 threadPool->start();
37 std::unique_ptr<IGameLogic> gameLogic = std::make_unique<GameLogic>(ecsWorld, threadPool, _eventBus);
38 _gameLoop = std::make_unique<ServerLoop>(std::move(gameLogic), _eventBus, _gameSpeedMultiplier);
39
40 if (!_gameLoop->initialize()) {
41 LOG_ERROR("Failed to initialize game loop for room ", _id);
42 throw std::runtime_error("Failed to initialize game loop for room " + _id);
43 }
44
45 _gameLoop->start();
46
47 _gameLogic = std::shared_ptr<IGameLogic>(&_gameLoop->getGameLogic(), [](IGameLogic *) {});
48
49 LOG_INFO("Room '", _name, "' (", _id, ") created [State: WAITING, Max: ", _maxPlayers,
50 " players, Private: ", (_isPrivate ? "Yes" : "No"), "] with dedicated GameLoop");
51 }
52
53 bool Room::join(uint32_t playerId) {
54 std::lock_guard<std::mutex> lock(_mutex);
55
56 if (isFull()) {
57 LOG_WARNING("Player ", playerId, " cannot join room ", _id, " - room is full");
58 return false;
59 }
60
62 LOG_WARNING("Player ", playerId, " cannot join room ", _id, " - game already in progress");
63 return false;
64 }
65 if (hasPlayer(playerId)) {
66 LOG_WARNING("Player ", playerId, " already in room ", _id);
67 return false;
68 }
69 _players.push_back(playerId);
70
71 if (_players.size() == 1) {
72 _hostPlayerId = playerId;
73 LOG_INFO("Player ", playerId, " is host of room ", _id);
74 }
75
76 LOG_INFO("Player ", playerId, " joined room ", _id, " (", _players.size(), "/", _maxPlayers, ")");
77 return true;
78 }
79
80 bool Room::joinAsSpectator(uint32_t playerId) {
81 std::lock_guard<std::mutex> lock(_mutex);
82
83 if (hasPlayer(playerId) || hasSpectator(playerId)) {
84 LOG_WARNING("Player ", playerId, " already in room ", _id);
85 return false;
86 }
87
88 _spectators.push_back(playerId);
89 LOG_INFO("Spectator ", playerId, " joined room ", _id, " (", _spectators.size(), " spectators)");
90 return true;
91 }
92
93 bool Room::leave(uint32_t playerId) {
94 std::lock_guard<std::mutex> lock(_mutex);
95
96 // Check if it's a regular player
97 auto it = std::find(_players.begin(), _players.end(), playerId);
98 if (it != _players.end()) {
99 _players.erase(it);
100 LOG_INFO("Player ", playerId, " left room ", _id, " (", _players.size(), " remaining)");
101
102 if (playerId == _hostPlayerId && !_players.empty()) {
104 LOG_INFO("Player ", _hostPlayerId, " is new host");
105 }
106
107 // Reset room to WAITING if all players left
110 _gameStartSent = false;
111 LOG_INFO("Room ", _id, " reset to WAITING (no players left)");
112 }
113
114 return true;
115 }
116
117 // Check if it's a spectator
118 auto specIt = std::find(_spectators.begin(), _spectators.end(), playerId);
119 if (specIt != _spectators.end()) {
120 _spectators.erase(specIt);
121 LOG_INFO("Spectator ", playerId, " left room ", _id, " (", _spectators.size(),
122 " spectators remaining)");
123 return true;
124 }
125
126 return false;
127 }
128
130 if (_state != state) {
131 const char *stateNames[] = {"WAITING", "STARTING", "IN_PROGRESS", "FINISHED"};
132 LOG_INFO("Room '", _name, "' state: ", stateNames[static_cast<int>(_state)], " -> ",
133 stateNames[static_cast<int>(state)]);
134 _state = state;
135 }
136 }
137
138 size_t Room::getPlayerCount() const {
139 std::lock_guard<std::mutex> lock(_mutex);
140 return _players.size();
141 }
142
143 std::vector<uint32_t> Room::getPlayers() const {
144 std::lock_guard<std::mutex> lock(_mutex);
145 return _players;
146 }
147
148 std::vector<uint32_t> Room::getSpectators() const {
149 std::lock_guard<std::mutex> lock(_mutex);
150 return _spectators;
151 }
152
153 bool Room::hasPlayer(uint32_t playerId) const {
154 return std::find(_players.begin(), _players.end(), playerId) != _players.end();
155 }
156
157 bool Room::hasSpectator(uint32_t playerId) const {
158 return std::ranges::find(_spectators, playerId) != _spectators.end();
159 }
160
162 std::lock_guard<std::mutex> lock(_mutex);
163 return RoomInfo{
164 _id, _name, _state, _players.size(), _maxPlayers, _isPrivate, std::to_string(_hostPlayerId)};
165 }
166
168 std::lock_guard<std::mutex> lock(_mutex);
169
171 LOG_WARNING("Cannot start game in room ", _id, " - invalid state");
172 return false;
173 }
174
175 if (_players.empty()) {
176 LOG_WARNING("Cannot start game in room ", _id, " - no players");
177 return false;
178 }
179
180 if (_gameLogic) {
181 if (!_gameLogic->initialize()) {
182 LOG_ERROR("Failed to initialize game logic for room ", _id);
183 return false;
184 }
185
186 // Spawn enemies (Lua scripts) now that game is starting
187 // Cast to GameLogic to access spawnEnemies()
188 LOG_INFO("Will call onGameStart for room ", _id);
189 auto gameLogicPtr = std::dynamic_pointer_cast<GameLogic>(_gameLogic);
190 if (gameLogicPtr) {
191 gameLogicPtr->onGameStart();
192 } else {
193 LOG_ERROR("Failed to cast IGameLogic to GameLogic for room ", _id);
194 return false;
195 }
196
197 // Spawn players and validate entity IDs
198 std::vector<uint32_t> failedPlayers;
199 for (uint32_t playerId : _players) {
200 uint32_t entityId = _gameLogic->spawnPlayer(playerId, "Player" + std::to_string(playerId));
201 if (entityId == 0) {
202 LOG_ERROR("Failed to spawn player ", playerId, " in room ", _id);
203 failedPlayers.push_back(playerId);
204 }
205 }
206
207 // Remove players that failed to spawn
208 if (!failedPlayers.empty()) {
209 for (uint32_t playerId : failedPlayers) {
210 auto it = std::find(_players.begin(), _players.end(), playerId);
211 if (it != _players.end()) {
212 _players.erase(it);
213 LOG_WARNING("Removed player ", playerId, " from room ", _id, " due to spawn failure");
214 }
215 }
216
217 // Check if we still have players after removals
218 if (_players.empty()) {
219 LOG_ERROR("No players left in room ", _id, " after spawn failures");
220 return false;
221 }
222 }
223 }
224
226 LOG_INFO("Game started in room ", _id, " with ", _players.size(), " players");
227 return true;
228 }
229
230 void Room::update(float deltaTime) {
232 static uint32_t tick = 0;
233 _gameLogic->update(deltaTime, tick++);
234
235 if (!_gameLogic->isGameActive()) {
237 LOG_INFO("Game ended in room ", _id);
238 }
239 }
240 }
241
242 void Room::setHost(uint32_t playerId) {
243 if (hasPlayer(playerId)) {
244 _hostPlayerId = playerId;
245 LOG_INFO("Player ", playerId, " is now host of room ", _id);
246 }
247 }
248
250 if (_state == RoomState::WAITING) {
251 LOG_INFO("Room ", _id, " starting game immediately (", _players.size(), " players)");
252 startGame();
253 }
254 }
255
257 std::lock_guard<std::mutex> lock(_mutex);
258 if (_gameStartSent) {
259 return false;
260 }
261 _gameStartSent = true;
262 return true;
263 }
264
265 void Room::broadcastChatMessage(uint32_t senderId, const std::string &senderName,
266 const std::string &message) {
267 // Note: The actual broadcasting will be done by the Server
268 // This method is called by Server::_handleChatMessage
269 // We just log it here for room tracking
270 LOG_INFO("[Room ", _id, "] Chat from ", senderName, " (", senderId, "): ", message);
271 }
272
273} // namespace server
#define LOG_INFO(...)
Definition Logger.hpp:181
#define LOG_ERROR(...)
Definition Logger.hpp:183
#define LOG_WARNING(...)
Definition Logger.hpp:182
Interface for server-side game logic orchestration.
RoomInfo getInfo() const override
Get room information.
Definition Room.cpp:161
bool leave(uint32_t playerId) override
Remove a player from the room.
Definition Room.cpp:93
void broadcastChatMessage(uint32_t senderId, const std::string &senderName, const std::string &message)
Broadcast a chat message to all players in the room.
Definition Room.cpp:265
uint32_t _hostPlayerId
Definition Room.hpp:147
bool isFull() const override
Check if room is full.
Definition Room.hpp:51
bool tryMarkGameStartSent()
Atomically check and set GameStart sent flag (thread-safe)
Definition Room.cpp:256
std::shared_ptr< EventBus > _eventBus
Definition Room.hpp:152
std::vector< uint32_t > _spectators
Definition Room.hpp:149
bool join(uint32_t playerId) override
Join a player to the room.
Definition Room.cpp:53
std::string _id
Definition Room.hpp:141
void setState(RoomState state) override
Set the state of the room.
Definition Room.cpp:129
void requestStartGame()
Request to start the game (initiates countdown)
Definition Room.cpp:249
bool _gameStartSent
Definition Room.hpp:154
bool hasSpectator(uint32_t playerId) const override
Check if a spectator is in this room.
Definition Room.cpp:157
bool _isPrivate
Definition Room.hpp:145
std::vector< uint32_t > _players
Definition Room.hpp:148
void setHost(uint32_t playerId)
Set the host player ID.
Definition Room.cpp:242
std::string _name
Definition Room.hpp:142
std::vector< uint32_t > getPlayers() const override
Get list of player IDs in this room.
Definition Room.cpp:143
std::unique_ptr< ServerLoop > _gameLoop
Definition Room.hpp:151
size_t getPlayerCount() const override
Get number of players currently in room.
Definition Room.cpp:138
size_t _maxPlayers
Definition Room.hpp:144
RoomState _state
Definition Room.hpp:143
float _gameSpeedMultiplier
Definition Room.hpp:146
std::shared_ptr< IGameLogic > _gameLogic
Definition Room.hpp:150
bool hasPlayer(uint32_t playerId) const override
Check if a player is in this room.
Definition Room.cpp:153
Room(const std::string &id, const std::string &name="", size_t maxPlayers=4, bool isPrivate=false, float gameSpeedMultiplier=1.0f, std::shared_ptr< EventBus > eventBus=nullptr)
Construct a room.
Definition Room.cpp:21
std::mutex _mutex
Definition Room.hpp:153
std::vector< uint32_t > getSpectators() const override
Get list of spectator IDs in this room.
Definition Room.cpp:148
void update(float deltaTime)
Update the room state (called by server loop)
Definition Room.cpp:230
bool startGame()
Start the game for this room.
Definition Room.cpp:167
bool joinAsSpectator(uint32_t playerId) override
Join a player to the room as a spectator.
Definition Room.cpp:80
RoomState
State of a game room.
Definition IRoom.hpp:20
Information about a room.
Definition IRoom.hpp:31