fixes lol
This commit is contained in:
parent
a206a606f7
commit
07b8e12197
10 changed files with 211 additions and 57 deletions
|
|
@ -13,6 +13,7 @@
|
|||
#include <openssl/sha.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
std::unordered_map<WebSocketConnectionPtr, ChatWebSocketController::ConnectionInfo>
|
||||
ChatWebSocketController::connections_;
|
||||
|
|
@ -28,6 +29,20 @@ std::mutex ChatWebSocketController::connectionsMutex_;
|
|||
|
||||
// Helper to broadcast participant joined event to realm
|
||||
void ChatWebSocketController::broadcastParticipantJoined(const std::string& realmId, const ConnectionInfo& joinedUser) {
|
||||
// Check if user already has another connection in this realm (multiple tabs)
|
||||
// If so, don't broadcast - they're already shown as a participant
|
||||
int userConnectionsInRealm = 0;
|
||||
for (const auto& [conn, info] : connections_) {
|
||||
if (info.realmId == realmId && info.userId == joinedUser.userId) {
|
||||
userConnectionsInRealm++;
|
||||
}
|
||||
}
|
||||
|
||||
// Only broadcast if this is the user's first connection to this realm
|
||||
if (userConnectionsInRealm > 1) {
|
||||
return; // User already present, don't broadcast duplicate join
|
||||
}
|
||||
|
||||
Json::Value broadcast;
|
||||
broadcast["type"] = "participant_joined";
|
||||
broadcast["realmId"] = realmId;
|
||||
|
|
@ -44,12 +59,14 @@ void ChatWebSocketController::broadcastParticipantJoined(const std::string& real
|
|||
participant["joinedAt"] = static_cast<Json::Int64>(joinedAtMs);
|
||||
broadcast["participant"] = participant;
|
||||
|
||||
// Count participants in realm
|
||||
int count = 0;
|
||||
// Count unique participants in realm (not connections)
|
||||
std::unordered_set<std::string> uniqueUserIds;
|
||||
for (const auto& [conn, info] : connections_) {
|
||||
if (info.realmId == realmId) count++;
|
||||
if (info.realmId == realmId) {
|
||||
uniqueUserIds.insert(info.userId);
|
||||
}
|
||||
}
|
||||
broadcast["participantCount"] = count;
|
||||
broadcast["participantCount"] = static_cast<int>(uniqueUserIds.size());
|
||||
|
||||
std::string messageStr = Json::writeString(Json::StreamWriterBuilder(), broadcast);
|
||||
|
||||
|
|
@ -62,18 +79,34 @@ void ChatWebSocketController::broadcastParticipantJoined(const std::string& real
|
|||
|
||||
// Helper to broadcast participant left event to realm
|
||||
void ChatWebSocketController::broadcastParticipantLeft(const std::string& realmId, const std::string& userId, const std::string& username) {
|
||||
// Check if user still has other connections in this realm (multiple tabs)
|
||||
// This function is called AFTER the connection is removed, so if count > 0, user is still present
|
||||
int userConnectionsInRealm = 0;
|
||||
for (const auto& [conn, info] : connections_) {
|
||||
if (info.realmId == realmId && info.userId == userId) {
|
||||
userConnectionsInRealm++;
|
||||
}
|
||||
}
|
||||
|
||||
// Only broadcast if this was the user's last connection to this realm
|
||||
if (userConnectionsInRealm > 0) {
|
||||
return; // User still has other tabs open, don't broadcast leave
|
||||
}
|
||||
|
||||
Json::Value broadcast;
|
||||
broadcast["type"] = "participant_left";
|
||||
broadcast["realmId"] = realmId;
|
||||
broadcast["userId"] = userId;
|
||||
broadcast["username"] = username;
|
||||
|
||||
// Count remaining participants in realm
|
||||
int count = 0;
|
||||
// Count remaining unique participants in realm (not connections)
|
||||
std::unordered_set<std::string> uniqueUserIds;
|
||||
for (const auto& [conn, info] : connections_) {
|
||||
if (info.realmId == realmId) count++;
|
||||
if (info.realmId == realmId) {
|
||||
uniqueUserIds.insert(info.userId);
|
||||
}
|
||||
}
|
||||
broadcast["participantCount"] = count;
|
||||
broadcast["participantCount"] = static_cast<int>(uniqueUserIds.size());
|
||||
|
||||
std::string messageStr = Json::writeString(Json::StreamWriterBuilder(), broadcast);
|
||||
|
||||
|
|
@ -859,26 +892,43 @@ void ChatWebSocketController::handleGetParticipants(const WebSocketConnectionPtr
|
|||
response["realmId"] = info.realmId;
|
||||
response["participants"] = Json::arrayValue;
|
||||
|
||||
// Get all participants in the same realm
|
||||
// Get all participants in the same realm, deduplicated by userId
|
||||
// For users with multiple connections (multiple tabs), show only the earliest connection
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
std::unordered_map<std::string, const ConnectionInfo*> uniqueUsers;
|
||||
|
||||
for (const auto& [conn, connInfo] : connections_) {
|
||||
if (connInfo.realmId == info.realmId) {
|
||||
Json::Value participant;
|
||||
participant["userId"] = connInfo.userId;
|
||||
participant["username"] = connInfo.username;
|
||||
participant["userColor"] = connInfo.userColor;
|
||||
participant["avatarUrl"] = connInfo.avatarUrl;
|
||||
participant["isGuest"] = connInfo.isGuest;
|
||||
participant["isModerator"] = connInfo.isModerator;
|
||||
participant["isStreamer"] = connInfo.isStreamer;
|
||||
// Include join timestamp for ordering (milliseconds since epoch)
|
||||
auto joinedAtMs = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
connInfo.connectionTime.time_since_epoch()).count();
|
||||
participant["joinedAt"] = static_cast<Json::Int64>(joinedAtMs);
|
||||
response["participants"].append(participant);
|
||||
auto it = uniqueUsers.find(connInfo.userId);
|
||||
if (it == uniqueUsers.end()) {
|
||||
// First connection for this user
|
||||
uniqueUsers[connInfo.userId] = &connInfo;
|
||||
} else {
|
||||
// User already seen - keep the earlier connection (smaller joinedAt)
|
||||
if (connInfo.connectionTime < it->second->connectionTime) {
|
||||
uniqueUsers[connInfo.userId] = &connInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build response from deduplicated users
|
||||
for (const auto& [userId, connInfoPtr] : uniqueUsers) {
|
||||
Json::Value participant;
|
||||
participant["userId"] = connInfoPtr->userId;
|
||||
participant["username"] = connInfoPtr->username;
|
||||
participant["userColor"] = connInfoPtr->userColor;
|
||||
participant["avatarUrl"] = connInfoPtr->avatarUrl;
|
||||
participant["isGuest"] = connInfoPtr->isGuest;
|
||||
participant["isModerator"] = connInfoPtr->isModerator;
|
||||
participant["isStreamer"] = connInfoPtr->isStreamer;
|
||||
// Include join timestamp for ordering (milliseconds since epoch)
|
||||
auto joinedAtMs = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
connInfoPtr->connectionTime.time_since_epoch()).count();
|
||||
participant["joinedAt"] = static_cast<Json::Int64>(joinedAtMs);
|
||||
response["participants"].append(participant);
|
||||
}
|
||||
|
||||
response["count"] = response["participants"].size();
|
||||
wsConnPtr->send(Json::writeString(Json::StreamWriterBuilder(), response));
|
||||
}
|
||||
|
|
@ -1275,21 +1325,22 @@ void ChatWebSocketController::broadcastToUser(const std::string& userId, const J
|
|||
|
||||
Json::Value ChatWebSocketController::getRealmStats() {
|
||||
Json::Value result = Json::arrayValue;
|
||||
std::map<std::string, int> realmCounts;
|
||||
// Count unique users per realm (not connections)
|
||||
std::map<std::string, std::unordered_set<std::string>> realmUsers;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
for (const auto& [conn, info] : connections_) {
|
||||
if (!info.realmId.empty()) {
|
||||
realmCounts[info.realmId]++;
|
||||
realmUsers[info.realmId].insert(info.userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [realmId, count] : realmCounts) {
|
||||
for (const auto& [realmId, userIds] : realmUsers) {
|
||||
Json::Value realm;
|
||||
realm["realmId"] = realmId;
|
||||
realm["participantCount"] = count;
|
||||
realm["participantCount"] = static_cast<int>(userIds.size());
|
||||
result.append(realm);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue