mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-19 07:45:26 +02:00
Merge branch 'Dingo-Shenanigans:main' into main
This commit is contained in:
4 files changed
+102
-67
No files matched your search
@@ -116,17 +116,31 @@ hostent* WSAAPI get_host_by_name(const char* name) {
|
||||
return original_get_host_by_name.load()(name);
|
||||
}
|
||||
|
||||
// `required`: the entry point the game itself resolves names through; without it nothing is
|
||||
// blocked, and that is an error. The others close the same door for anything else loaded into
|
||||
// the process, and are left alone where they cannot be hooked: Wine's ws2_32 (Proton, on Linux
|
||||
// and the Steam Deck) does not have all of them, and what it does not have nothing can call.
|
||||
template<class Fn>
|
||||
void hook(const char* name, void* replacement, std::atomic<Fn>& original) {
|
||||
void hook(const char* name, void* replacement, std::atomic<Fn>& original, bool required) {
|
||||
const auto fail = [&](const std::string& why) {
|
||||
if (required) throw std::runtime_error(why);
|
||||
logging::log(logging::Level::info, logging::Channel::runtime, "EA online services block: {} left as it is ({}).", name, why);
|
||||
};
|
||||
const auto module = LoadLibraryExW(L"ws2_32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
const auto target = module ? reinterpret_cast<void*>(GetProcAddress(module, name)) : nullptr;
|
||||
if (!target) throw std::runtime_error(std::string("Missing ") + name);
|
||||
if (!target) return fail(std::string("Missing ") + name);
|
||||
void* relay{};
|
||||
const auto create = hook_prepare(target, replacement, &relay);
|
||||
if (create != HookOk) throw std::runtime_error(std::string("Cannot hook ") + name + ": " + hook_status_string(create));
|
||||
if (create != HookOk || !relay) {
|
||||
if (create == HookOk) (void)hook_remove(target);
|
||||
return fail(std::string("Cannot hook ") + name + ": " + hook_status_string(create == HookOk ? HookUnsupportedFunction : create));
|
||||
}
|
||||
original.store(reinterpret_cast<Fn>(relay));
|
||||
const auto enable = hook_enable(target);
|
||||
if (enable != HookOk) throw std::runtime_error(std::string("Cannot enable ") + name + ": " + hook_status_string(enable));
|
||||
if (enable != HookOk) {
|
||||
(void)hook_remove(target);
|
||||
return fail(std::string("Cannot enable ") + name + ": " + hook_status_string(enable));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -137,11 +151,11 @@ bool start_ea_service_block(std::string& error) noexcept {
|
||||
// same door for anything else loaded into the process.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4191)
|
||||
hook("getaddrinfo", reinterpret_cast<void*>(&get_addr_info_a), original_get_addr_info_a);
|
||||
hook("GetAddrInfoW", reinterpret_cast<void*>(&get_addr_info_w), original_get_addr_info_w);
|
||||
hook("GetAddrInfoExA", reinterpret_cast<void*>(&get_addr_info_ex_a), original_get_addr_info_ex_a);
|
||||
hook("GetAddrInfoExW", reinterpret_cast<void*>(&get_addr_info_ex_w), original_get_addr_info_ex_w);
|
||||
hook("gethostbyname", reinterpret_cast<void*>(&get_host_by_name), original_get_host_by_name);
|
||||
hook("getaddrinfo", reinterpret_cast<void*>(&get_addr_info_a), original_get_addr_info_a, true);
|
||||
hook("GetAddrInfoW", reinterpret_cast<void*>(&get_addr_info_w), original_get_addr_info_w, false);
|
||||
hook("GetAddrInfoExA", reinterpret_cast<void*>(&get_addr_info_ex_a), original_get_addr_info_ex_a, false);
|
||||
hook("GetAddrInfoExW", reinterpret_cast<void*>(&get_addr_info_ex_w), original_get_addr_info_ex_w, false);
|
||||
hook("gethostbyname", reinterpret_cast<void*>(&get_host_by_name), original_get_host_by_name, false);
|
||||
#pragma warning(pop)
|
||||
error.clear();
|
||||
return true;
|
||||
|
||||
@@ -449,77 +449,96 @@ void draw_command_list(ChatState& c, ImFont* heading, ImFont* body, ImVec2 botto
|
||||
"; Tab for the next, Shift+Tab back").c_str());
|
||||
}
|
||||
|
||||
// The vote a dedicated server is running, or has just finished: a card above the chat with what
|
||||
// is voted on, the tally, the time left, and Yes and No. The player answers with their binds
|
||||
// (shown on the buttons; F1 and F2 unless changed), or by clicking while the cursor is free.
|
||||
// `bottom_right` is the corner the card sits on. Returns the room it took, 0 when there is none.
|
||||
float draw_vote(const MultiplayerVote& vote, ImFont* heading, ImFont* body, ImVec2 bottom_right, float width, float scale,
|
||||
bool clickable) {
|
||||
if (!vote.id) return 0.0f;
|
||||
constexpr ImU32 green = IM_COL32(88, 214, 141, 255), red = IM_COL32(236, 96, 96, 255);
|
||||
// The vote a dedicated server is running, or has just finished: a card on the middle of the
|
||||
// screen's right edge, in the look of the overlay's notices (a dark tile with a strip of colour
|
||||
// down its left). It says what is voted on, the tally and the time left, and has Yes and No. The
|
||||
// player answers with their binds (the keycaps on the buttons; F1 and F2 unless changed), or by
|
||||
// clicking while the cursor is free.
|
||||
void draw_vote(const MultiplayerVote& vote, ImFont* heading, ImFont* body, ImVec2 display, float scale, bool clickable) {
|
||||
if (!vote.id) return;
|
||||
constexpr ImU32 green = skate_theme::good, red = skate_theme::danger;
|
||||
const bool running = vote.outcome == 0;
|
||||
const ImU32 accent = running ? theme::blue : vote.outcome == 1 ? green : vote.outcome == 2 ? red : theme::muted;
|
||||
const char* title = running ? "VOTE" : vote.outcome == 1 ? "VOTE PASSED" : vote.outcome == 2 ? "VOTE FAILED" : "VOTE CANCELLED";
|
||||
std::string question = vote.label;
|
||||
if (!question.empty() && question.front() >= 'a' && question.front() <= 'z') question.front() = static_cast<char>(question.front() - 32);
|
||||
if (running) question += '?';
|
||||
const float pad = 12.0f * scale, gap = 6.0f * scale, title_size = 14.0f * scale, size = 16.0f * scale, fine = 14.0f * scale;
|
||||
const float button_height = 30.0f * scale, bar = 3.0f * scale, inner = width - pad * 2.0f;
|
||||
const auto question_extent = body->CalcTextSizeA(size, FLT_MAX, inner, question.c_str());
|
||||
const float width = 360.0f * scale, margin = 24.0f * scale, strip = 5.0f * scale;
|
||||
const float pad = 14.0f * scale, gap = 8.0f * scale, title_size = 13.0f * scale, size = 18.0f * scale, fine = 13.0f * scale;
|
||||
const float button_height = 34.0f * scale, bar = 4.0f * scale, inner = width - strip - pad * 2.0f;
|
||||
const auto question_extent = heading->CalcTextSizeA(size, FLT_MAX, inner, question.c_str());
|
||||
const bool answers = running && vote.may_vote;
|
||||
const float height = pad + title_size + gap + question_extent.y + gap + fine + gap + bar +
|
||||
const float height = pad + title_size + gap + question_extent.y + gap + bar + gap + fine +
|
||||
(running ? gap + (answers ? button_height : fine) : 0.0f) + pad;
|
||||
const ImVec2 min(bottom_right.x - width, bottom_right.y - height), max(bottom_right);
|
||||
const ImVec2 min(display.x - margin - width, std::floor((display.y - height) * 0.5f)), max(min.x + width, min.y + height);
|
||||
auto* draw = ImGui::GetForegroundDrawList();
|
||||
draw->AddRectFilled(min, max, with_alpha(theme::ink, 0.92f), 4.0f * scale);
|
||||
draw->AddRect(min, max, with_alpha(accent, 0.9f), 4.0f * scale);
|
||||
draw->AddRectFilled(min, max, with_alpha(theme::ink, 0.92f));
|
||||
draw->AddRectFilled(min, ImVec2(min.x + strip, max.y), accent);
|
||||
const float left = min.x + strip + pad, right = max.x - pad;
|
||||
float y = min.y + pad;
|
||||
draw->AddText(heading, title_size, ImVec2(min.x + pad, y), accent, title);
|
||||
draw->AddText(heading, title_size, ImVec2(left, y), accent, title);
|
||||
if (running) {
|
||||
const auto left = std::to_string(vote.seconds) + " s";
|
||||
const auto extent = heading->CalcTextSizeA(title_size, FLT_MAX, 0.0f, left.c_str());
|
||||
draw->AddText(heading, title_size, ImVec2(max.x - pad - extent.x, y), theme::muted, left.c_str());
|
||||
const auto time = std::to_string(vote.seconds) + " s";
|
||||
const auto extent = heading->CalcTextSizeA(title_size, FLT_MAX, 0.0f, time.c_str());
|
||||
draw->AddText(heading, title_size, ImVec2(right - extent.x, y), theme::paper, time.c_str());
|
||||
}
|
||||
y += title_size + gap;
|
||||
draw->AddText(body, size, ImVec2(min.x + pad, y), theme::paper, question.c_str(), nullptr, inner);
|
||||
draw->AddText(heading, size, ImVec2(left, y), theme::paper, question.c_str(), nullptr, inner);
|
||||
y += question_extent.y + gap;
|
||||
// The tally, and a bar of how far Yes is towards what it needs.
|
||||
float x = min.x + pad;
|
||||
// How far Yes is towards what it needs, then the tally under it.
|
||||
const float share = vote.needed ? std::clamp(static_cast<float>(vote.yes) / static_cast<float>(vote.needed), 0.0f, 1.0f) : 0.0f;
|
||||
draw->AddRectFilled(ImVec2(left, y), ImVec2(right, y + bar), skate_theme::tile_light);
|
||||
if (share > 0.0f) draw->AddRectFilled(ImVec2(left, y), ImVec2(left + inner * share, y + bar), green);
|
||||
y += bar + gap;
|
||||
float x = left;
|
||||
const auto word = [&](const std::string& text, ImU32 colour) {
|
||||
draw->AddText(heading, fine, ImVec2(x, y), colour, text.c_str());
|
||||
x += heading->CalcTextSizeA(fine, FLT_MAX, 0.0f, text.c_str()).x + 14.0f * scale;
|
||||
draw->AddText(body, fine, ImVec2(x, y), colour, text.c_str());
|
||||
x += body->CalcTextSizeA(fine, FLT_MAX, 0.0f, text.c_str()).x + 14.0f * scale;
|
||||
};
|
||||
word("Yes " + std::to_string(vote.yes), green);
|
||||
word("No " + std::to_string(vote.no), red);
|
||||
word(std::to_string(vote.needed) + " needed", theme::muted);
|
||||
y += fine + gap;
|
||||
const float share = vote.needed ? std::clamp(static_cast<float>(vote.yes) / static_cast<float>(vote.needed), 0.0f, 1.0f) : 0.0f;
|
||||
draw->AddRectFilled(ImVec2(min.x + pad, y), ImVec2(max.x - pad, y + bar), with_alpha(theme::muted, 0.35f), bar * 0.5f);
|
||||
if (share > 0.0f) draw->AddRectFilled(ImVec2(min.x + pad, y), ImVec2(min.x + pad + inner * share, y + bar), green, bar * 0.5f);
|
||||
y += bar;
|
||||
if (!running) return height + gap;
|
||||
{
|
||||
const auto needed = std::to_string(vote.needed) + " needed";
|
||||
const auto extent = body->CalcTextSizeA(fine, FLT_MAX, 0.0f, needed.c_str());
|
||||
draw->AddText(body, fine, ImVec2(right - extent.x, y), theme::muted, needed.c_str());
|
||||
}
|
||||
y += fine;
|
||||
if (!running) return;
|
||||
y += gap;
|
||||
if (!answers) {
|
||||
draw->AddText(body, fine, ImVec2(min.x + pad, y), theme::muted, "This vote is about you, so you have no vote in it.");
|
||||
return height + gap;
|
||||
draw->AddText(body, fine, ImVec2(left, y), theme::muted, "This vote is about you, so you have no vote in it.");
|
||||
return;
|
||||
}
|
||||
// Two tiles as the menu's: grey, the answer's colour once it is this player's, each with its
|
||||
// bind on a white keycap.
|
||||
const float button_width = (inner - gap) * 0.5f;
|
||||
const auto button = [&](float left, bool yes, std::uint32_t bind) {
|
||||
const ImVec2 a(left, y), b(left + button_width, y + button_height);
|
||||
const auto button = [&](float at, bool yes, std::uint32_t bind) {
|
||||
const ImVec2 a(at, y), b(at + button_width, y + button_height);
|
||||
const ImU32 colour = yes ? green : red;
|
||||
const bool chosen = vote.mine == (yes ? 1 : 2);
|
||||
const bool hovered = clickable && ImGui::IsMouseHoveringRect(a, b, false);
|
||||
draw->AddRectFilled(a, b, with_alpha(colour, chosen ? 0.85f : hovered ? 0.35f : 0.14f), 4.0f * scale);
|
||||
draw->AddRect(a, b, with_alpha(colour, 0.9f), 4.0f * scale);
|
||||
const std::string label = (bind ? dingosdk::controller_combo_label(bind) + " " : std::string{}) + (yes ? "Yes" : "No");
|
||||
const auto extent = heading->CalcTextSizeA(size, FLT_MAX, 0.0f, label.c_str());
|
||||
draw->AddText(heading, size, ImVec2(a.x + (button_width - extent.x) * 0.5f, a.y + (button_height - extent.y) * 0.5f),
|
||||
chosen ? theme::ink : theme::paper, label.c_str());
|
||||
draw->AddRectFilled(a, b, chosen ? colour : hovered ? skate_theme::tile_light : skate_theme::tile_grey);
|
||||
if (!chosen) draw->AddRectFilled(ImVec2(a.x, b.y - 3.0f * scale), b, colour);
|
||||
const std::string key = bind ? dingosdk::controller_combo_label(bind) : std::string{};
|
||||
const char* label = yes ? "YES" : "NO";
|
||||
const float label_size = 15.0f * scale, key_size = 12.0f * scale, cap_height = 20.0f * scale;
|
||||
const auto label_extent = heading->CalcTextSizeA(label_size, FLT_MAX, 0.0f, label);
|
||||
const auto key_extent = key.empty() ? ImVec2{} : heading->CalcTextSizeA(key_size, FLT_MAX, 0.0f, key.c_str());
|
||||
const float cap_width = key.empty() ? 0.0f : key_extent.x + 12.0f * scale, between = key.empty() ? 0.0f : 8.0f * scale;
|
||||
float cursor = a.x + (button_width - cap_width - between - label_extent.x) * 0.5f;
|
||||
if (!key.empty()) {
|
||||
const ImVec2 cap(cursor, a.y + (button_height - cap_height) * 0.5f);
|
||||
draw->AddRectFilled(cap, ImVec2(cap.x + cap_width, cap.y + cap_height), theme::paper, 3.0f * scale);
|
||||
draw->AddText(heading, key_size, ImVec2(cap.x + 6.0f * scale, cap.y + (cap_height - key_extent.y) * 0.5f), skate_theme::black,
|
||||
key.c_str());
|
||||
cursor += cap_width + between;
|
||||
}
|
||||
draw->AddText(heading, label_size, ImVec2(cursor, a.y + (button_height - label_extent.y) * 0.5f),
|
||||
chosen ? skate_theme::black : theme::paper, label);
|
||||
if (hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) queue_multiplayer_action("vote", yes ? "yes" : "no");
|
||||
};
|
||||
button(min.x + pad, true, vote.yes_bind);
|
||||
button(min.x + pad + button_width + gap, false, vote.no_bind);
|
||||
return height + gap;
|
||||
button(left, true, vote.yes_bind);
|
||||
button(left + button_width + gap, false, vote.no_bind);
|
||||
}
|
||||
|
||||
void draw_chat() {
|
||||
@@ -637,11 +656,10 @@ void draw_chat() {
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor(4);
|
||||
// The vote card sits on the panel; the command list and the emote picker go above it.
|
||||
const float above = panel_top - 6.0f * scale -
|
||||
draw_vote(c.feed.vote, heading, body, ImVec2(display.x - margin, panel_top - 6.0f * scale), width, scale, true);
|
||||
const float above = panel_top - 6.0f * scale;
|
||||
draw_command_list(c, heading, body, ImVec2(display.x - margin - width, above), width, scale);
|
||||
emote_picker(c, heading, ImVec2(display.x - margin - width, above), width, scale);
|
||||
draw_vote(c.feed.vote, heading, body, display, scale, true); // the cursor is free while typing
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -692,9 +710,7 @@ void draw_chat() {
|
||||
else draw->AddText(body, size, text_at, text, drawn.text.c_str(), nullptr, wrap);
|
||||
bottom = top_left.y - gap;
|
||||
}
|
||||
// Above the newest lines, or in the corner when there are none. Clickable while the menu or
|
||||
// the console has freed the cursor.
|
||||
draw_vote(c.feed.vote, heading, body, ImVec2(display.x - margin, bottom), width, scale,
|
||||
s.visible.load() || s.console_visible.load());
|
||||
// Clickable while the menu or the console has freed the cursor.
|
||||
draw_vote(c.feed.vote, heading, body, display, scale, s.visible.load() || s.console_visible.load());
|
||||
}
|
||||
} // namespace dingosdk::overlay::detail
|
||||
@@ -139,7 +139,7 @@ void camera_controls(SkateMenu& menu, const Model& model, const CallbacksV3& cal
|
||||
|
||||
void movement_controls(SkateMenu& menu, const Model& model, const CallbacksV3& callbacks) {
|
||||
const auto& debug = model.debug;
|
||||
begin_card(menu, "movement", "MOVEMENT");
|
||||
begin_card(menu, "movement", "FLIGHT & BAILS");
|
||||
bool noclip = debug.noclip;
|
||||
if (toggle_row(menu, "Noclip", "Fly with the normal player camera. Includes No Bail; uses the Freecam flight speed.", noclip,
|
||||
(debug.noclip_available || debug.noclip) && callbacks.queue_debug))
|
||||
@@ -155,9 +155,14 @@ void movement_controls(SkateMenu& menu, const Model& model, const CallbacksV3& c
|
||||
(debug.no_bail_available || debug.no_bail) && callbacks.queue_debug))
|
||||
debug_request(menu, callbacks, {DebugAction::set_no_bail, no_bail});
|
||||
bool hall_of_meat = model.hall_of_meat.enabled;
|
||||
if (toggle_row(menu, "Hall of Meat",
|
||||
"Your bails show the bones they hurt and score a Meat card, as in skate. 3. A break slows the game in single player.",
|
||||
hall_of_meat, model.hall_of_meat.available && callbacks.queue_console_command))
|
||||
// No Bail (and noclip, which includes it) stops the bails Hall of Meat scores.
|
||||
const bool bails_off = debug.no_bail || debug.no_bail_active;
|
||||
const char* meat_help = hall_of_meat && bails_off
|
||||
? "On, but No Bail is stopping your bails, so nothing shows. Turn No Bail (and noclip) off to use it."
|
||||
: bails_off
|
||||
? "Your bails show the bones they hurt and score a Meat card. Needs No Bail (and noclip) off."
|
||||
: "Your bails show the bones they hurt and score a Meat card. A break slows the game in single player.";
|
||||
if (toggle_row(menu, "Hall of Meat", meat_help, hall_of_meat, model.hall_of_meat.available && callbacks.queue_console_command))
|
||||
send_console(menu, callbacks, hall_of_meat ? "hallofmeat 1" : "hallofmeat 0");
|
||||
end_card();
|
||||
|
||||
@@ -182,7 +187,7 @@ void movement_controls(SkateMenu& menu, const Model& model, const CallbacksV3& c
|
||||
}
|
||||
|
||||
void skater_page(SkateMenu& menu, const Model& model, const CallbacksV3& callbacks) {
|
||||
category_tabs(menu, menu.skater_tab, {"CAMERA", "MOVEMENT"}, "skater-tabs");
|
||||
category_tabs(menu, menu.skater_tab, {"CAMERA", "GAMEPLAY"}, "skater-tabs");
|
||||
ImGui::PushID(menu.skater_tab);
|
||||
ImGui::BeginChild("skater-tab", ImVec2(0, page_body_height(menu)));
|
||||
if (menu.skater_tab == 0) camera_controls(menu, model, callbacks);
|
||||
|
||||
@@ -164,7 +164,7 @@ void Host::start_vote(Guest &guest, VoteKind kind, std::string_view argument) {
|
||||
const auto label = vote.label;
|
||||
vote_ = std::move(vote);
|
||||
send_chat(guest_name(guest) + " started a vote to " + label + " (" + std::to_string(setting.percent) + "% needed, " +
|
||||
std::to_string(config_.votes.seconds) + " s). Vote on the card above chat, or type /yes or /no.");
|
||||
std::to_string(config_.votes.seconds) + " s). Vote on the card at the right of your screen, or type /yes or /no.");
|
||||
log_("[vote] " + guest_name(guest) + " started a vote to " + label + ".");
|
||||
check_vote(false);
|
||||
}
|
||||
@@ -228,7 +228,7 @@ void Host::check_vote(bool expired) {
|
||||
const bool lost = !passed && (expired || yes + (eligible - std::min(eligible, yes + no)) < needed);
|
||||
const auto tally = std::to_string(yes) + " yes, " + std::to_string(no) + " no, " + std::to_string(needed) + " needed";
|
||||
if (!passed && !lost) {
|
||||
// Games show the tally as it changes (the vote card above chat); chat is not filled with it.
|
||||
// Games show the tally as it changes (the vote card); chat is not filled with it.
|
||||
vote.shown_yes = yes;
|
||||
vote.shown_no = no;
|
||||
show_vote(vote, multiplayer::vote_running, yes, no, needed);
|
||||
|
||||
Reference in new issue
Block a user