FlightGear next
MPServersModel.cpp
Go to the documentation of this file.
1#include "MPServersModel.h"
2
3#include <QDebug>
4#include <QSettings>
5
8
9#include <Main/fg_props.hxx>
10#include <Main/globals.hxx>
11
12#include "LaunchConfig.hxx"
13
14const int IsCustomIndexRole = Qt::UserRole + 1;
15const int ServerNameRole = Qt::UserRole + 2;
16
18 QAbstractListModel(parent)
19{
20
21}
22
24{
25 // if we don't cancel this now, it may complete after we are gone,
26 // causing a crash when the SGCallback fires (SGCallbacks don't clean up
27 // when their subject is deleted)
28 globals->get_subsystem<FGHTTPClient>()->client()->cancelRequest(m_mpServerRequest);
29}
30
31int MPServersModel::rowCount(const QModelIndex&) const
32{
33 // if query failed, we have two item:
34 // 1) - 'no servers found'
35 // 2) - 'custom server'
36 if (m_servers.empty())
37 return 2;
38
39 return m_servers.size() + 1;
40}
41
42QVariant MPServersModel::data(const QModelIndex &index, int role) const
43{
44 const int row = index.row();
45 const int customServerRow = (m_servers.empty() ? 1 : m_servers.size());
46
47 if ((row == 0) && m_servers.empty()) {
48 if (role == Qt::DisplayRole) {
49 return tr("No servers available");
50 }
51
52 return QVariant();
53 }
54
55 if (row == customServerRow) {
56 if (role == Qt::DisplayRole) {
57 return tr("Custom server");
58 } else if (role == IsCustomIndexRole) {
59 return true;
60 }
61
62 return QVariant();
63 }
64
65 const ServerInfo& sv(m_servers.at(row));
66 if (role == Qt::DisplayRole) {
67 return tr("%1 - %2").arg(sv.name).arg(sv.location);
68 } else if (role == ServerNameRole) {
69 return sv.name;
70 } else if (role == IsCustomIndexRole) {
71 return false;
72 }
73
74 return QVariant();
75}
76
77QHash<int, QByteArray> MPServersModel::roleNames() const
78{
79 QHash<int, QByteArray> result = QAbstractListModel::roleNames();
80 result[IsCustomIndexRole] = "isCustomIndex";
81 return result;
82}
83
85{
86 if (m_mpServerRequest.get()) {
87 return; // in-progress
88 }
89
90 std::string url(fgGetString("/sim/multiplay/serverlist-url",
91 "http://liveries.flightgear.org/mpstatus/mpservers.xml"));
92
93 if (url.empty()) {
94 return;
95 }
96
97 SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true);
98 m_mpServerRequest.reset(new RemoteXMLRequest(url, targetnode));
99 m_mpServerRequest->done(this, &MPServersModel::onRefreshMPServersDone);
100 m_mpServerRequest->fail(this, &MPServersModel::onRefreshMPServersFailed);
101 globals->get_subsystem<FGHTTPClient>()->makeRequest(m_mpServerRequest);
102}
103
104void MPServersModel::onRefreshMPServersDone(simgear::HTTP::Request*)
105{
106 beginResetModel();
107 // parse the properties
108 SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true);
109 m_servers.clear();
110
111 for (int i=0; i<targetnode->nChildren(); ++i) {
112 SGPropertyNode* c = targetnode->getChild(i);
113 if (c->getNameString() != "server") {
114 continue;
115 }
116
117 if (c->getBoolValue("online") != true) {
118 // only list online servers
119 continue;
120 }
121
122 QString name = QString::fromStdString(c->getStringValue("name"));
123 QString loc = QString::fromStdString(c->getStringValue("location"));
124 QString host = QString::fromStdString(c->getStringValue("hostname"));
125 int port = c->getIntValue("port");
126 m_servers.push_back(ServerInfo(name, loc, host, port));
127 }
128 endResetModel();
129 emit validChanged();
130
132 m_mpServerRequest.clear();
133}
134
135void MPServersModel::onRefreshMPServersFailed(simgear::HTTP::Request*)
136{
137 qWarning() << "refreshing MP servers failed:" << QString::fromStdString(m_mpServerRequest->responseReason());
138 m_mpServerRequest.clear();
139 beginResetModel();
140 m_servers.clear();
141 endResetModel();
142 emit validChanged();
143 emit currentIndexChanged(m_currentIndex);
145}
146
148{
149 if (m_doRestoreMPServer) {
150 QSettings settings;
151 settings.beginGroup("mpSettings");
152 QString host = settings.value("mp-server").toString();
153 if (host == "__custom__") {
154 setCurrentIndex(m_servers.size());
155 } else {
156 // restore a built-in server
157 auto it = std::find_if(m_servers.begin(), m_servers.end(), [host](const ServerInfo& info)
158 { return (info.host == host); });
159
160 if (it != m_servers.end()) {
161 setCurrentIndex(std::distance(m_servers.begin(), it));
162 } else {
164 }
165 }
166
167 // force a refresh on the QML side, since the model may have changed
168 emit currentIndexChanged(m_currentIndex);
169 m_doRestoreMPServer = false;
170 }
171}
172
174{
175 m_doRestoreMPServer = true;
176}
177
179{
180 if (!valid()) {
181 return (m_currentIndex == 1) ? "__custom__" : "__noservers__";
182 }
183
184 if (static_cast<std::vector<MPServersModel::ServerInfo>::size_type>(m_currentIndex) == m_servers.size()) {
185 return "__custom__";
186 }
187
188 return m_servers.at(m_currentIndex).host;
189}
190
192{
193 if (!valid())
194 return 0;
195
196 return m_servers.at(m_currentIndex).port;
197}
198
200{
201 return !m_servers.empty();
202}
203
205{
206 if (m_currentIndex == currentIndex)
207 return;
208
209 m_currentIndex = currentIndex;
210 emit currentIndexChanged(m_currentIndex);
211}
212
213MPServersModel::ServerInfo::ServerInfo(QString n, QString l, QString h, int p)
214{
215 name = n;
216 location = l;
217 host = h;
218 port = p;
219}
#define p(x)
const int ServerNameRole
const int IsCustomIndexRole
#define i(x)
MPServersModel(QObject *parent=nullptr)
void validChanged()
~MPServersModel() override
QVariant data(const QModelIndex &index, int role) const override
void onRefreshMPServersDone(simgear::HTTP::Request *)
QString currentServer
QHash< int, QByteArray > roleNames() const override
void setCurrentIndex(int currentIndex)
void onRefreshMPServersFailed(simgear::HTTP::Request *)
void currentIndexChanged(int currentIndex)
void restoreMPServerSelection()
int rowCount(const QModelIndex &index) const override
const char * name
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
FGGlobals * globals
Definition globals.cxx:142
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27