35 #include <rapidjson/document.h>
40 # include <curl/curl.h>
41 # include <curl/easy.h>
44 #if defined(_MSC_VER) && defined(GetObject) // This MS Windows macro from <wingdi.h> (Windows Kit 8.1) clashes with RapidJSON
54 size_t CurlWriteFunc(
void *ptr,
size_t size,
size_t nmemb, std::string* data)
56 data->append((
char*) ptr, size * nmemb);
62 std::string serverlist_url = portal_url +
"/server-list?json=true";
63 std::string response_payload;
64 std::string response_header;
65 long response_code = 0;
67 CURL *curl = curl_easy_init();
68 curl_easy_setopt(curl, CURLOPT_URL, serverlist_url.c_str());
70 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_payload);
71 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response_header);
73 CURLcode curl_result = curl_easy_perform(curl);
74 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
76 curl_easy_cleanup(curl);
79 if (curl_result != CURLE_OK || response_code != 200)
81 Ogre::LogManager::getSingleton().stream()
82 <<
"[RoR|Multiplayer] Failed to retrieve serverlist;"
83 <<
" Error: '" << curl_easy_strerror(curl_result) <<
"'; HTTP status code: " << response_code;
86 failinfo->
title =
_LC(
"MultiplayerSelector",
"Error connecting to server :(");
95 rapidjson::Document j_data_doc;
96 j_data_doc.Parse(response_payload.c_str());
97 if (j_data_doc.HasParseError() || !j_data_doc.IsArray())
99 Ogre::LogManager::getSingleton().stream()
100 <<
"[RoR|Multiplayer] Error parsing serverlist JSON";
107 size_t num_rows = j_data_doc.GetArray().Size();
110 servers.resize(num_rows);
111 for (
size_t i = 0; i < num_rows; ++i)
113 rapidjson::Value& j_row = j_data_doc[
static_cast<rapidjson::SizeType
>(i)];
115 servers[i].display_name = j_row[
"name"].GetString();
116 servers[i].display_terrn = j_row[
"terrain-name"].GetString();
117 servers[i].net_host = j_row[
"ip"].GetString();
118 servers[i].net_port = j_row[
"port"].GetInt();
120 servers[i].has_password = j_row[
"has-password"].GetBool();
121 servers[i].display_passwd = servers[i].has_password ?
_LC(
"MultiplayerSelector",
"Yes") :
_LC(
"MultiplayerSelector",
"No");
123 servers[i].display_host =
fmt::format(
"{}:{}", j_row[
"ip"].GetString(), j_row[
"port"].GetInt());
124 servers[i].display_users =
fmt::format(
"{} / {}", j_row[
"current-users"].GetInt(), j_row[
"max-clients"].GetInt());
126 servers[i].net_version = j_row[
"version"].GetString();
127 servers[i].display_version = Ogre::StringUtil::replaceAll(j_row[
"version"].GetString(),
"RoRnet_",
"");
133 #endif // defined(USE_CURL)
137 float table_padding_y = 4.f;
138 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + table_padding_y);
139 ImGui::Text(
"%s", title);
150 int window_flags = ImGuiWindowFlags_NoCollapse;
151 ImGui::SetNextWindowSize(ImVec2(750.f, 400.f), ImGuiCond_FirstUseEver);
152 ImGui::SetNextWindowPosCenter();
153 bool keep_open =
true;
156 ImGui::BeginTabBar(
"GameSettingsTabs");
158 if (ImGui::BeginTabItem(
_LC(
"MultiplayerSelector",
"Online (click to refresh)")))
160 if (ImGui::IsItemClicked())
167 if (ImGui::BeginTabItem(
_LC(
"MultiplayerSelector",
"Direct IP")))
172 if (ImGui::BeginTabItem(
_LC(
"MultiplayerSelector",
"Settings")))
189 ImGui::PushID(
"setup");
201 ImGui::PushItemWidth(250.f);
210 ImGui::PopItemWidth();
217 ImGui::PushID(
"direct");
219 ImGui::PushItemWidth(250.f);
223 ImGui::PopItemWidth();
226 if (ImGui::Button(
_LC(
"MultiplayerSelector",
"Join")))
246 m_lock_icon = Ogre::TextureManager::getSingleton().load(
254 float spinner_size = 25.f;
255 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - spinner_size);
256 ImGui::SetCursorPosY((ImGui::GetWindowSize().
y / 2.f) - spinner_size);
264 const float table_height = ImGui::GetWindowHeight()
265 - ((2.f * ImGui::GetStyle().WindowPadding.y) + (3.f * ImGui::GetItemsLineHeightWithSpacing())
266 + ImGui::GetStyle().ItemSpacing.y);
267 ImGui::BeginChild(
"scrolling", ImVec2(0.f, table_height),
false);
269 const float table_width = ImGui::GetWindowContentRegionWidth();
270 ImGui::Columns(5,
"mp-selector-columns");
271 ImGui::SetColumnOffset(1, 0.36f * table_width);
272 ImGui::SetColumnOffset(2, 0.67f * table_width);
273 ImGui::SetColumnOffset(3, 0.74f * table_width);
274 ImGui::SetColumnOffset(4, 0.82f * table_width);
296 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
308 ImGui::Image(
reinterpret_cast<ImTextureID
>(
m_lock_icon->getHandle()), ImVec2(16, 16));
313 ImVec4 version_color = compatible ? ImVec4(0.0f, 0.9f, 0.0f, 1.0f) : ImVec4(0.9f, 0.0f, 0.0f, 1.0f);
316 ImGui::Text(
"%s", server.
display_terrn.c_str()); ImGui::NextColumn();
317 ImGui::Text(
"%s", server.
display_users.c_str()); ImGui::NextColumn();
318 ImGui::PushStyleColor(ImGuiCol_Text, version_color);
319 ImGui::Text(
"%s", server.
display_version.c_str()); ImGui::NextColumn();
320 ImGui::PopStyleColor();
321 ImGui::Text(
"%s", server.
display_host.c_str()); ImGui::NextColumn();
332 if (ImGui::Button(
_LC(
"MultiplayerSelector",
"Join"), ImVec2(200.f, 0.f)))
343 ImGui::PushItemWidth(250.f);
345 ImGui::PopItemWidth();
354 float y = (ImGui::GetWindowSize().y / 2.f) - (ImGui::GetTextLineHeight() / 2.f);
355 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - (label_size.x / 2.f));
356 ImGui::SetCursorPosY(
y);
358 y += ImGui::GetTextLineHeightWithSpacing();
363 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - (detail_size.x / 2.f));
364 ImGui::SetCursorPosY(
y);
366 y += ImGui::GetTextLineHeight();
372 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - (detail_size.x / 2.f));
373 ImGui::SetCursorPosY(
y);
381 #if defined(USE_CURL)
389 #endif // defined(USE_CURL)