R-Type
Distributed multiplayer game engine in C++
Loading...
Searching...
No Matches
AddServerMenu.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** r-type
4** File description:
5** AddServerMenu - Dialog for adding a new server to the list
6*/
7
9#include <cstring>
10#include <regex>
11#include <sstream>
12#include "../common/Logger/Logger.hpp"
13
14namespace Game {
16 : BaseMenu(uiFactory), _graphics(graphics) {}
17
19 std::function<void(const std::string &, const std::string &, const std::string &)> onAdd) {
20 _onAdd = std::move(onAdd);
21 }
22
23 void AddServerMenu::SetOnCancel(std::function<void()> onCancel) {
24 _onCancel = std::move(onCancel);
25 }
26
28 if (!_menu) {
29 return;
30 }
31
32 const float inputWidth = 350.0f;
33 const float inputHeight = 40.0f;
34 const float spacing = 20.0f;
35 const float buttonWidth = 140.0f;
36 const float buttonHeight = 45.0f;
37 const float buttonSpacing = 20.0f;
38
39 const float startY = 150.0f; // Start from top with margin
40
41 // Clear menu and create text inputs
42 _menu->Clear();
43
44 // --- Server Name Input ---
45 _nameInput = CreateInput(inputWidth, inputHeight, startY, "Server name (e.g., My Server)", 30,
46 "[a-zA-Z0-9_ -]+", "Name");
47
48 // --- IP Input ---
49 float ipY = startY + inputHeight + spacing;
50 _ipInput =
51 CreateInput(inputWidth, inputHeight, ipY, "IP Address (e.g., 127.0.0.1)", 15, "[0-9.]+", "IP");
52
53 // --- Port Input ---
54 float portY = ipY + inputHeight + spacing;
55 _portInput = CreateInput(inputWidth, inputHeight, portY, "Port (e.g., 4242)", 5, "[0-9]+", "Port");
56
57 // --- Buttons (Add and Cancel) ---
58 _buttonsY = portY + inputHeight + spacing * 2;
59 _buttonHeight = buttonHeight;
60 float totalButtonWidth = buttonWidth * 2 + buttonSpacing;
61 float addButtonX = -(totalButtonWidth / 2.0f);
62 float cancelButtonX = addButtonX + buttonWidth + buttonSpacing;
63
64 // Helper to create buttons with horizontal alignment
65 auto createHorizontalButton = [&](const char *label, float posX, unsigned int backgroundColor,
66 unsigned int hoverColor, std::function<void()> callback) {
67 auto button = _uiFactory.CreateButton();
68 button->SetSize(buttonWidth, buttonHeight);
69 button->SetAlign(UI::Align::CENTER_HORIZONTAL);
70 button->ApplyAlignment();
71
72 float bx, by;
73 button->GetPosition(bx, by);
74 button->SetPosition(bx + posX + (totalButtonWidth / 2.0f) - (buttonWidth / 2.0f), _buttonsY);
75
76 button->SetBackgroundColor(backgroundColor);
77 button->SetHoverColor(hoverColor);
78 button->SetText(label);
79 button->SetTextSize(18);
80 button->SetTextColor(0xFFFFFFFF);
81 button->SetFont(-1);
82 button->SetCallback(WrapWithClickSound(std::move(callback)));
83 return button;
84 };
85
86 _menu->AddButton(
87 createHorizontalButton("ADD", addButtonX, 0xFF4CAF50, 0xFF66BB6A, [this]() { OnAddClicked(); }));
88
89 _menu->AddButton(createHorizontalButton("CANCEL", cancelButtonX, 0xFF424242, 0xFF616161,
90 [this]() { OnCancelClicked(); }));
91 }
92
95 if (_nameInput) {
96 _nameInput->Update();
97 }
98 if (_ipInput) {
99 _ipInput->Update();
100 }
101 if (_portInput) {
102 _portInput->Update();
103 }
104 }
105
107 if (!_menu || !_menu->IsVisible()) {
108 return;
109 }
110
111 // Draw title
112 const char *title = "Add New Server";
113 int screenWidth = _graphics.GetScreenWidth();
114 int titleFontSize = 28;
115 int titleWidth = static_cast<int>(strlen(title) * titleFontSize * 0.5f);
116 int titleX = (screenWidth - titleWidth) / 2;
117 _graphics.DrawText(title, titleX, 80, titleFontSize, 0xFFFFFFFF);
118
119 // Render the inputs and buttons
120 if (_nameInput) {
121 _nameInput->Render();
122 }
123 if (_ipInput) {
124 _ipInput->Render();
125 }
126 if (_portInput) {
127 _portInput->Render();
128 }
129
131
132 // Render error message if present
133 if (_hasError && !_errorMessage.empty()) {
134 const int fontSize = 16;
135 const int errorMargin = 15;
136
137 const int errorY = static_cast<int>(_buttonsY + _buttonHeight + errorMargin);
138
139 int textWidth = static_cast<int>(_errorMessage.length() * fontSize * 0.6);
140 int errorX = (screenWidth - textWidth) / 2;
141
142 _graphics.DrawText(_errorMessage.c_str(), errorX, errorY, fontSize, 0xFFFF0000);
143 }
144 }
145
147 LOG_INFO("[AddServerMenu] Add button clicked");
148
149 std::string name = _nameInput ? _nameInput->GetText() : "";
150 std::string ip = _ipInput ? _ipInput->GetText() : "";
151 std::string port = _portInput ? _portInput->GetText() : "";
152
153 std::string errorMsg;
154
155 // Validate all fields
156 if (!ValidateName(name, errorMsg)) {
157 _errorMessage = errorMsg;
159 _hasError = true;
160 return;
161 }
162
163 if (!ValidateIP(ip, errorMsg)) {
164 _errorMessage = errorMsg;
166 _hasError = true;
167 return;
168 }
169
170 if (!ValidatePort(port, errorMsg)) {
171 _errorMessage = errorMsg;
173 _hasError = true;
174 return;
175 }
176
177 // All valid - trigger callback
178 LOG_INFO("[AddServerMenu] Server validated: ", name, " - ", ip, ":", port);
179 if (_onAdd) {
180 _onAdd(name, ip, port);
181 }
182
183 // Clear inputs for next time
184 ClearInputs();
185 }
186
188 LOG_INFO("[AddServerMenu] Cancel button clicked");
189
190 // Clear inputs
191 ClearInputs();
192
193 if (_onCancel) {
194 _onCancel();
195 }
196 }
197
198 std::unique_ptr<UI::ITextInput> AddServerMenu::CreateInput(float width, float height, float yPos,
199 const std::string &placeholder, int maxLength,
200 const std::string &regex,
201 const std::string &logName) {
202 auto input = _uiFactory.CreateTextInput();
203 input->SetSize(width, height);
204 input->SetAlign(UI::Align::CENTER_HORIZONTAL);
205 input->ApplyAlignment();
206
207 float x, y;
208 input->GetPosition(x, y);
209 input->SetPosition(x, yPos);
210
211 input->SetPlaceholder(placeholder);
212 input->SetMaxLength(maxLength);
213 input->SetValidationRegex(regex);
214 input->SetTextSize(18);
215 input->SetBackgroundColor(0xFF2A2A2A);
216 input->SetBorderColor(0xFF505050);
217 input->SetActiveBorderColor(0xFF4CAF50);
218 input->SetTextColor(0xFFFFFFFF);
219 input->SetPlaceholderColor(0xFF808080);
220 input->SetOnTextChanged([this, logName](const std::string &text) {
221 LOG_INFO("[AddServerMenu] " + logName + ": " + text);
222 ClearError();
223 });
224
225 return input;
226 }
227
229 if (_nameInput)
230 _nameInput->SetText("");
231 if (_ipInput)
232 _ipInput->SetText("");
233 if (_portInput)
234 _portInput->SetText("");
235 ClearError();
236 }
237
238 bool AddServerMenu::ValidateName(const std::string &name, std::string &errorMsg) {
239 if (name.empty()) {
240 errorMsg = "Server name cannot be empty";
241 return false;
242 }
243 if (name.length() < 2) {
244 errorMsg = "Server name must be at least 2 characters";
245 return false;
246 }
247 return true;
248 }
249
250 bool AddServerMenu::ValidateIP(const std::string &ip, std::string &errorMsg) {
251 if (ip.empty()) {
252 errorMsg = "IP address cannot be empty";
253 return false;
254 }
255
256 // Basic IP validation (simplified)
257 std::regex ipRegex(R"(^(\d{1,3}\.){3}\d{1,3}$)");
258 if (!std::regex_match(ip, ipRegex)) {
259 errorMsg = "Invalid IP address format";
260 return false;
261 }
262
263 // Check each octet is 0-255
264 std::istringstream iss(ip);
265 std::string octet;
266 while (std::getline(iss, octet, '.')) {
267 try {
268 int value = std::stoi(octet);
269 if (value < 0 || value > 255) {
270 errorMsg = "IP octets must be between 0 and 255";
271 return false;
272 }
273 } catch (...) {
274 errorMsg = "Invalid IP address";
275 return false;
276 }
277 }
278
279 return true;
280 }
281
282 bool AddServerMenu::ValidatePort(const std::string &port, std::string &errorMsg) {
283 if (port.empty()) {
284 errorMsg = "Port cannot be empty";
285 return false;
286 }
287
288 try {
289 int portNum = std::stoi(port);
290 if (portNum < 1 || portNum > 65535) {
291 errorMsg = "Port must be between 1 and 65535";
292 return false;
293 }
294 } catch (...) {
295 errorMsg = "Invalid port number";
296 return false;
297 }
298
299 return true;
300 }
301
303 _hasError = false;
304 _errorMessage.clear();
306 }
307} // namespace Game
#define LOG_INFO(...)
Definition Logger.hpp:181
std::string _errorMessage
void Update() override
Update menu state (should be called every frame).
std::unique_ptr< UI::ITextInput > CreateInput(float width, float height, float yPos, const std::string &placeholder, int maxLength, const std::string &regex, const std::string &logName)
Graphics::IGraphics & _graphics
void Render() override
Render menu (should be called every frame).
std::function< void(const std::string &, const std::string &, const std::string &)> _onAdd
AddServerMenu(UI::IUIFactory &uiFactory, Graphics::IGraphics &graphics)
bool ValidateName(const std::string &name, std::string &errorMsg)
bool ValidatePort(const std::string &port, std::string &errorMsg)
std::unique_ptr< UI::ITextInput > _nameInput
bool ValidateIP(const std::string &ip, std::string &errorMsg)
void SetOnCancel(std::function< void()> onCancel)
Set callback triggered when the Cancel button is clicked.
std::function< void()> _onCancel
void Initialize() override
Initialize menu (must be implemented by derived classes).
std::unique_ptr< UI::ITextInput > _portInput
std::unique_ptr< UI::ITextInput > _ipInput
void SetOnAdd(std::function< void(const std::string &, const std::string &, const std::string &)> onAdd)
Set callback triggered when the Add button is clicked.
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
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 GetScreenWidth() const =0
Get the screen width (same as window width)
Abstract factory interface for creating UI elements.
virtual std::unique_ptr< ITextInput > CreateTextInput()=0
Create a text input instance.
virtual std::unique_ptr< IButton > CreateButton()=0
Create a button instance.
@ CENTER_HORIZONTAL
Center on the X axis.