Mana
Loading...
Searching...
No Matches
serverdialog.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "gui/serverdialog.h"
23
24#include "chatlogger.h"
25#include "client.h"
26#include "configuration.h"
27#include "gui.h"
28#include "log.h"
29
31#include "gui/okdialog.h"
32#include "gui/sdlinput.h"
33
34#include "gui/widgets/button.h"
35#include "gui/widgets/label.h"
36#include "gui/widgets/layout.h"
37#include "gui/widgets/listbox.h"
39
40#include "resources/theme.h"
41
42#include "utils/gettext.h"
43#include "utils/stringutils.h"
44
45#include <guichan/font.hpp>
46
47#include <cstdlib>
48#include <string>
49
51 mServers(servers),
52 mVersionStrings(servers->size(), VersionString(0, std::string())),
53 mParent(parent)
54{
55}
56
58{
59 return mServers->size();
60}
61
62std::string ServersListModel::getElementAt(int elementIndex)
63{
64 const ServerInfo &server = mServers->at(elementIndex);
65 std::string myServer;
66 myServer += server.hostname;
67 myServer += ":";
68 myServer += toString(server.port);
69 return myServer;
70}
71
72void ServersListModel::setVersionString(int index, const std::string &version)
73{
74 if (version.empty())
75 mVersionStrings[index] = VersionString(0, std::string());
76 else
77 {
78 int width = gui->getFont()->getWidth(version);
79 mVersionStrings[index] = VersionString(width, version);
80 }
81}
82
83class ServersListBox : public ListBox
84{
85public:
87 ListBox(model)
88 {
89 }
90
91 void draw(gcn::Graphics *graphics) override
92 {
93 if (!mListModel)
94 return;
95
96 auto *model = static_cast<ServersListModel*>(mListModel);
97
98 graphics->setFont(getFont());
99
100 const int height = getRowHeight();
101
102 // Draw filled rectangle around the selected list element
103 if (mSelected >= 0)
104 {
105 auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT);
106 highlightColor.a = gui->getTheme()->getGuiAlpha();
107 graphics->setColor(highlightColor);
108 graphics->fillRectangle(gcn::Rectangle(0, height * mSelected,
109 getWidth(), height));
110 }
111
112 // Draw the list elements
113 for (int i = 0, y = 0; i < model->getNumberOfElements();
114 ++i, y += height)
115 {
116 const ServerInfo &info = model->getServer(i);
117
118 if (mSelected == i)
120 else
122
123 if (!info.name.empty())
124 {
125 graphics->setFont(boldFont);
126 graphics->drawText(info.name, 2, y);
127 }
128
129 graphics->setFont(getFont());
130
131 int top = y + height / 2;
132
133 graphics->drawText(model->getElementAt(i), 2, top);
134
135 if (info.version.first > 0)
136 {
138 unsupportedColor.a = gui->getTheme()->getGuiAlpha();
139 graphics->setColor(unsupportedColor);
140 graphics->drawText(info.version.second,
141 getWidth() - info.version.first - 2, top);
142 }
143 }
144 }
145
146 unsigned int getRowHeight() const override
147 {
148 return 2 * getFont()->getHeight();
149 }
150};
151
152
153ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
154 Window(_("Choose Your Server")),
155 mDir(dir),
156 mServerInfo(serverInfo)
157{
158 setWindowName("ServerDialog");
159
161
162 mServersListModel = std::make_unique<ServersListModel>(&mServers, this);
164
165 auto *usedScroll = new ScrollArea(mServersList);
166 usedScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
167
168 mDescription = new Label(std::string());
169 mDownloadText = new Label(std::string());
170
171 mQuitButton = new Button(_("Quit"), "quit", this);
172 mConnectButton = new Button(_("Connect"), "connect", this);
173 mManualEntryButton = new Button(_("Add custom Server..."), "addEntry", this);
174 mModifyButton = new Button(_("Modify..."), "modify", this);
175 mDeleteButton = new Button(_("Delete"), "remove", this);
176
177 mServersList->setActionEventId("connect");
178 mServersList->addSelectionListener(this);
179 usedScroll->setVerticalScrollAmount(0);
180
181 place(0, 0, usedScroll, 6, 5).setPadding(3);
182 place(0, 5, mDescription, 6);
183 place(0, 6, mDownloadText, 6);
185 place(1, 7, mModifyButton);
186 place(2, 7, mDeleteButton);
187 place(4, 7, mQuitButton);
188 place(5, 7, mConnectButton);
189
190 // Make sure the list has enough height
191 getLayout().setRowHeight(3, 80);
192
193 // Do this manually instead of calling reflowLayout so we can enforce a
194 // minimum width.
195 int width = 0, height = 0;
196 getLayout().reflow(width, height);
197 if (width < 400)
198 {
199 width = 400;
200 getLayout().reflow(width, height);
201 }
202
203 setContentSize(width, height);
204
205 setMinWidth(getWidth());
206 setMinHeight(getHeight());
207 setDefaultSize(getWidth(), getHeight(), WindowAlignment::Center);
208
209 setResizable(true);
210 addKeyListener(this);
211
213
214 mServersList->setSelected(0); // Do this after for the Delete button
215
216 setVisible(true);
217
218 mServersList->requestFocus();
219
221}
222
224
225void ServerDialog::action(const gcn::ActionEvent &event)
226{
227 if (event.getId() == "ok")
228 {
229 // Give focus back to the server dialog.
230 mServersList->requestFocus();
231 }
232 else if (event.getId() == "connect")
233 {
234 int index = mServersList->getSelected();
235
236 // Check login
237 if (index < 0
238#ifndef MANASERV_SUPPORT
239 || mServersListModel->getServer(index).type == ServerType::ManaServ
240#endif
241 )
242 {
243 auto *dlg = new OkDialog(_("Error"),
244 _("Please select a valid server."));
245 dlg->addActionListener(this);
246 }
247 else
248 {
249 mDownload->cancel();
250
251 mQuitButton->setEnabled(false);
252 mConnectButton->setEnabled(false);
253 mDeleteButton->setEnabled(false);
254 mManualEntryButton->setEnabled(false);
255 mModifyButton->setEnabled(false);
256
257 const ServerInfo &serverInfo = mServersListModel->getServer(index);
258 mServerInfo->hostname = serverInfo.hostname;
259 mServerInfo->name = serverInfo.name;
260 mServerInfo->port = serverInfo.port;
261 mServerInfo->type = serverInfo.type;
262
263 // Save the selected server
264 mServerInfo->save = true;
266
268
270 }
271 }
272 else if (event.getId() == "quit")
273 {
274 mDownload->cancel();
276 }
277 else if (event.getId() == "addEntry")
278 {
279 // Add a custom server: It will delete itself using guichan logic.
280 new CustomServerDialog(this);
281 }
282 else if (event.getId() == "modify")
283 {
284 int index = mServersList->getSelected();
285 // Check whether a server is selected.
286 if (index < 0)
287 {
288 auto *dlg = new OkDialog(_("Error"),
289 _("Please select a custom server."));
290 dlg->addActionListener(this);
291 }
292 else
293 {
294 new CustomServerDialog(this, index);
295 }
296 }
297 else if (event.getId() == "remove")
298 {
299 int index = mServersList->getSelected();
300 mServers.erase(mServers.begin() + index);
301 mServersList->setSelected(0);
302
304 }
305}
306
307void ServerDialog::keyPressed(gcn::KeyEvent &keyEvent)
308{
309 gcn::Key key = keyEvent.getKey();
310
311 if (key.getValue() == Key::ESCAPE)
312 {
314 }
315 else if (key.getValue() == Key::ENTER)
316 {
317 action(gcn::ActionEvent(nullptr, mConnectButton->getActionEventId()));
318 }
319}
320
321void ServerDialog::valueChanged(const gcn::SelectionEvent &)
322{
323 const int index = mServersList->getSelected();
324 if (index == -1)
325 {
326 mDeleteButton->setEnabled(false);
327 mModifyButton->setEnabled(false);
328 return;
329 }
330
331 // Update the server and post fields according to the new selection
332 const ServerInfo &myServer = mServersListModel->getServer(index);
333 mDescription->setCaption(myServer.description);
334 mDeleteButton->setEnabled(myServer.save);
335 mModifyButton->setEnabled(myServer.save);
336}
337
338void ServerDialog::mouseClicked(gcn::MouseEvent &mouseEvent)
339{
340 if (mouseEvent.getSource() == mServersList &&
341 isDoubleClick(mServersList->getSelected()))
342 {
343 action(gcn::ActionEvent(mConnectButton,
344 mConnectButton->getActionEventId()));
345 }
346}
347
349{
350 Window::logic();
351
352 if (mDownloadDone)
353 return;
354
355 auto state = mDownload->getState();
356
357 switch (state.status) {
359 mDownloadText->setCaption(strprintf(_("Downloading server list..."
360 "%2.0f%%"),
361 state.progress * 100));
362 break;
363
366 mDownloadDone = true;
367 Log::info("Error retrieving server list: %s", mDownload->getError());
368 mDownloadText->setCaption(_("Error retrieving server list!"));
369 break;
370
372 mDownloadDone = true;
373 loadServers();
374
375 if (mServers.empty())
376 {
377 mDownloadText->setCaption(_("No servers found!"));
378 }
379 else
380 {
381 mDownloadText->setCaption(std::string());
382 mDescription->setCaption(mServers[0].description);
383 }
384 break;
385 }
386}
387
389{
390 // Try to load the configuration value for the onlineServerList
391 std::string listFile = branding.getStringValue("onlineServerList");
392
393 if (listFile.empty())
394 listFile = config.onlineServerList;
395
396 // Fall back to manasource.org when neither branding nor config set it
397 if (listFile.empty())
398 listFile = "https://www.manasource.org/serverlist.xml";
399
400 mDownload = std::make_unique<Net::Download>(listFile);
401 mDownload->setFile(mDir + "/serverlist.xml");
402 mDownload->start();
403}
404
406{
407 XML::Document doc(mDir + "/serverlist.xml", false);
408 XML::Node rootNode = doc.rootNode();
409
410 if (!rootNode || rootNode.name() != "serverlist")
411 {
412 Log::info("Error loading server list!");
413 return;
414 }
415
416 int version = rootNode.getProperty("version", 0);
417 if (version != 1)
418 {
419 Log::error("Unsupported online server list version: %d", version);
420 return;
421 }
422
423 for (auto serverNode : rootNode.children())
424 {
425 if (serverNode.name() == "server")
426 loadServer(serverNode);
427 }
428}
429
431{
432 ServerInfo server;
433
434 std::string type = serverNode.getProperty("type", "unknown");
435
436 server.type = ServerInfo::parseType(type);
437
438 // Ignore unknown server types
439 if (server.type == ServerType::Unknown
440#ifndef MANASERV_SUPPORT
441 || server.type == ServerType::ManaServ
442#endif
443 )
444 {
445 Log::info("Ignoring server entry with unknown type: %s",
446 type.c_str());
447 return;
448 }
449
450 server.name = serverNode.getProperty("name", std::string());
451
452 std::string version = serverNode.getProperty("minimumVersion",
453 std::string());
454
455 bool meetsMinimumVersion = strcmp(version.c_str(), PACKAGE_VERSION) <= 0;
456
457 // For display in the list
458 if (meetsMinimumVersion)
459 version.clear();
460 else if (version.empty())
461 version = _("requires a newer version");
462 else
463 version = strprintf(_("requires v%s"), version.c_str());
464
465 for (auto subNode : serverNode.children())
466 {
467 if (subNode.name() == "connection")
468 {
469 server.hostname = subNode.getProperty("hostname", std::string());
470 server.port = subNode.getProperty("port", 0);
471 if (server.port == 0)
472 {
473 // If no port is given, use the default for the given type
475 }
476 }
477 else if (subNode.name() == "description")
478 {
479 server.description = subNode.textContent();
480 }
481 else if (subNode.name() == "persistentIp")
482 {
483 const auto text = subNode.textContent();
484 server.persistentIp = text == "1" || text == "true";
485 }
486 }
487
488 server.version.first = gui->getFont()->getWidth(version);
489 server.version.second = version;
490
491 // Add the server to the local list if it's not already present
492 bool found = false;
493 int i = 0;
494 for (auto &s : mServers)
495 {
496 if (s == server)
497 {
498 // Use the name listed in the server list
499 s.name = server.name;
500 s.version = server.version;
501 s.description = server.description;
502 mServersListModel->setVersionString(i, version);
503 found = true;
504 break;
505 }
506 ++i;
507 }
508
509 if (!found)
510 mServers.push_back(server);
511}
512
514{
515 for (auto &server : config.servers)
516 if (server.isValid())
517 mServers.push_back(server);
518}
519
520void ServerDialog::saveCustomServers(const ServerInfo &currentServer, int index)
521{
522 // Make sure the current server is mentioned first
523 if (currentServer.isValid())
524 {
525 if (index > -1)
526 {
527 mServers[index] = currentServer;
528 }
529 else
530 {
531 for (auto it = mServers.begin(), it_end = mServers.end(); it != it_end; ++it)
532 {
533 if (*it == currentServer)
534 {
535 mServers.erase(it);
536 break;
537 }
538 }
539 mServers.push_front(currentServer);
540 }
541 }
542
544
545 // Restore the correct description
546 if (index < 0)
547 index = 0;
548 if (static_cast<size_t>(index) < mServers.size())
549 mDescription->setCaption(mServers[index].description);
550}
ChatLogger * chatLogger
Chat log object.
Definition client.cpp:100
Button widget.
Definition button.h:38
void setServerName(const std::string &serverName)
static void setState(State state)
Definition client.h:169
std::string getStringValue(const std::string &key) const
void drawText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, const gcn::Color &color, gcn::Font *font, bool outline=false, bool shadow=false, const std::optional< gcn::Color > &outlineColor={}, const std::optional< gcn::Color > &shadowColor={})
Definition graphics.cpp:176
void setColor(const gcn::Color &color) override
Definition graphics.h:255
gcn::Font * getFont() const
Return game font.
Definition gui.h:107
Theme * getTheme() const
The global GUI theme.
Definition gui.h:138
Label widget.
Definition label.h:34
void setRowHeight(int n, int h)
Definition layout.h:221
LayoutCell & setPadding(int p)
Sets the padding around the cell content.
Definition layout.h:179
void reflow(int &nW, int &nH)
Sets the positions of all the widgets.
Definition layout.cpp:321
A list box, meant to be used inside a scroll area.
Definition listbox.h:36
An 'Ok' button dialog.
Definition okdialog.h:34
A scroll area.
Definition scrollarea.h:38
The server choice dialog.
void valueChanged(const gcn::SelectionEvent &event) override
Called when the selected value changed in the servers list box.
Button * mQuitButton
void saveCustomServers(const ServerInfo &currentServer=ServerInfo(), int index=-1)
Saves the new server entry in the custom server list.
const std::string & mDir
Button * mConnectButton
Label * mDescription
Button * mModifyButton
void downloadServerList()
Called to load a list of available server from an online xml file.
void keyPressed(gcn::KeyEvent &keyEvent) override
ServerInfos mServers
ServerDialog(ServerInfo *serverInfo, const std::string &dir)
void loadServer(XML::Node serverNode)
void action(const gcn::ActionEvent &event) override
Called when receiving actions from the widgets.
Label * mDownloadText
void loadCustomServers()
Button * mManualEntryButton
friend class CustomServerDialog
void logic() override
ListBox * mServersList
Button * mDeleteButton
std::unique_ptr< ServersListModel > mServersListModel
void mouseClicked(gcn::MouseEvent &mouseEvent) override
~ServerDialog() override
ServerInfo * mServerInfo
std::unique_ptr< Net::Download > mDownload
std::string hostname
Definition serverinfo.h:42
static ServerType parseType(const std::string &type)
Definition serverinfo.h:73
uint16_t port
Definition serverinfo.h:43
bool isValid() const
Definition serverinfo.h:51
VersionString version
Definition serverinfo.h:46
ServerType type
Definition serverinfo.h:40
bool persistentIp
Definition serverinfo.h:49
std::string name
Definition serverinfo.h:41
static uint16_t defaultPortForServerType(ServerType type)
Definition serverinfo.h:85
std::string description
Definition serverinfo.h:45
unsigned int getRowHeight() const override
void draw(gcn::Graphics *graphics) override
ServersListBox(ServersListModel *model)
Server and Port List Model.
std::pair< int, std::string > VersionString
ServerInfos * mServers
std::string getElementAt(int elementIndex) override
Used to get an element from the list.
int getNumberOfElements() override
Used to get number of line in the list.
std::vector< VersionString > mVersionStrings
ServersListModel(ServerInfos *servers, ServerDialog *parent)
void setVersionString(int index, const std::string &version)
int getGuiAlpha() const
Get the current GUI alpha value.
Definition theme.h:321
static const gcn::Color & getThemeColor(int type)
Gets the color associated with the type in the default palette (0).
Definition theme.cpp:313
@ HIGHLIGHT_TEXT
Definition theme.h:232
@ HIGHLIGHT
Definition theme.h:231
@ SERVER_VERSION_NOT_SUPPORTED
Definition theme.h:264
@ TEXT
Definition theme.h:214
A window.
Definition window.h:59
virtual void setVisible(bool visible)
Overloads window setVisible by Guichan to allow sticky window handling.
Definition window.cpp:282
void setMinHeight(int height)
Sets the minimum height of the window.
Definition window.cpp:197
void setContentSize(int width, int height)
Sets the size of this window.
Definition window.cpp:151
Layout & getLayout()
Gets the layout handler for this window.
Definition window.cpp:714
void setWindowName(const std::string &name)
Sets the name of the window.
Definition window.h:272
void setResizable(bool resize)
Sets whether or not the window can be resized.
Definition window.cpp:212
LayoutCell & place(int x, int y, gcn::Widget *, int w=1, int h=1)
Adds a widget to the window and sets it at given cell.
Definition window.cpp:737
void setDefaultSize()
Set the default win pos and size to the current ones.
Definition window.cpp:545
void loadWindowState()
Reads the position (and the size for resizable windows) in the configuration based on the given strin...
Definition window.cpp:467
void setMinWidth(int width)
Sets the minimum width of the window.
Definition window.cpp:192
A helper class for parsing an XML document, which also cleans it up again (RAII).
Definition xml.h:190
Node rootNode() const
Returns the root node of the document (or NULL if there was a load error).
Definition xml.h:213
std::string_view name() const
Definition xml.h:46
int getProperty(const char *name, int def) const
Definition xml.h:144
Children children() const
Definition xml.h:97
Config config
Global settings (config.xml)
Definition client.cpp:97
Graphics * graphics
Definition client.cpp:104
bool isDoubleClick(int selected)
Returns whether this call and the last call were done for the same selected index and within a short ...
Definition client.cpp:126
Configuration branding
XML branding information reader.
Definition client.cpp:98
@ STATE_CONNECT_SERVER
Definition client.h:63
@ STATE_EXIT
Definition client.h:94
@ STATE_FORCE_QUIT
Definition client.h:95
#define _(s)
Definition gettext.h:38
Gui * gui
The GUI system.
Definition gui.cpp:50
gcn::Font * boldFont
Bolded text font.
Definition gui.cpp:54
@ ENTER
Definition sdlinput.h:78
@ ESCAPE
Definition sdlinput.h:96
void info(const char *log_text,...) LOG_PRINTF_ATTR
void error(const char *log_text,...) LOG_PRINTF_ATTR
std::deque< ServerInfo > ServerInfos
Definition serverinfo.h:109
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
std::string toString(const T &arg)
Converts the given value to a string using std::stringstream.
Definition stringutils.h:68
ServerInfos servers
std::string onlineServerList