Füge Unterstützung für Würfelspiele hinzu und verbessere Debugging-Optionen

- Implementiere neue Funktionen in der ChatRoom-Klasse für das Starten, Rollen und Beenden von Würfelspielen.
- Füge eine Option zur Aktivierung von Debug-Logging in CMake hinzu, um die Entwicklung zu erleichtern.
- Aktualisiere die ChatUser-Klasse, um die Interaktion mit dem Würfelspiel zu ermöglichen.
- Verbessere die Socket-Verwaltung im Server, um WebSocket-Verbindungen zu unterstützen und die Handhabung von Anfragen zu optimieren.
- Aktualisiere die Konfiguration, um die neue Funktionalität zu unterstützen und die Benutzererfahrung zu verbessern.
This commit is contained in:
Torsten Schulz (local)
2025-08-16 22:43:08 +02:00
parent 864d86aa09
commit 7338f1a740
15 changed files with 1556 additions and 135 deletions

View File

@@ -6,41 +6,234 @@
#include <sys/socket.h>
#include <memory>
#include <iostream>
#include <algorithm>
#include <errno.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <set>
#include <mutex>
namespace Yc {
namespace Lib {
std::string Base::getJsonString(Json::Value json) {
std::string outString;
static std::set<int> g_wsSockets;
static std::mutex g_wsMutex;
// Rekursive Entfernung aller "token"-Felder
static void stripTokensRecursiveImpl(Json::Value &v) {
if (v.isObject()) {
if (v.isMember("token")) {
v.removeMember("token");
}
for (const auto &name : v.getMemberNames()) {
stripTokensRecursiveImpl(v[name]);
}
} else if (v.isArray()) {
for (Json::ArrayIndex i = 0; i < v.size(); ++i) {
stripTokensRecursiveImpl(v[i]);
}
}
}
// Öffentliche Helper
void Base::sanitizeTokens(Json::Value& v) {
stripTokensRecursiveImpl(v);
}
void Base::sanitizeTokensInString(std::string& s) {
if (s.find("\"token\"") == std::string::npos) return;
Json::Value v = getJsonTree(s);
if (!v.isNull()) {
sanitizeTokens(v);
s = getJsonString(v);
}
}
std::string Base::getJsonString(const Json::Value& json) {
std::stringstream outStream;
outStream << json;
return outStream.str();
}
void Base::send(int socket, std::string out) {
write(socket, out.c_str(), out.length());
// Token-Felder aus String-Payloads entfernen (falls JSON)
sanitizeTokensInString(out);
if (isWebSocket(socket)) {
sendWebSocketMessage(socket, out);
return;
}
ssize_t result = write(socket, out.c_str(), out.length());
if (result < 0) {
std::cout << "[Error] Fehler beim Senden auf Socket (vermutlich Verbindung verloren): " << strerror(errno) << std::endl;
}
}
void Base::send(int socket, Json::Value out) {
// Entferne alle Token-Felder rekursiv aus Antworten
sanitizeTokens(out);
std::string outString = getJsonString(out);
send(socket, outString);
}
std::string Base::readSocket(int socket)
{
std::string msg("");
char buffer[256];
bzero(buffer, 256);
while (int received = recv(socket, buffer, 255, 0) > 0) {
msg += std::string(buffer);
if (received < 255) {
if (isWebSocket(socket)) {
return readWebSocketMessage(socket);
}
std::string msg;
char buffer[1024];
while (true) {
ssize_t received = recv(socket, buffer, sizeof(buffer), 0);
if (received > 0) {
msg.append(buffer, static_cast<size_t>(received));
if (received < static_cast<ssize_t>(sizeof(buffer))) {
break;
}
} else if (received == 0) {
break;
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
}
break;
}
}
return msg;
}
Json::Value Base::getJsonTree(std::string msg) {
static std::string base64Encode(const unsigned char* input, size_t len) {
BIO *bmem = BIO_new(BIO_s_mem());
BIO *b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
b64 = BIO_push(b64, bmem);
BIO_write(b64, input, (int)len);
BIO_flush(b64);
BUF_MEM *bptr;
BIO_get_mem_ptr(b64, &bptr);
std::string out(bptr->data, bptr->length);
BIO_free_all(b64);
return out;
}
static std::string wsAcceptKey(const std::string& clientKey) {
const std::string magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
std::string in = clientKey + magic;
unsigned char sha[SHA_DIGEST_LENGTH];
SHA1(reinterpret_cast<const unsigned char*>(in.data()), in.size(), sha);
return base64Encode(sha, SHA_DIGEST_LENGTH);
}
std::string Base::webSocketAcceptKey(const std::string& clientKey) {
return wsAcceptKey(clientKey);
}
void Base::markWebSocket(int socket) {
std::lock_guard<std::mutex> lock(g_wsMutex);
g_wsSockets.insert(socket);
}
void Base::unmarkWebSocket(int socket) {
std::lock_guard<std::mutex> lock(g_wsMutex);
g_wsSockets.erase(socket);
}
bool Base::isWebSocket(int socket) {
std::lock_guard<std::mutex> lock(g_wsMutex);
return g_wsSockets.count(socket) > 0;
}
std::string Base::readWebSocketMessage(int socket) {
std::string accumulated;
bool haveText = false;
unsigned char firstOpcode = 0;
while (true) {
unsigned char hdr[2];
ssize_t r = recv(socket, hdr, 2, MSG_WAITALL);
if (r != 2) return std::string();
bool fin = (hdr[0] & 0x80) != 0;
unsigned char opcode = hdr[0] & 0x0F;
bool masked = (hdr[1] & 0x80) != 0;
uint64_t len = hdr[1] & 0x7F;
if (len == 126) {
unsigned char ext[2];
if (recv(socket, ext, 2, MSG_WAITALL) != 2) return {};
len = (ext[0] << 8) | ext[1];
} else if (len == 127) {
unsigned char ext[8];
if (recv(socket, ext, 8, MSG_WAITALL) != 8) return {};
len = 0; for (int i=0;i<8;++i) { len = (len<<8) | ext[i]; }
}
unsigned char mask[4] = {0,0,0,0};
if (masked) {
if (recv(socket, mask, 4, MSG_WAITALL) != 4) return {};
}
std::string payload; payload.resize((size_t)len);
if (len > 0) {
if (recv(socket, payload.data(), len, MSG_WAITALL) != (ssize_t)len) return {};
if (masked) {
for (uint64_t i=0;i<len;++i) payload[i] ^= mask[i % 4];
}
}
if (opcode == 0x8) {
// Close frame
return std::string();
} else if (opcode == 0x9) {
// Ping -> reply Pong with same payload
std::string frame;
unsigned char oh[2]; oh[0] = 0x80 | 0xA; // FIN + pong
size_t plen = payload.size();
if (plen < 126) { oh[1] = (unsigned char)plen; frame.append((char*)oh, 2); }
else if (plen <= 0xFFFF) { oh[1] = 126; frame.append((char*)oh, 2); unsigned char ext[2] = {(unsigned char)((plen>>8)&0xFF),(unsigned char)(plen&0xFF)}; frame.append((char*)ext,2);}
else { oh[1] = 127; frame.append((char*)oh,2); unsigned char ext[8]; uint64_t l=plen; for(int i=7;i>=0;--i){ext[i]=(unsigned char)(l&0xFF); l>>=8;} frame.append((char*)ext,8);}
frame.append(payload);
::send(socket, frame.data(), frame.size(), 0);
// continue reading next frame
continue;
} else if (opcode == 0xA) {
// Pong -> ignore
continue;
} else if (opcode == 0x1 || (opcode == 0x0 && haveText)) {
if (!haveText) { firstOpcode = opcode; haveText = true; }
accumulated += payload;
if (fin) {
return accumulated;
} else {
// read next continuation frame
continue;
}
} else {
// unsupported/binary -> ignore content, continue
if (fin) continue;
}
}
}
void Base::sendWebSocketMessage(int socket, const std::string& out) {
std::string frame;
unsigned char hdr[2];
hdr[0] = 0x80 | 0x1; // FIN + text
size_t len = out.size();
if (len < 126) {
hdr[1] = (unsigned char)len;
frame.append(reinterpret_cast<char*>(hdr), 2);
} else if (len <= 0xFFFF) {
hdr[1] = 126;
frame.append(reinterpret_cast<char*>(hdr), 2);
unsigned char ext[2] = { (unsigned char)((len>>8)&0xFF), (unsigned char)(len & 0xFF) };
frame.append(reinterpret_cast<char*>(ext), 2);
} else {
hdr[1] = 127;
frame.append(reinterpret_cast<char*>(hdr), 2);
unsigned char ext[8];
uint64_t l = len;
for (int i=7;i>=0;--i) { ext[i] = (unsigned char)(l & 0xFF); l >>= 8; }
frame.append(reinterpret_cast<char*>(ext), 8);
}
frame.append(out);
::send(socket, frame.data(), frame.size(), 0);
}
Json::Value Base::getJsonTree(const std::string& msg) {
Json::Value inputTree;
Json::CharReaderBuilder rbuilder;
std::unique_ptr<Json::CharReader> const reader(rbuilder.newCharReader());