mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-10 09:11:52 -04:00
fix: harden Discord IPC retries
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -32,6 +33,7 @@ namespace {
|
||||
constexpr uint32_t kHandshakeOpcode = 0;
|
||||
constexpr uint32_t kFrameOpcode = 1;
|
||||
constexpr size_t kMaxClientIdLength = 32;
|
||||
constexpr auto kConnectionRetryCooldown = std::chrono::seconds(1);
|
||||
|
||||
bool IsClientId(std::string_view value) {
|
||||
return !value.empty() && value.size() <= kMaxClientIdLength &&
|
||||
@@ -87,32 +89,48 @@ std::string BuildActivityPayload(const Activity& activity) {
|
||||
bool hasActivityField = false;
|
||||
AppendJsonString(json, hasActivityField, "details", activity.details);
|
||||
AppendJsonString(json, hasActivityField, "state", activity.state);
|
||||
const auto appendSection = [&](std::string_view name, const auto& append) {
|
||||
if (hasActivityField) {
|
||||
json << ',';
|
||||
}
|
||||
json << '\"' << name << "\":{";
|
||||
append();
|
||||
json << '}';
|
||||
hasActivityField = true;
|
||||
};
|
||||
const bool hasAssets = !activity.largeImageKey.empty() || !activity.largeImageText.empty() ||
|
||||
!activity.smallImageKey.empty() || !activity.smallImageText.empty();
|
||||
if (hasAssets) {
|
||||
appendSection("assets", [&] {
|
||||
bool hasAsset = false;
|
||||
AppendJsonString(json, hasAsset, "large_image", activity.largeImageKey);
|
||||
AppendJsonString(json, hasAsset, "large_text", activity.largeImageText);
|
||||
AppendJsonString(json, hasAsset, "small_image", activity.smallImageKey);
|
||||
AppendJsonString(json, hasAsset, "small_text", activity.smallImageText);
|
||||
});
|
||||
}
|
||||
if (activity.startTimestamp > 0 || activity.endTimestamp > 0) {
|
||||
appendSection("timestamps", [&] {
|
||||
if (activity.startTimestamp > 0) {
|
||||
json << "\"start\":" << activity.startTimestamp;
|
||||
}
|
||||
if (activity.endTimestamp > 0) {
|
||||
if (activity.startTimestamp > 0) {
|
||||
json << ',';
|
||||
}
|
||||
json << "\"end\":" << activity.endTimestamp;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (activity.partySize > 0 && activity.partyMax > 0) {
|
||||
appendSection("party", [&] {
|
||||
json << "\"size\":[" << activity.partySize << ',' << activity.partyMax << ']';
|
||||
});
|
||||
}
|
||||
if (hasActivityField) {
|
||||
json << ',';
|
||||
}
|
||||
json << "\"assets\":{";
|
||||
bool hasAsset = false;
|
||||
AppendJsonString(json, hasAsset, "large_image", activity.largeImageKey);
|
||||
AppendJsonString(json, hasAsset, "large_text", activity.largeImageText);
|
||||
AppendJsonString(json, hasAsset, "small_image", activity.smallImageKey);
|
||||
AppendJsonString(json, hasAsset, "small_text", activity.smallImageText);
|
||||
json << "},\"timestamps\":{";
|
||||
bool hasTimestamp = false;
|
||||
if (activity.startTimestamp > 0) {
|
||||
json << "\"start\":" << activity.startTimestamp;
|
||||
hasTimestamp = true;
|
||||
}
|
||||
if (activity.endTimestamp > 0) {
|
||||
if (hasTimestamp) {
|
||||
json << ',';
|
||||
}
|
||||
json << "\"end\":" << activity.endTimestamp;
|
||||
}
|
||||
json << "},\"party\":{";
|
||||
if (activity.partySize > 0 || activity.partyMax > 0) {
|
||||
json << "\"size\":[" << activity.partySize << ',' << activity.partyMax << ']';
|
||||
}
|
||||
json << "},\"instance\":false}}}";
|
||||
json << "\"instance\":false}}}";
|
||||
return json.str();
|
||||
}
|
||||
|
||||
@@ -195,10 +213,12 @@ private:
|
||||
}
|
||||
const std::string handshake = "{\"v\":1,\"client_id\":\"" + clientId + "\"}";
|
||||
if (!WriteFrameLocked(kHandshakeOpcode, handshake)) {
|
||||
RecordFailedConnectionLocked();
|
||||
CloseLocked();
|
||||
return;
|
||||
}
|
||||
if (!ReadReadyLocked()) {
|
||||
RecordFailedConnectionLocked();
|
||||
CloseLocked();
|
||||
return;
|
||||
}
|
||||
@@ -213,15 +233,28 @@ private:
|
||||
return;
|
||||
}
|
||||
if (!WriteFrameLocked(kFrameOpcode, BuildActivityPayload(activity_))) {
|
||||
RecordFailedConnectionLocked();
|
||||
CloseLocked();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionRetryAllowedLocked() const {
|
||||
return !lastFailedConnectionAttempt_ ||
|
||||
std::chrono::steady_clock::now() - *lastFailedConnectionAttempt_ >= kConnectionRetryCooldown;
|
||||
}
|
||||
|
||||
void RecordFailedConnectionLocked() {
|
||||
lastFailedConnectionAttempt_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
bool ConnectLocked() {
|
||||
if (connected_) {
|
||||
return true;
|
||||
}
|
||||
if (!ConnectionRetryAllowedLocked()) {
|
||||
return false;
|
||||
}
|
||||
for (unsigned int index = 0; index < 10; ++index) {
|
||||
const std::string name = "\\\\.\\pipe\\discord-ipc-" + std::to_string(index);
|
||||
handle_ = ::CreateFileA(name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
@@ -229,9 +262,11 @@ private:
|
||||
DWORD mode = PIPE_READMODE_BYTE;
|
||||
::SetNamedPipeHandleState(handle_, &mode, nullptr, nullptr);
|
||||
connected_ = true;
|
||||
lastFailedConnectionAttempt_.reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
RecordFailedConnectionLocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -248,9 +283,25 @@ private:
|
||||
}
|
||||
|
||||
bool ReadAllLocked(uint8_t* data, size_t size) {
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(1);
|
||||
while (size != 0) {
|
||||
if (std::chrono::steady_clock::now() >= deadline) {
|
||||
return false;
|
||||
}
|
||||
DWORD available = 0;
|
||||
if (!::PeekNamedPipe(handle_, nullptr, 0, nullptr, &available, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
if (available == 0) {
|
||||
if (std::chrono::steady_clock::now() >= deadline) {
|
||||
return false;
|
||||
}
|
||||
::Sleep(10);
|
||||
continue;
|
||||
}
|
||||
DWORD read = 0;
|
||||
if (!::ReadFile(handle_, data, static_cast<DWORD>(size), &read, nullptr) || read == 0) {
|
||||
const DWORD requested = static_cast<DWORD>(std::min<size_t>(size, available));
|
||||
if (!::ReadFile(handle_, data, requested, &read, nullptr) || read == 0) {
|
||||
return false;
|
||||
}
|
||||
data += read;
|
||||
@@ -273,6 +324,9 @@ private:
|
||||
if (connected_) {
|
||||
return true;
|
||||
}
|
||||
if (!ConnectionRetryAllowedLocked()) {
|
||||
return false;
|
||||
}
|
||||
std::array<std::string, 5> roots{};
|
||||
size_t rootCount = 0;
|
||||
if (const char* runtimeDir = std::getenv("XDG_RUNTIME_DIR"); runtimeDir && *runtimeDir) {
|
||||
@@ -307,11 +361,13 @@ private:
|
||||
::setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
fd_ = socketFd;
|
||||
connected_ = true;
|
||||
lastFailedConnectionAttempt_.reset();
|
||||
return true;
|
||||
}
|
||||
::close(socketFd);
|
||||
}
|
||||
}
|
||||
RecordFailedConnectionLocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -375,6 +431,7 @@ private:
|
||||
std::mutex mutex_;
|
||||
bool connected_ = false;
|
||||
bool customClient_ = false;
|
||||
std::optional<std::chrono::steady_clock::time_point> lastFailedConnectionAttempt_;
|
||||
std::string basicClientId_;
|
||||
std::string clientId_;
|
||||
Activity basicActivity_;
|
||||
|
||||
Reference in New Issue
Block a user