mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 15:07:06 +02:00
Server browser: favorite servers
A dedicated server can be marked as a favorite: F on the chosen server, or ADD TO FAVORITES in the panel beside the list (the same to take it away). Favorites carry a star, are listed first whatever the sort, and have a FAVORITES filter beside FRIENDS.
This commit is contained in:
1 parent
8c9c6cff13
commit
6f1dae531c
1 file changed
+68
-2
@@ -20,6 +20,7 @@
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -86,7 +87,7 @@ constexpr std::uint32_t hash(std::string_view text, std::uint64_t salt = 0) {
|
||||
struct Row {
|
||||
const MultiplayerLobby *lobby{};
|
||||
std::string map;
|
||||
bool joinable{}, here{}, full{}, own{};
|
||||
bool joinable{}, here{}, full{}, own{}, favorite{};
|
||||
};
|
||||
struct Item {
|
||||
std::uint32_t id{};
|
||||
@@ -118,6 +119,9 @@ struct Hub {
|
||||
std::string search, password, code, code_password;
|
||||
Kind kind{Kind::all};
|
||||
bool friends_only{}, hide_full{}, same_map{}, code_mode{};
|
||||
// Dedicated servers the player marked, by Steam ID: first in the list, saved with the profile.
|
||||
std::set<std::uint64_t> favorites;
|
||||
bool favorites_loaded{}, favorites_only{};
|
||||
view::Sort sort{view::Sort::players};
|
||||
std::uint64_t selected{};
|
||||
// Hosting.
|
||||
@@ -338,7 +342,49 @@ struct Scroller {
|
||||
|
||||
// ---------------------------------------------------------------- the server browser
|
||||
|
||||
// Favourite servers. A dedicated server signed in with a login token keeps its Steam ID, so that
|
||||
// is what is kept: "ServerFavorites" in the profile, the IDs with commas between. A player's
|
||||
// lobby has a new ID each time and cannot be one.
|
||||
constexpr std::size_t most_favorites = 256;
|
||||
void load_favorites(Hub &h) {
|
||||
if (h.favorites_loaded) return;
|
||||
h.favorites_loaded = true;
|
||||
const auto saved = dingosdk::profile_runtime::local_value("ServerFavorites");
|
||||
if (!saved || !saved->is_string()) return;
|
||||
std::uint64_t id{};
|
||||
for (const char c : saved->string() + ",") {
|
||||
if (c >= '0' && c <= '9') id = id * 10 + static_cast<std::uint64_t>(c - '0');
|
||||
else {
|
||||
if (id && h.favorites.size() < most_favorites) h.favorites.insert(id);
|
||||
id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
void toggle_favorite(Hub &h, const MultiplayerLobby &lobby) {
|
||||
if (!lobby.dedicated) return;
|
||||
if (!h.favorites.erase(lobby.id)) {
|
||||
if (h.favorites.size() >= most_favorites) return say(h, "That is as many favorites as there is room for.");
|
||||
h.favorites.insert(lobby.id);
|
||||
}
|
||||
std::string text;
|
||||
for (const auto id : h.favorites) text += (text.empty() ? "" : ",") + std::to_string(id);
|
||||
dingosdk::profile_runtime::set_local_values({{"ServerFavorites", dingosdk::Json(text)}});
|
||||
h.rows_stale = true;
|
||||
}
|
||||
// A five-pointed star, for a favorite's row.
|
||||
void star(ImDrawList *draw, ImVec2 centre, float radius, ImU32 colour) {
|
||||
ImVec2 points[10];
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
const float angle = -1.5707963f + static_cast<float>(i) * .62831853f, r = i % 2 ? radius * .42f : radius;
|
||||
points[i] = ImVec2(centre.x + std::cos(angle) * r, centre.y + std::sin(angle) * r);
|
||||
}
|
||||
for (int i = 0; i < 10; i += 2) draw->AddTriangleFilled(points[i], points[(i + 1) % 10], points[(i + 9) % 10], colour);
|
||||
const ImVec2 inner[5]{points[1], points[3], points[5], points[7], points[9]};
|
||||
draw->AddConvexPolyFilled(inner, 5, colour);
|
||||
}
|
||||
|
||||
void rebuild(Hub &h) {
|
||||
load_favorites(h);
|
||||
h.rows_stale = false;
|
||||
h.rows.clear();
|
||||
const auto &mp = h.model;
|
||||
@@ -352,9 +398,13 @@ void rebuild(Hub &h) {
|
||||
if ((h.kind == Kind::servers && !lobby->dedicated) || (h.kind == Kind::lobbies && lobby->dedicated) ||
|
||||
(h.friends_only && lobby->friends.empty()) || (h.hide_full && full))
|
||||
continue;
|
||||
const bool favorite = lobby->dedicated && h.favorites.contains(lobby->id);
|
||||
if (h.favorites_only && !favorite) continue;
|
||||
h.rows.push_back({lobby, view::caption(view::map_name(lobby->map, levels), 40), view::can_join(mp, *lobby),
|
||||
mp.active && lobby->id == mp.public_lobby, full, lobby->owner == mp.local_id});
|
||||
mp.active && lobby->id == mp.public_lobby, full, lobby->owner == mp.local_id, favorite});
|
||||
}
|
||||
// Favorites come first, in the order the rest are in.
|
||||
std::stable_partition(h.rows.begin(), h.rows.end(), [](const Row &row) { return row.favorite; });
|
||||
if (std::none_of(h.rows.begin(), h.rows.end(), [&](const Row &row) { return row.lobby->id == h.selected; }))
|
||||
h.selected = h.rows.empty() ? 0 : h.rows.front().lobby->id;
|
||||
}
|
||||
@@ -405,6 +455,7 @@ void browser_page(Ui &ui, ImVec2 a, ImVec2 z) {
|
||||
if (chip("SERVERS", h.kind == Kind::servers)) h.kind = Kind::servers, changed = true;
|
||||
if (chip("LOBBIES", h.kind == Kind::lobbies)) h.kind = Kind::lobbies, changed = true;
|
||||
x += 16 * u;
|
||||
if (chip("FAVORITES", h.favorites_only)) h.favorites_only = !h.favorites_only, changed = true;
|
||||
if (chip("FRIENDS", h.friends_only)) h.friends_only = !h.friends_only, changed = true;
|
||||
if (chip("HIDE FULL", h.hide_full)) h.hide_full = !h.hide_full, changed = true;
|
||||
if (chip("MY MAP", h.same_map)) h.same_map = !h.same_map, changed = true;
|
||||
@@ -414,6 +465,9 @@ void browser_page(Ui &ui, ImVec2 a, ImVec2 z) {
|
||||
}
|
||||
if (ui.input && !h.typing && (ui.pad.buttons & ~h.pad_before & 0x4000) && !mp.lobby_searching) queue_multiplayer_action("browse", ""); // X / Square
|
||||
if (ui.input && !h.typing && (ui.pad.buttons & ~h.pad_before & 0x8000)) h.sort = static_cast<view::Sort>((static_cast<int>(h.sort) + 1) % 3), changed = true; // Y / Triangle
|
||||
// F marks the chosen server as a favorite, or takes the mark away.
|
||||
if (ui.input && !h.typing && !h.code_mode && ImGui::IsKeyPressed(ImGuiKey_F, false))
|
||||
if (const auto *row = selected_row(h); row && row->lobby->dedicated) toggle_favorite(h, *row->lobby), changed = true;
|
||||
|
||||
// ---- the list
|
||||
const ImVec2 la(a.x, a.y + bar + gap), lz(a.x + list_width, z.y - 60 * u);
|
||||
@@ -469,6 +523,10 @@ void browser_page(Ui &ui, ImVec2 a, ImVec2 z) {
|
||||
lit ? IM_COL32(0, 0, 0, 90) : lobby.official ? skate_theme::official : skate_theme::tile_light, 4 * u);
|
||||
draw->AddText(ui.bold, 24 * u, ImVec2(x + 10 * u, plate_top + 7 * u), lit || lobby.official ? theme::paper : theme::muted, plate);
|
||||
x += plate_width + 18 * u;
|
||||
if (row.favorite) {
|
||||
star(draw, ImVec2(x + 18 * u, top + row_height * .5f), 20 * u, lit ? theme::paper : skate_theme::warning);
|
||||
x += 48 * u;
|
||||
}
|
||||
const bool friends = !lobby.friends.empty();
|
||||
label(draw, ui.bold, name_size, ImVec2(x, top + (friends ? 14 * u : (row_height - name_size) * .5f)), map_x - 24 * u, ink, view::caption(lobby.name, 64));
|
||||
if (friends) label(draw, ui.body, 28 * u, ImVec2(x, top + 62 * u), map_x - 24 * u, lit ? theme::paper : skate_theme::friends_text, "with " + lobby_friends_text(lobby));
|
||||
@@ -512,6 +570,7 @@ void browser_page(Ui &ui, ImVec2 a, ImVec2 z) {
|
||||
};
|
||||
hint(0x1000, "Enter", "Join");
|
||||
if (ui.pad.available) hint(0x4000, "", "Refresh"), hint(0x8000, "", "Sort");
|
||||
else hint(0, "F", "Favorite");
|
||||
}
|
||||
|
||||
// ---- beside the list: the chosen session, or joining with a code
|
||||
@@ -577,6 +636,13 @@ void browser_page(Ui &ui, ImVec2 a, ImVec2 z) {
|
||||
const auto &status = !h.notice.empty() ? h.notice : mp.active && !mp.hosting && !mp.echo && row->joinable
|
||||
? std::string("Joining leaves the session you are in.") : mp.browser_status;
|
||||
label(draw, ui.body, 30 * u, ImVec2(left, y), edge, theme::muted, view::caption(status, 96));
|
||||
// A server can be kept as a favorite: it is listed first from then on.
|
||||
if (lobby.dedicated &&
|
||||
button(ui, hash("favorite"), ImVec2(left, foot.y - 100 * u), ImVec2(edge, foot.y - 16 * u),
|
||||
row->favorite ? "REMOVE FROM FAVORITES" : "ADD TO FAVORITES", Look::plain)) {
|
||||
toggle_favorite(h, lobby);
|
||||
rebuild(h); // `row` is gone from here on
|
||||
}
|
||||
} else {
|
||||
draw->AddText(ui.body, 36 * u, ImVec2(left, da.y + inset), theme::muted, "Pick a session to see it here.");
|
||||
label(draw, ui.body, 30 * u, ImVec2(left, da.y + inset + 60 * u), edge, theme::muted, view::caption(!h.notice.empty() ? h.notice : mp.browser_status, 96));
|
||||
|
||||
Reference in new issue
Block a user