From f40d09cb8f712e87691af4912f3630d92d692779 Mon Sep 17 00:00:00 2001 From: untodesu Date: Thu, 11 Dec 2025 15:14:26 +0500 Subject: Shuffle stuff around - Use the new and improved hierarchy I figured out when making Prospero chat - Re-add NSIS scripts, again from Prospero - Update most build and utility scripts with their most recent versions --- game/client/gui/play_menu.cc | 594 ------------------------------------------- 1 file changed, 594 deletions(-) delete mode 100644 game/client/gui/play_menu.cc (limited to 'game/client/gui/play_menu.cc') diff --git a/game/client/gui/play_menu.cc b/game/client/gui/play_menu.cc deleted file mode 100644 index cd7f730..0000000 --- a/game/client/gui/play_menu.cc +++ /dev/null @@ -1,594 +0,0 @@ -#include "client/pch.hh" - -#include "client/gui/play_menu.hh" - -#include "core/config/boolean.hh" - -#include "core/io/config_map.hh" - -#include "core/math/constexpr.hh" - -#include "core/utils/string.hh" - -#include "core/version.hh" - -#include "shared/protocol.hh" - -#include "client/gui/bother.hh" -#include "client/gui/gui_screen.hh" -#include "client/gui/language.hh" - -#include "client/io/glfw.hh" - -#include "client/game.hh" -#include "client/globals.hh" -#include "client/session.hh" - -constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; -constexpr static std::string_view DEFAULT_SERVER_NAME = "Voxelius Server"; -constexpr static std::string_view SERVERS_TXT = "servers.txt"; - -constexpr static std::size_t MAX_SERVER_ITEM_NAME = 18; - -enum class item_status : unsigned int { - UNKNOWN = 0x0000U, - PINGING = 0x0001U, - REACHED = 0x0002U, - FAILURE = 0x0003U, -}; - -struct ServerStatusItem final { - std::string name; - std::string password; - std::string hostname; - std::uint16_t port; - - // Things pulled from bother events - std::uint16_t num_players; - std::uint16_t max_players; - std::string motd; - std::uint16_t game_version_major; - std::uint16_t game_version_minor; - std::uint16_t game_version_patch; - - // Unique identifier that monotonically - // grows with each new server added and - // doesn't reset with each server removed - unsigned int identity; - - item_status status; -}; - -static std::string str_tab_servers; - -static std::string str_join; -static std::string str_connect; -static std::string str_add; -static std::string str_edit; -static std::string str_remove; -static std::string str_refresh; - -static std::string str_status_init; -static std::string str_status_ping; -static std::string str_status_fail; - -static std::string input_itemname; -static std::string input_hostname; -static std::string input_password; - -static unsigned int next_identity; -static std::deque servers_deque; -static ServerStatusItem* selected_server; -static bool editing_server; -static bool adding_server; -static bool needs_focus; - -static void parse_hostname(ServerStatusItem* item, const std::string& hostname) -{ - auto parts = utils::split(hostname, ":"); - - if(!parts[0].empty()) { - item->hostname = parts[0]; - } - else { - item->hostname = std::string("localhost"); - } - - if(parts.size() >= 2) { - item->port = glm::clamp(strtoul(parts[1].c_str(), nullptr, 10), 1024, UINT16_MAX); - } - else { - item->port = protocol::PORT; - } -} - -static void add_new_server(void) -{ - auto item = new ServerStatusItem(); - item->port = protocol::PORT; - item->max_players = UINT16_MAX; - item->num_players = UINT16_MAX; - item->identity = next_identity; - item->status = item_status::UNKNOWN; - item->game_version_major = 0U; - item->game_version_minor = 0U; - item->game_version_patch = 0U; - - next_identity += 1U; - - input_itemname = DEFAULT_SERVER_NAME; - input_hostname = std::string(); - input_password = std::string(); - - servers_deque.push_back(item); - selected_server = item; - editing_server = true; - adding_server = true; - needs_focus = true; -} - -static void edit_selected_server(void) -{ - input_itemname = selected_server->name; - - if(selected_server->port != protocol::PORT) { - input_hostname = std::format("{}:{}", selected_server->hostname, selected_server->port); - } - else { - input_hostname = selected_server->hostname; - } - - input_password = selected_server->password; - - editing_server = true; - needs_focus = true; -} - -static void remove_selected_server(void) -{ - gui::bother::cancel(selected_server->identity); - - for(auto it = servers_deque.cbegin(); it != servers_deque.cend(); ++it) { - if(selected_server == (*it)) { - delete selected_server; - selected_server = nullptr; - servers_deque.erase(it); - return; - } - } -} - -static void join_selected_server(void) -{ - if(!session::peer) { - session::connect(selected_server->hostname.c_str(), selected_server->port, selected_server->password.c_str()); - } -} - -static void on_glfw_key(const io::GlfwKeyEvent& event) -{ - if((event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { - if(globals::gui_screen == GUI_PLAY_MENU) { - if(editing_server) { - if(adding_server) { - remove_selected_server(); - } - else { - input_itemname.clear(); - input_hostname.clear(); - input_password.clear(); - editing_server = false; - adding_server = false; - return; - } - } - - globals::gui_screen = GUI_MAIN_MENU; - selected_server = nullptr; - return; - } - } -} - -static void on_language_set(const gui::LanguageSetEvent& event) -{ - str_tab_servers = gui::language::resolve_gui("play_menu.tab.servers"); - - str_join = gui::language::resolve_gui("play_menu.join"); - str_connect = gui::language::resolve_gui("play_menu.connect"); - str_add = gui::language::resolve_gui("play_menu.add"); - str_edit = gui::language::resolve_gui("play_menu.edit"); - str_remove = gui::language::resolve_gui("play_menu.remove"); - str_refresh = gui::language::resolve_gui("play_menu.refresh"); - - str_status_init = gui::language::resolve("play_menu.status.init"); - str_status_ping = gui::language::resolve("play_menu.status.ping"); - str_status_fail = gui::language::resolve("play_menu.status.fail"); -} - -static void on_bother_response(const gui::BotherResponseEvent& event) -{ - for(auto item : servers_deque) { - if(item->identity == event.identity) { - if(event.is_server_unreachable) { - item->num_players = UINT16_MAX; - item->max_players = UINT16_MAX; - item->motd = str_status_fail; - item->status = item_status::FAILURE; - item->game_version_major = 0U; - item->game_version_minor = 0U; - item->game_version_patch = 0U; - } - else { - item->num_players = event.num_players; - item->max_players = event.max_players; - item->motd = event.motd; - item->status = item_status::REACHED; - item->game_version_major = event.game_version_major; - item->game_version_minor = event.game_version_minor; - item->game_version_patch = event.game_version_patch; - } - - break; - } - } -} - -static void layout_server_item(ServerStatusItem* item) -{ - // Preserve the cursor at which we draw stuff - const ImVec2& cursor = ImGui::GetCursorScreenPos(); - const ImVec2& padding = ImGui::GetStyle().FramePadding; - const ImVec2& spacing = ImGui::GetStyle().ItemSpacing; - - const float item_width = ImGui::GetContentRegionAvail().x; - const float line_height = ImGui::GetTextLineHeightWithSpacing(); - const std::string sid = std::format("###play_menu.servers.{}", static_cast(item)); - if(ImGui::Selectable(sid.c_str(), (item == selected_server), 0, ImVec2(0.0, 2.0f * (line_height + padding.y + spacing.y)))) { - selected_server = item; - editing_server = false; - } - - if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - // Double clicked - join the selected server - join_selected_server(); - } - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - if(item == selected_server) { - const ImVec2 start = ImVec2(cursor.x, cursor.y); - const ImVec2 end = ImVec2(start.x + item_width, start.y + 2.0f * (line_height + padding.y + spacing.y)); - draw_list->AddRect(start, end, ImGui::GetColorU32(ImGuiCol_Text), 0.0f, 0, globals::gui_scale); - } - - const ImVec2 name_pos = ImVec2(cursor.x + padding.x + 0.5f * spacing.x, cursor.y + padding.y); - draw_list->AddText(name_pos, ImGui::GetColorU32(ImGuiCol_Text), item->name.c_str(), item->name.c_str() + item->name.size()); - - if(item->status == item_status::REACHED) { - auto stats = std::format("{}/{}", item->num_players, item->max_players); - auto stats_size = ImGui::CalcTextSize(stats.c_str(), stats.c_str() + stats.size()); - auto stats_pos = ImVec2(cursor.x + item_width - stats_size.x - padding.x, cursor.y + padding.y); - draw_list->AddText(stats_pos, ImGui::GetColorU32(ImGuiCol_TextDisabled), stats.c_str(), stats.c_str() + stats.size()); - - auto major_version_mismatch = item->game_version_major != version::major; - auto minor_version_mismatch = item->game_version_minor != version::minor; - auto patch_version_mismatch = item->game_version_patch != version::patch; - - ImU32 version_color; - - if(major_version_mismatch || minor_version_mismatch || patch_version_mismatch) { - version_color = ImGui::GetColorU32(major_version_mismatch ? ImGuiCol_PlotLinesHovered : ImGuiCol_DragDropTarget); - } - else { - version_color = ImGui::GetColorU32(ImGuiCol_PlotHistogram); - } - - ImGui::PushFont(globals::font_unscii8, 4.0f); - - std::string version_toast; - - if(item->game_version_major < 16U) { - // Pre v16.x.x servers didn't send minor and patch versions - // and also used a different versioning scheme; post v16 the - // major version became the protocol version and the semver lost the tweak part - version_toast = std::string("15.x.x"); - } - else { - version_toast = std::format("{}.{}.{}", item->game_version_major, item->game_version_minor, item->game_version_patch); - } - - auto version_size = ImGui::CalcTextSize(version_toast.c_str(), version_toast.c_str() + version_toast.size()); - auto version_pos = ImVec2(stats_pos.x - version_size.x - padding.x - 4.0f * globals::gui_scale, - cursor.y + padding.y + 0.5f * (stats_size.y - version_size.y)); - auto version_end = ImVec2(version_pos.x + version_size.x, version_pos.y + version_size.y); - - auto outline_pos = ImVec2(version_pos.x - 2U * globals::gui_scale, version_pos.y - 2U * globals::gui_scale); - auto outline_end = ImVec2(version_end.x + 2U * globals::gui_scale, version_end.y + 2U * globals::gui_scale); - auto outline_thickness = glm::max(1.0f, 0.5f * static_cast(globals::gui_scale)); - - draw_list->AddRect(outline_pos, outline_end, version_color, 0.0f, 0, outline_thickness); - draw_list->AddText(version_pos, version_color, version_toast.c_str(), version_toast.c_str() + version_toast.size()); - - ImGui::PopFont(); - } - - ImU32 motd_color = {}; - const std::string* motd_text; - - switch(item->status) { - case item_status::UNKNOWN: - motd_color = ImGui::GetColorU32(ImGuiCol_TextDisabled); - motd_text = &str_status_init; - break; - case item_status::PINGING: - motd_color = ImGui::GetColorU32(ImGuiCol_TextDisabled); - motd_text = &str_status_ping; - break; - case item_status::REACHED: - motd_color = ImGui::GetColorU32(ImGuiCol_TextDisabled); - motd_text = &item->motd; - break; - default: - motd_color = ImGui::GetColorU32(ImGuiCol_PlotLinesHovered); - motd_text = &str_status_fail; - break; - } - - const ImVec2 motd_pos = ImVec2(cursor.x + padding.x + 0.5f * spacing.x, cursor.y + padding.y + line_height); - draw_list->AddText(motd_pos, motd_color, motd_text->c_str(), motd_text->c_str() + motd_text->size()); -} - -static void layout_server_edit(ServerStatusItem* item) -{ - if(needs_focus) { - ImGui::SetKeyboardFocusHere(); - needs_focus = false; - } - - ImGui::SetNextItemWidth(-0.25f * ImGui::GetContentRegionAvail().x); - ImGui::InputText("###play_menu.servers.edit_itemname", &input_itemname); - ImGui::SameLine(); - - const bool ignore_input = utils::is_whitespace(input_itemname) || input_hostname.empty(); - - ImGui::BeginDisabled(ignore_input); - - if(ImGui::Button("OK###play_menu.servers.submit_input", ImVec2(-1.0f, 0.0f)) - || (!ignore_input && ImGui::IsKeyPressed(ImGuiKey_Enter))) { - parse_hostname(item, input_hostname); - item->password = input_password; - item->name = input_itemname.substr(0, MAX_SERVER_ITEM_NAME); - item->status = item_status::UNKNOWN; - editing_server = false; - adding_server = false; - - input_itemname.clear(); - input_hostname.clear(); - - gui::bother::cancel(item->identity); - } - - ImGui::EndDisabled(); - - ImGuiInputTextFlags hostname_flags = ImGuiInputTextFlags_CharsNoBlank; - - if(client_game::streamer_mode.get_value()) { - // Hide server hostname to avoid things like - // followers flooding the server that is streamed online - hostname_flags |= ImGuiInputTextFlags_Password; - } - - ImGui::SetNextItemWidth(-0.50f * ImGui::GetContentRegionAvail().x); - ImGui::InputText("###play_menu.servers.edit_hostname", &input_hostname, hostname_flags); - ImGui::SameLine(); - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputText("###play_menu.servers.edit_password", &input_password, ImGuiInputTextFlags_Password); -} - -static void layout_servers(void) -{ - if(ImGui::BeginListBox("###play_menu.servers.listbox", ImVec2(-1.0f, -1.0f))) { - for(ServerStatusItem* item : servers_deque) { - if(editing_server && item == selected_server) { - layout_server_edit(item); - } - else { - layout_server_item(item); - } - } - - ImGui::EndListBox(); - } -} - -static void layout_servers_buttons(void) -{ - auto avail_width = ImGui::GetContentRegionAvail().x; - - // Can only join when selected and not editing - ImGui::BeginDisabled(!selected_server || editing_server); - - if(ImGui::Button(str_join.c_str(), ImVec2(-0.50f * avail_width, 0.0f))) { - join_selected_server(); - } - - ImGui::EndDisabled(); - ImGui::SameLine(); - - // Can only connect directly when not editing anything - ImGui::BeginDisabled(editing_server); - - if(ImGui::Button(str_connect.c_str(), ImVec2(-1.00f, 0.0f))) { - globals::gui_screen = GUI_DIRECT_CONNECTION; - } - - ImGui::EndDisabled(); - - // Can only add when not editing anything - ImGui::BeginDisabled(editing_server); - - if(ImGui::Button(str_add.c_str(), ImVec2(-0.75f * avail_width, 0.0f))) { - add_new_server(); - } - - ImGui::EndDisabled(); - ImGui::SameLine(); - - // Can only edit when selected and not editing - ImGui::BeginDisabled(!selected_server || editing_server); - - if(ImGui::Button(str_edit.c_str(), ImVec2(-0.50f * avail_width, 0.0f))) { - edit_selected_server(); - } - - ImGui::EndDisabled(); - ImGui::SameLine(); - - // Can only remove when selected and not editing - ImGui::BeginDisabled(!selected_server || editing_server); - - if(ImGui::Button(str_remove.c_str(), ImVec2(-0.25f * avail_width, 0.0f))) { - remove_selected_server(); - } - - ImGui::EndDisabled(); - ImGui::SameLine(); - - if(ImGui::Button(str_refresh.c_str(), ImVec2(-1.0f, 0.0f))) { - for(ServerStatusItem* item : servers_deque) { - if(item->status != item_status::PINGING) { - if(!editing_server || item != selected_server) { - item->status = item_status::UNKNOWN; - gui::bother::cancel(item->identity); - } - } - } - } -} - -void gui::play_menu::init(void) -{ - if(auto file = PHYSFS_openRead(std::string(SERVERS_TXT).c_str())) { - auto source = std::string(PHYSFS_fileLength(file), char(0x00)); - PHYSFS_readBytes(file, source.data(), source.size()); - PHYSFS_close(file); - - auto stream = std::istringstream(source); - auto line = std::string(); - - while(std::getline(stream, line)) { - auto parts = utils::split(line, "%"); - - auto item = new ServerStatusItem(); - item->port = protocol::PORT; - item->max_players = UINT16_MAX; - item->num_players = UINT16_MAX; - item->identity = next_identity; - item->status = item_status::UNKNOWN; - item->game_version_major = version::major; - item->game_version_minor = version::minor; - item->game_version_patch = version::patch; - - next_identity += 1U; - - parse_hostname(item, parts[0]); - - if(parts.size() >= 2) { - item->password = parts[1]; - } - else { - item->password = std::string(); - } - - if(parts.size() >= 3) { - item->name = parts[2].substr(0, MAX_SERVER_ITEM_NAME); - } - else { - item->name = DEFAULT_SERVER_NAME; - } - - servers_deque.push_back(item); - } - } - - globals::dispatcher.sink().connect<&on_glfw_key>(); - globals::dispatcher.sink().connect<&on_language_set>(); - globals::dispatcher.sink().connect<&on_bother_response>(); -} - -void gui::play_menu::shutdown(void) -{ - std::ostringstream stream; - - for(const auto item : servers_deque) { - stream << std::format("{}:{}%{}%{}", item->hostname, item->port, item->password, item->name) << std::endl; - } - - if(auto file = PHYSFS_openWrite(std::string(SERVERS_TXT).c_str())) { - auto source = stream.str(); - PHYSFS_writeBytes(file, source.data(), source.size()); - PHYSFS_close(file); - } - - for(auto item : servers_deque) - delete item; - servers_deque.clear(); -} - -void gui::play_menu::layout(void) -{ - const auto viewport = ImGui::GetMainViewport(); - const auto window_start = ImVec2(viewport->Size.x * 0.05f, viewport->Size.y * 0.05f); - const auto window_size = ImVec2(viewport->Size.x * 0.90f, viewport->Size.y * 0.90f); - - ImGui::SetNextWindowPos(window_start); - ImGui::SetNextWindowSize(window_size); - - if(ImGui::Begin("###play_menu", nullptr, WINDOW_FLAGS)) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(3.0f * globals::gui_scale, 3.0f * globals::gui_scale)); - - if(ImGui::BeginTabBar("###play_menu.tabs", ImGuiTabBarFlags_FittingPolicyResizeDown)) { - if(ImGui::TabItemButton("<<")) { - globals::gui_screen = GUI_MAIN_MENU; - selected_server = nullptr; - editing_server = false; - } - - if(ImGui::BeginTabItem(str_tab_servers.c_str())) { - if(ImGui::BeginChild("###play_menu.servers.child", ImVec2(0.0f, -2.0f * ImGui::GetFrameHeightWithSpacing()))) { - layout_servers(); - } - - ImGui::EndChild(); - - layout_servers_buttons(); - - ImGui::EndTabItem(); - } - - if(ImGui::BeginTabItem("debug###play_menu.debug.child")) { - ImGui::ShowStyleEditor(); - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - - ImGui::PopStyleVar(); - } - - ImGui::End(); -} - -void gui::play_menu::update_late(void) -{ - for(auto item : servers_deque) { - if(item->status == item_status::UNKNOWN) { - gui::bother::ping(item->identity, item->hostname.c_str(), item->port); - item->status = item_status::PINGING; - continue; - } - } -} -- cgit