R-Type
Distributed multiplayer game engine in C++
Loading...
Searching...
No Matches
ServerListMenu.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** r-type
4** File description:
5** ServerListMenu - Menu for selecting a server from saved list
6*/
7
9#include <cstring>
10#include <sstream>
11#include "../common/Logger/Logger.hpp"
12
13namespace Game {
15 : BaseMenu(uiFactory), _graphics(graphics) {
16 // Add default local server
17 _servers.emplace_back("Local Server", "127.0.0.1", 4242);
18 }
19
21 std::function<void(const std::string &, uint16_t)> onServerSelected) {
22 _onServerSelected = std::move(onServerSelected);
23 }
24
25 void ServerListMenu::SetOnAddServer(std::function<void()> onAddServer) {
26 _onAddServer = std::move(onAddServer);
27 }
28
29 void ServerListMenu::SetOnBack(std::function<void()> onBack) {
30 _onBack = std::move(onBack);
31 }
32
33 void ServerListMenu::AddServer(const std::string &name, const std::string &ip, uint16_t port) {
34 if (_servers.size() >= MAX_SERVERS) {
35 LOG_WARNING("[ServerListMenu] Cannot add server: maximum limit reached (", MAX_SERVERS, ")");
36 return;
37 }
38 _servers.emplace_back(name, ip, port);
41 }
42
43 void ServerListMenu::RemoveServer(size_t index) {
44 if (index >= _servers.size()) {
45 LOG_ERROR("[ServerListMenu] Cannot remove server: invalid index ", index);
46 return;
47 }
48
49 const auto &server = _servers[index];
50 LOG_INFO("[ServerListMenu] Removing server: ", server.name, " (", server.ip, ":", server.port, ")");
51
52 _servers.erase(_servers.begin() + static_cast<long>(index));
55 }
56
58 if (!_menu) {
59 return;
60 }
61
62 _menu->Clear();
63 _serverButtons.clear();
64
66
67 // Add "Add Server" button at bottom center
68 const float buttonWidth = 200.0f;
69 const float buttonHeight = 45.0f;
70 const float bottomMargin = 120.0f;
71
72 auto addServerPtr = _uiFactory.CreateButton();
73 addServerPtr->SetSize(buttonWidth, buttonHeight);
74 addServerPtr->SetAlign(UI::Align::CENTER_HORIZONTAL);
75 addServerPtr->ApplyAlignment();
76
77 float x, y;
78 addServerPtr->GetPosition(x, y);
79 float screenHeight = static_cast<float>(_graphics.GetScreenHeight());
80 addServerPtr->SetPosition(x, screenHeight - bottomMargin);
81
82 addServerPtr->SetText("Add Server");
83 addServerPtr->SetBackgroundColor(0xFF4CAF50); // Green
84 addServerPtr->SetHoverColor(0xFF66BB6A);
85 addServerPtr->SetTextColor(0xFFFFFFFF);
86 addServerPtr->SetCallback(WrapWithClickSound([this]() { OnAddServerClicked(); }));
87
88 _addServerButton = std::move(addServerPtr);
89 _menu->AddButton(_addServerButton);
90
91 // Add "Exit" button at bottom left
92 const float exitMargin = 20.0f;
93 auto exitPtr = _uiFactory.CreateButton();
94 exitPtr->SetSize(150.0f, 45.0f);
95 exitPtr->SetPosition(exitMargin, screenHeight - bottomMargin);
96 exitPtr->SetText("Exit");
97 exitPtr->SetBackgroundColor(0xFFF44336); // Red
98 exitPtr->SetHoverColor(0xFFE57373);
99 exitPtr->SetTextColor(0xFFFFFFFF);
100 exitPtr->SetCallback(WrapWithClickSound([this]() { OnBackClicked(); }));
101
102 _backButton = std::move(exitPtr);
103 _menu->AddButton(_backButton);
104
106 }
107
109 if (!_menu) {
110 return;
111 }
112
113 // Clear the entire menu and rebuild everything
114 _menu->Clear();
115 _serverButtons.clear();
116 _deleteButtons.clear();
117
118 // Button dimensions
119 const float deleteButtonWidth = 60.0f;
120 const float buttonSpacing = 10.0f;
121 const float serverButtonWidth = SERVER_BUTTON_WIDTH - deleteButtonWidth - buttonSpacing;
122
123 // Create buttons for each server
124 float currentY = LIST_START_Y;
125 for (size_t i = 0; i < _servers.size() && i < MAX_VISIBLE_SERVERS; ++i) {
126 const auto &server = _servers[i];
127
128 // Server button (select)
129 auto buttonPtr = _uiFactory.CreateButton();
130 buttonPtr->SetSize(serverButtonWidth, SERVER_BUTTON_HEIGHT);
131 buttonPtr->SetAlign(UI::Align::CENTER_HORIZONTAL);
132 buttonPtr->ApplyAlignment();
133
134 float x, y;
135 buttonPtr->GetPosition(x, y);
136 // Adjust position to make room for delete button
137 float adjustedX = x - (deleteButtonWidth + buttonSpacing) / 2.0f;
138 buttonPtr->SetPosition(adjustedX, currentY);
139
140 // Button text: "Server Name - IP:Port"
141 std::ostringstream oss;
142 oss << server.name << " - " << server.ip << ":" << server.port;
143 buttonPtr->SetText(oss.str());
144
145 buttonPtr->SetBackgroundColor(0xFF2196F3); // Blue
146 buttonPtr->SetHoverColor(0xFF64B5F6);
147 buttonPtr->SetTextColor(0xFFFFFFFF);
148 buttonPtr->SetTextSize(16);
149
150 size_t index = i; // Capture by value for lambda
151 buttonPtr->SetCallback(WrapWithClickSound([this, index]() { OnServerClicked(index); }));
152
153 std::shared_ptr<UI::IButton> button = std::move(buttonPtr);
154 _serverButtons.push_back(button);
155 _menu->AddButton(button);
156
157 // Delete button
158 auto deletePtr = _uiFactory.CreateButton();
159 deletePtr->SetSize(deleteButtonWidth, SERVER_BUTTON_HEIGHT);
160 float deleteX = adjustedX + serverButtonWidth + buttonSpacing;
161 deletePtr->SetPosition(deleteX, currentY);
162 deletePtr->SetText("x");
163 deletePtr->SetBackgroundColor(0xFFF44336); // Red
164 deletePtr->SetHoverColor(0xFFE57373);
165 deletePtr->SetTextColor(0xFFFFFFFF);
166 deletePtr->SetTextSize(20);
167 deletePtr->SetCallback(WrapWithClickSound([this, index]() { OnDeleteServerClicked(index); }));
168
169 std::shared_ptr<UI::IButton> deleteButton = std::move(deletePtr);
170 _deleteButtons.push_back(deleteButton);
171 _menu->AddButton(deleteButton);
172
174 }
175
176 // Re-add "Add Server" button at bottom center
177 if (_addServerButton) {
178 _menu->AddButton(_addServerButton);
179 }
180
181 // Re-add "Exit" button at bottom left
182 if (_backButton) {
183 _menu->AddButton(_backButton);
184 }
185 }
186
190
192 if (!_menu || !_menu->IsVisible()) {
193 return;
194 }
195
196 // Draw title
197 const char *title = "Select a Server";
198 int screenWidth = _graphics.GetScreenWidth();
199 int titleFontSize = 32;
200 int titleWidth = static_cast<int>(strlen(title) * titleFontSize * 0.5f);
201 int titleX = (screenWidth - titleWidth) / 2;
202 _graphics.DrawText(title, titleX, 30, titleFontSize, 0xFFFFFFFF);
203
204 // Draw connecting message if connecting
205 if (_isConnecting) {
206 std::string connectingMsg = "Connecting to " + _connectingServerName + "...";
207 int connectingFontSize = 20;
208 int connectingY = 70;
209 int connectingWidth = static_cast<int>(connectingMsg.length() * connectingFontSize * 0.5f);
210 int connectingX = (screenWidth - connectingWidth) / 2;
211 _graphics.DrawText(connectingMsg.c_str(), connectingX, connectingY, connectingFontSize,
212 0xFF00FF00); // Green
213 }
214 // Draw error message if present (and not connecting)
215 else if (_hasConnectionError && !_connectionError.empty()) {
216 int errorFontSize = 20;
217 int errorY = 70; // Below title
218 int errorWidth = static_cast<int>(_connectionError.length() * errorFontSize * 0.5f);
219 int errorX = (screenWidth - errorWidth) / 2;
220 _graphics.DrawText(_connectionError.c_str(), errorX, errorY, errorFontSize, 0xFFFF0000); // Red
221 }
222
224 }
225
227 if (index >= _servers.size()) {
228 LOG_ERROR("[ServerListMenu] Invalid server index: ", index);
229 return;
230 }
231
232 // Clear previous error
234
235 const auto &server = _servers[index];
236 LOG_INFO("[ServerListMenu] Selected server: ", server.name, " (", server.ip, ":", server.port, ")");
237
238 if (_onServerSelected) {
240 }
241 }
242
244 LOG_INFO("[ServerListMenu] Add Server button clicked");
245 if (_onAddServer) {
246 _onAddServer();
247 }
248 }
249
251 LOG_INFO("[ServerListMenu] Back button clicked");
252 if (_onBack) {
253 _onBack();
254 }
255 }
256
258 LOG_INFO("[ServerListMenu] Delete button clicked for server index: ", index);
259 RemoveServer(index);
260 }
261
263 if (!_addServerButton) {
264 return;
265 }
266
267 if (_servers.size() >= MAX_SERVERS) {
268 // Disable button when limit reached
269 _addServerButton->SetBackgroundColor(0xFF757575); // Gray
270 _addServerButton->SetHoverColor(0xFF757575); // Same gray (no hover effect)
272 []() { LOG_WARNING("[ServerListMenu] Cannot add server: maximum limit reached"); }));
273 } else {
274 // Enable button
275 _addServerButton->SetBackgroundColor(0xFF4CAF50); // Green
276 _addServerButton->SetHoverColor(0xFF66BB6A);
277 _addServerButton->SetCallback(WrapWithClickSound([this]() { OnAddServerClicked(); }));
278 }
279 }
280
281 void ServerListMenu::SetConnectionError(const std::string &errorMsg) {
282 _connectionError = errorMsg;
283 _hasConnectionError = true;
284 LOG_ERROR("[ServerListMenu] Connection error: ", errorMsg);
285 }
286
291
292 void ServerListMenu::SetConnecting(bool connecting, const std::string &serverName) {
293 _isConnecting = connecting;
294 _connectingServerName = serverName;
295 if (connecting) {
296 // Clear error when starting to connect
298 }
299 }
300} // namespace Game
#define LOG_INFO(...)
Definition Logger.hpp:181
#define LOG_ERROR(...)
Definition Logger.hpp:183
#define LOG_WARNING(...)
Definition Logger.hpp:182
Base class for all menu implementations.
Definition BaseMenu.hpp:26
virtual void Update()
Update menu state (should be called every frame).
Definition BaseMenu.cpp:16
std::shared_ptr< UI::IMenu > _menu
Definition BaseMenu.hpp:101
virtual void Render()
Render menu (should be called every frame).
Definition BaseMenu.cpp:22
std::function< void()> WrapWithClickSound(std::function< void()> callback)
Wrap a callback to play click sound before executing.
Definition BaseMenu.cpp:48
UI::IUIFactory & _uiFactory
Definition BaseMenu.hpp:100
void SetOnAddServer(std::function< void()> onAddServer)
Set callback triggered when "Add Server" button is clicked.
static constexpr float LIST_START_Y
std::function< void(const std::string &, uint16_t)> _onServerSelected
void ClearConnectionError()
Clear connection error message.
std::string _connectingServerName
std::shared_ptr< UI::IButton > _addServerButton
static constexpr int MAX_VISIBLE_SERVERS
static constexpr float SERVER_BUTTON_SPACING
static constexpr size_t MAX_SERVERS
void SetOnBack(std::function< void()> onBack)
Set callback triggered when the Back button is clicked.
void Update() override
Update menu state (should be called every frame).
std::vector< ServerInfo > _servers
std::vector< std::shared_ptr< UI::IButton > > _deleteButtons
std::vector< std::shared_ptr< UI::IButton > > _serverButtons
std::shared_ptr< UI::IButton > _backButton
void AddServer(const std::string &name, const std::string &ip, uint16_t port)
Add a server to the list.
static constexpr float SERVER_BUTTON_HEIGHT
void OnDeleteServerClicked(size_t index)
std::function< void()> _onBack
static constexpr float SERVER_BUTTON_WIDTH
void OnServerClicked(size_t index)
void SetConnectionError(const std::string &errorMsg)
Set connection error message.
void Render() override
Render menu (should be called every frame).
ServerListMenu(UI::IUIFactory &uiFactory, Graphics::IGraphics &graphics)
void SetConnecting(bool connecting, const std::string &serverName="")
Set connecting state.
void Initialize() override
Initialize menu (must be implemented by derived classes).
Graphics::IGraphics & _graphics
std::function< void()> _onAddServer
void SetOnServerSelected(std::function< void(const std::string &, uint16_t)> onServerSelected)
Set callback triggered when a server is selected.
void RemoveServer(size_t index)
Remove a server from the list.
Abstract interface for graphics rendering operations.
Definition IGraphics.hpp:32
virtual void DrawText(int fontHandle, const char *text, int x, int y, int fontSize, unsigned int color)=0
Draw text using a loaded font.
virtual int GetScreenHeight() const =0
Get the screen height (same as window height)
virtual int GetScreenWidth() const =0
Get the screen width (same as window width)
Abstract factory interface for creating UI elements.
virtual std::unique_ptr< IButton > CreateButton()=0
Create a button instance.
@ CENTER_HORIZONTAL
Center on the X axis.