r-type  0.0.0
R-Type main
Loading...
Searching...
No Matches
waitingRoom.cpp
Go to the documentation of this file.
2#include <Utils/Logger.hpp>
3#include <cmath>
4
5#include "ECS/Component.hpp"
7#include "Utils/Common.hpp"
8
9static constexpr eng::Color CYAN_ELECTRIC = {0U, 191U, 255U, 255U};
10static constexpr eng::Color GRAY_BLUE_SUBTLE = {160U, 160U, 160U, 255U};
11static constexpr eng::Color TEXT_VALUE_COLOR = {200U, 200U, 255U, 255U};
12static constexpr eng::Color INFO_TEXT_COLOR = {180U, 180U, 180U, 200U};
13static constexpr eng::Color GREEN_READY = {0U, 255U, 100U, 255U};
14
15namespace gme
16{
17 WaitingRoomScene::WaitingRoomScene(const eng::id assignedId, const std::shared_ptr<eng::IRenderer> &renderer)
18 : AScene(assignedId), m_renderer(renderer)
19 {
20 auto &registry = AScene::getRegistry();
21
22 registry.onComponentAdded(
23 [&renderer, &registry](const ecs::Entity e, const std::type_info &type)
24 {
25 const auto *audioComp = registry.getComponent<ecs::Audio>(e);
26 const auto *colorComp = registry.getComponent<ecs::Color>(e);
27 const auto *fontComp = registry.getComponent<ecs::Font>(e);
28 const auto *textComp = registry.getComponent<ecs::Text>(e);
29 const auto *transform = registry.getComponent<ecs::Transform>(e);
30
31 if (type == typeid(ecs::Text))
32 {
33 if (textComp && transform && fontComp)
34 {
35 renderer->createFont(fontComp->id, fontComp->path);
36 renderer->createText(
37 {.font_name = fontComp->id,
38 .color = {.r = colorComp->r, .g = colorComp->g, .b = colorComp->b, .a = colorComp->a},
39 .content = textComp->content,
40 .size = textComp->font_size,
41 .x = transform->x,
42 .y = transform->y,
43 .name = textComp->id});
44 }
45 }
46 });
47
48 registry.createEntity()
49 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
50 .with<ecs::Transform>("transform_title", 100.F, 40.F, 0.F)
52 .with<ecs::Text>("title", std::string("WAITING ROOM"), 64U)
53 .build();
54
55 m_lobbyIdEntity = registry.createEntity()
56 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
57 .with<ecs::Transform>("transform_lobby_id", 100.F, 120.F, 0.F)
58 .with<ecs::Color>("color_lobby_id", TEXT_VALUE_COLOR.r, TEXT_VALUE_COLOR.g,
60 .with<ecs::Text>("lobby_id_text", std::string("Lobby ID"), 32U)
61 .build();
62
63 m_playerCountEntity = registry.createEntity()
64 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
65 .with<ecs::Transform>("transform_player_count", 100.F, 160.F, 0.F)
66 .with<ecs::Color>("color_player_count", TEXT_VALUE_COLOR.r, TEXT_VALUE_COLOR.g,
68 .with<ecs::Text>("player_count_text", std::string("Players 0 of 4"), 32U)
69 .build();
70
71 m_statusEntity = registry.createEntity()
72 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
73 .with<ecs::Transform>("transform_status", 100.F, 200.F, 0.F)
76 .with<ecs::Text>("status_text", std::string("Waiting for players"), 28U)
77 .build();
78
79 m_leaveButtonEntity = registry.createEntity()
80 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
81 .with<ecs::Transform>("transform_leave", 100.F, 500.F, 0.F)
82 .with<ecs::Color>("color_leave", GRAY_BLUE_SUBTLE.r, GRAY_BLUE_SUBTLE.g,
84 .with<ecs::Text>("leave_text", std::string(" Leave Lobby"), 32U)
85 .build();
86
87 m_readyButtonEntity = registry.createEntity()
88 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
89 .with<ecs::Transform>("transform_ready", 100.F, 460.F, 0.F)
90 .with<ecs::Color>("color_ready", GRAY_BLUE_SUBTLE.r, GRAY_BLUE_SUBTLE.g,
92 .with<ecs::Text>("ready_text", std::string(" Ready"), 32U)
93 .build();
94
95 m_startButtonEntity = registry.createEntity()
96 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
97 .with<ecs::Transform>("transform_start", 100.F, 420.F, 0.F)
98 .with<ecs::Color>("color_start", GRAY_BLUE_SUBTLE.r, GRAY_BLUE_SUBTLE.g,
100 .with<ecs::Text>("start_text", std::string(" Start Game"), 32U)
101 .build();
102
104 m_eventBus.registerComponent(m_eventComponentId, "Waiting_Room_Scene");
106 }
107
113
115 {
116 try
117 {
118 rnp::Serializer deserializer(event.data);
119 rnp::PacketLobbyUpdate packet = deserializer.deserializeLobbyUpdate();
120
121 utl::Logger::log("WaitingRoomScene: Received lobby update for lobby " +
122 std::to_string(packet.lobbyInfo.lobbyId) +
123 " - Players: " + std::to_string(packet.lobbyInfo.currentPlayers) + "/" +
124 std::to_string(packet.lobbyInfo.maxPlayers),
126
128 m_hasLobbyInfo = true;
130 }
131 catch (const std::exception &e)
132 {
133 utl::Logger::log("WaitingRoomScene: Error handling lobby update: " + std::string(e.what()),
135 }
136 }
137
139 {
140 try
141 {
142 rnp::Serializer deserializer(event.data);
143 rnp::PacketGameStart packet = deserializer.deserializeGameStart();
144
145 utl::Logger::log("WaitingRoomScene: Game starting for lobby " + std::to_string(packet.lobbyId),
147
148 if (onGameStart)
149 {
150 onGameStart();
151 }
152 }
153 catch (const std::exception &e)
154 {
155 utl::Logger::log("WaitingRoomScene: Error handling game start: " + std::string(e.what()),
157 }
158 }
159
160 void WaitingRoomScene::update(float dt, const eng::WindowSize & /*size*/)
161 {
163
164 m_animationTime += dt;
165 auto &registry = AScene::getRegistry();
166
167 // Update button selection visual
168 auto *leaveColor = registry.getComponent<ecs::Color>(m_leaveButtonEntity);
169 auto *readyColor = registry.getComponent<ecs::Color>(m_readyButtonEntity);
170 auto *startColor = registry.getComponent<ecs::Color>(m_startButtonEntity);
171 auto *leaveText = registry.getComponent<ecs::Text>(m_leaveButtonEntity);
172 auto *readyText = registry.getComponent<ecs::Text>(m_readyButtonEntity);
173 auto *startText = registry.getComponent<ecs::Text>(m_startButtonEntity);
174
175 if (leaveColor && readyColor && leaveText && readyText)
176 {
177 // Reset all buttons
178 leaveColor->r = GRAY_BLUE_SUBTLE.r;
179 leaveColor->g = GRAY_BLUE_SUBTLE.g;
180 leaveColor->b = GRAY_BLUE_SUBTLE.b;
181 leaveText->content = " Leave Lobby";
182
183 readyColor->r = GRAY_BLUE_SUBTLE.r;
184 readyColor->g = GRAY_BLUE_SUBTLE.g;
185 readyColor->b = GRAY_BLUE_SUBTLE.b;
186 readyText->content = " Ready";
187
188 if (m_isHost && startColor && startText)
189 {
190 startColor->r = GRAY_BLUE_SUBTLE.r;
191 startColor->g = GRAY_BLUE_SUBTLE.g;
192 startColor->b = GRAY_BLUE_SUBTLE.b;
193 startText->content = " Start Game";
194 }
195
196 // Highlight selected button
198 {
199 leaveColor->r = CYAN_ELECTRIC.r;
200 leaveColor->g = CYAN_ELECTRIC.g;
201 leaveColor->b = CYAN_ELECTRIC.b;
202 leaveText->content = " Leave Lobby";
203 }
204 else if (m_selectedButton == BUTTON_READY)
205 {
206 readyColor->r = CYAN_ELECTRIC.r;
207 readyColor->g = CYAN_ELECTRIC.g;
208 readyColor->b = CYAN_ELECTRIC.b;
209 readyText->content = " Ready";
210 }
211 else if (m_selectedButton == BUTTON_START && m_isHost && startColor && startText)
212 {
213 startColor->r = CYAN_ELECTRIC.r;
214 startColor->g = CYAN_ELECTRIC.g;
215 startColor->b = CYAN_ELECTRIC.b;
216 startText->content = " Start Game";
217 }
218 }
219
220 // Pulsing effect on status text
221 if (m_hasLobbyInfo)
222 {
223 auto *statusColor = registry.getComponent<ecs::Color>(m_statusEntity);
224 if (statusColor)
225 {
226 const float pulse = (std::sin(m_animationTime * 2.0F) + 1.0F) / 2.0F;
227 statusColor->a = static_cast<std::uint8_t>(150U + pulse * 105.0F);
228 }
229 }
230 }
231
233 {
234 switch (event.type)
235 {
237 if (event.key == eng::Key::Up)
238 {
239 if (m_isHost)
240 {
243 else if (m_selectedButton == BUTTON_READY)
245 else if (m_selectedButton == BUTTON_START)
247 }
248 else
249 {
250 // Non-host: Ready (top) -> Leave (bottom)
253 else
255 }
256 }
257 else if (event.key == eng::Key::Down)
258 {
259 if (m_isHost)
260 {
261 // Down should go: Start->Ready, Ready->Leave, Leave->Start
264 else if (m_selectedButton == BUTTON_READY)
266 else if (m_selectedButton == BUTTON_LEAVE)
268 }
269 else
270 {
271 // Non-host: Ready (top) -> Leave (bottom)
274 else
276 }
277 }
278 else if (event.key == eng::Key::Enter || event.key == eng::Key::Space)
279 {
281 {
282 leaveLobby();
283 }
284 else if (m_selectedButton == BUTTON_READY)
285 {
286 utl::Logger::log("WaitingRoomScene: Player ready", utl::LogLevel::INFO);
287 // TODO: Send ready status to server
288 }
290 {
291 startGame();
292 }
293 }
294 else if (event.key == eng::Key::Escape)
295 {
296 leaveLobby();
297 }
298 break;
299 default:
300 break;
301 }
302 }
303
304 void WaitingRoomScene::setLobbyId(std::uint32_t lobbyId)
305 {
306 m_lobbyId = lobbyId;
307 auto &registry = AScene::getRegistry();
308 auto *lobbyIdText = registry.getComponent<ecs::Text>(m_lobbyIdEntity);
309 if (lobbyIdText)
310 {
311 lobbyIdText->content = "Lobby ID " + std::to_string(lobbyId);
312 }
313 utl::Logger::log("WaitingRoomScene: Set lobby ID to " + std::to_string(lobbyId), utl::LogLevel::INFO);
314 }
315
317 {
318 m_currentLobbyInfo = lobbyInfo;
319 m_hasLobbyInfo = true;
320 m_lobbyId = lobbyInfo.lobbyId;
322 utl::Logger::log("WaitingRoomScene: Lobby info set - Players: " + std::to_string(lobbyInfo.currentPlayers) +
323 "/" + std::to_string(lobbyInfo.maxPlayers),
325 }
326
328 {
329 if (!m_hasLobbyInfo)
330 {
331 return;
332 }
333
334 auto &registry = AScene::getRegistry();
335
336 // Update lobby ID
337 auto *lobbyIdText = registry.getComponent<ecs::Text>(m_lobbyIdEntity);
338 if (lobbyIdText)
339 {
340 lobbyIdText->content = "Lobby ID " + std::to_string(m_currentLobbyInfo.lobbyId);
341 }
342
343 // Update player count
344 auto *playerCountText = registry.getComponent<ecs::Text>(m_playerCountEntity);
345 if (playerCountText)
346 {
347 playerCountText->content = "Players " + std::to_string(m_currentLobbyInfo.currentPlayers) + " of " +
348 std::to_string(m_currentLobbyInfo.maxPlayers);
349 }
350
351 // Update status
352 auto *statusText = registry.getComponent<ecs::Text>(m_statusEntity);
353 if (statusText)
354 {
356 {
357 statusText->content = "Lobby is full Waiting for host to start";
358 }
359 else
360 {
361 statusText->content =
362 "Waiting for " + std::to_string(m_currentLobbyInfo.maxPlayers - m_currentLobbyInfo.currentPlayers) +
363 " more players";
364 }
365 }
366
367 // Clear old player entities
369
370 // Create player slot displays
371 float startY = 250.0F;
372 float spacing = 35.0F;
373
374 for (std::uint8_t i = 0; i < m_currentLobbyInfo.maxPlayers; ++i)
375 {
376 std::string playerText;
377 eng::Color playerColor;
378
380 {
381 playerText = "Player " + std::to_string(i + 1) + " Connected";
382 playerColor = GREEN_READY;
383 }
384 else
385 {
386 playerText = "Player " + std::to_string(i + 1) + " Waiting";
387 playerColor = INFO_TEXT_COLOR;
388 }
389
390 ecs::Entity playerEntity =
391 registry.createEntity()
392 .with<ecs::Font>("main_font", utl::Path::Font::FONTS_RTYPE)
393 .with<ecs::Transform>("transform_player_" + std::to_string(i), 120.F, startY + i * spacing, 0.F)
394 .with<ecs::Color>("color_player_" + std::to_string(i), playerColor.r, playerColor.g, playerColor.b,
395 playerColor.a)
396 .with<ecs::Text>("player_" + std::to_string(i), playerText, 26U)
397 .build();
398
399 m_playerEntities.push_back(playerEntity);
400 }
401
402 utl::Logger::log("WaitingRoomScene: Updated player display - " +
403 std::to_string(m_currentLobbyInfo.currentPlayers) + "/" +
404 std::to_string(m_currentLobbyInfo.maxPlayers) + " players",
406 }
407
409 {
410 auto &registry = AScene::getRegistry();
411 for (const auto &entity : m_playerEntities)
412 {
413 if (auto *text = registry.getComponent<ecs::Text>(entity))
414 registry.removeComponent<ecs::Text>(entity);
415 if (auto *color = registry.getComponent<ecs::Color>(entity))
416 registry.removeComponent<ecs::Color>(entity);
417 if (auto *transform = registry.getComponent<ecs::Transform>(entity))
418 registry.removeComponent<ecs::Transform>(entity);
419 if (auto *font = registry.getComponent<ecs::Font>(entity))
420 registry.removeComponent<ecs::Font>(entity);
421 }
422 m_playerEntities.clear();
423 }
424
426 {
427 utl::Logger::log("WaitingRoomScene: Leaving lobby " + std::to_string(m_lobbyId), utl::LogLevel::INFO);
428
429 // Publish lobby leave event to network client
432
433 // Transition back to multi config
434 if (onLeaveLobby)
435 {
436 onLeaveLobby();
437 }
438 }
439
441 {
442 utl::Logger::log("WaitingRoomScene: Starting game for lobby " + std::to_string(m_lobbyId), utl::LogLevel::INFO);
443
444 if (!m_isHost)
445 {
446 utl::Logger::log("WaitingRoomScene: Only host can start game", utl::LogLevel::WARNING);
447 return;
448 }
449
450 std::vector<std::uint8_t> packetData;
451 packetData.resize(sizeof(std::uint32_t));
452 std::memcpy(packetData.data(), &m_lobbyId, sizeof(std::uint32_t));
453
455 }
456
458 {
459 std::vector<utl::Event> events = m_eventBus.consumeForTarget(m_eventComponentId);
460 for (const auto &event : events)
461 {
462 switch (event.type)
463 {
466 break;
468 utl::Logger::log("WaitingRoomScene: Received GAME_START event", utl::LogLevel::INFO);
469 if (onGameStart)
470 {
471 onGameStart();
472 }
473 break;
474 default:
475 break;
476 }
477 }
478 }
479
480} // namespace gme
This file contains the component definitions.
This file contains the Logger class.
Network packet serializer for RNP protocol.
Waiting room scene for R-Type multiplayer lobby system.
utl::EventBus & m_eventBus
Definition IScene.hpp:84
std::uint32_t m_eventComponentId
Definition IScene.hpp:85
void event(const eng::Event &event) override
Handle input events.
bool m_hasLobbyInfo
Whether lobby info has been received.
void processEventBus()
Process events from event bus.
void startGame()
Initiate game start sequence.
int m_selectedButton
Currently selected button index.
static constexpr int BUTTON_START
Start game button index (host only)
std::uint32_t m_lobbyId
Current lobby ID.
void handleLobbyUpdate(const utl::Event &event)
Handle lobby update event from server.
void leaveLobby() const
Send leave lobby request to server.
void setupEventSubscriptions() const
Subscribe to event bus events.
bool m_isHost
Whether local player is lobby host.
static constexpr int BUTTON_LEAVE
Leave lobby button index.
void setLobbyInfo(const rnp::LobbyInfo &lobbyInfo)
Update lobby information.
void update(float dt, const eng::WindowSize &size) override
Update the waiting room scene (called each frame)
std::function< void()> onGameStart
Callback invoked when game starts.
ecs::Entity m_readyButtonEntity
Ready button entity.
void handleGameStart(const utl::Event &event) const
Handle game start event from server.
ecs::Entity m_leaveButtonEntity
Leave button entity.
ecs::Entity m_playerCountEntity
Entity displaying player count.
void updatePlayerDisplay()
Update the player list display.
void setLobbyId(std::uint32_t lobbyId)
Set the lobby ID.
WaitingRoomScene(eng::id assignedId, const std::shared_ptr< eng::IRenderer > &renderer)
Constructor.
std::vector< ecs::Entity > m_playerEntities
Entities for player name displays.
ecs::Entity m_lobbyIdEntity
Entity displaying lobby ID.
std::function< void()> onLeaveLobby
Callback invoked when player leaves lobby.
float m_animationTime
Timer for UI animations.
ecs::Entity m_startButtonEntity
Start game button entity (host only)
static constexpr int BUTTON_READY
Ready button index.
void clearPlayerEntities()
Clear all player display entities.
rnp::LobbyInfo m_currentLobbyInfo
Current lobby state.
ecs::Entity m_statusEntity
Entity displaying lobby status.
Binary serializer for RNP protocol packets.
PacketLobbyUpdate deserializeLobbyUpdate()
Deserialize LOBBY_UPDATE packet.
PacketGameStart deserializeGameStart()
Deserialize GAME_START packet.
bool publish(const Event &event)
Publish an event to the bus.
Definition EventBus.hpp:118
std::vector< Event > consumeForTarget(std::uint32_t targetId, std::uint32_t maxEvents=100)
Consume events targeted to specific component.
Definition EventBus.hpp:357
void registerComponent(std::uint32_t componentId, const std::string &name)
Register component name for better debugging.
Definition EventBus.hpp:423
void subscribe(std::uint32_t componentId, EventType type)
Subscribe component to specific event types.
Definition EventBus.hpp:384
Event structure for inter-component communication.
Definition Event.hpp:89
static void log(const std::string &message, const LogLevel &logLevel)
Definition Logger.hpp:51
This file contains common definitions and constants.
std::uint32_t Entity
Definition Entity.hpp:13
unsigned int id
Definition IScene.hpp:20
constexpr auto FONTS_RTYPE
Definition Common.hpp:97
static constexpr std::uint32_t NETWORK_CLIENT
Definition Event.hpp:17
unsigned char a
Definition Component.hpp:29
unsigned char r
Definition Component.hpp:26
unsigned char g
Definition Component.hpp:27
unsigned char b
Definition Component.hpp:28
std::string id
Definition Component.hpp:15
std::string content
Definition Component.hpp:59
unsigned char r
Definition IRenderer.hpp:17
unsigned char a
Definition IRenderer.hpp:20
unsigned char g
Definition IRenderer.hpp:18
unsigned char b
Definition IRenderer.hpp:19
Lobby information structure.
Definition Protocol.hpp:237
std::uint32_t lobbyId
Definition Protocol.hpp:238
std::uint8_t currentPlayers
Definition Protocol.hpp:240
std::uint8_t maxPlayers
Definition Protocol.hpp:241
GAME_START packet payload.
Definition Protocol.hpp:309
std::uint32_t lobbyId
Definition Protocol.hpp:310
LOBBY_UPDATE packet payload.
Definition Protocol.hpp:301
static constexpr eng::Color INFO_TEXT_COLOR
static constexpr eng::Color CYAN_ELECTRIC
static constexpr eng::Color GREEN_READY
static constexpr eng::Color TEXT_VALUE_COLOR
static constexpr eng::Color GRAY_BLUE_SUBTLE