/
yaseeeeechka
/
CenterControl
Обзор
Документация
Войти
/
yaseeeeechka
/
CenterControl
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
CenterControl_TCP/include/client/Session.h
610 строк
21 KB
Yaroslava
New structure of Repo. TCP impl
11 дек 2025, 21:12
11 дек 2025, 21:12
6fdf9d7
Код
Авторство
О чём код?
#ifndef SESSION_H #define SESSION_H #include <functional> #include <string> #include <iostream> #include "MsgProtocol.h" // ==================== Платформозависимые includes ===================== #ifdef __linux__ #include <arpa/inet.h> #include <unistd.h> using SOCKET = int; constexpr int SOCKET_ERROR = -1; constexpr int INVALID_SOCKET = -1; #else #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib") #endif #ifdef _WIN32 const std::string kFontPath = "D:\\CenterControl\\CenterControl_TCP\\fonts\\arial\\arial.ttf"; #else const std::string kFontPath = "/home/user/dir/programming/C++/Yaroslava/CenterControl/fonts/arial/arial.ttf"; #endif // ==================== Session ========================================== class Session { public: using RoomsCallback = std::function<void(const std::vector<RoomInfo> &)>; using RoomCreateCallback = std::function<void(bool, const std::string &)>; using RoomPlayersCallback = std::function<void(const std::string &, const std::vector<std::string> &)>; using ChatCallback = std::function<void(const std::vector<ChatMessageDTO> &)>; using TopScoresCallback = std::function<void(const std::vector<ScoreDTO>&)>; public: explicit Session(SOCKET sock) : client_socket_{sock} { } ~Session() { std::cout << "[Session] Closed\n"; } void SetClearWindowCallback(std::function<void()> cb) { clear_window_callback_ = std::move(cb); } void SetAuthResultCallback(std::function<void(bool, const std::string &)> cb) { auth_result_callback_ = std::move(cb); } void SetRoomPlayersCallback(RoomPlayersCallback cb) { room_players_cb_ = std::move(cb); } void SetCurrentRoom(const std::string &name) { current_room_ = name; } const std::string &GetCurrentRoom() const { return current_room_; } void SetSuppressAutoGame(bool v) { suppress_auto_game_ = v; } bool GetSuppressAutoGame() const { return suppress_auto_game_; } void SetTopScoresCallback(TopScoresCallback cb) { top_scores_callback_ = std::move(cb); } void SendAuthData(const std::string &login, const std::string &password) { std::cout << "[Session] Sending auth data: " << login << "/" << password << std::endl; auto auth = std::make_shared<AuthPayload>(login, password); MsgProtocol msg{WINDOWS::AUTH, auth}; std::vector<char> send_data = msg.GetRawPayload(); std::vector<char> recv_data; CliServIter(send_data, recv_data); bool ok = false; std::string out_msg = "No response from server"; try { MsgProtocol resp_msg; resp_msg.CreateFromRawData(recv_data); auto resp_payload = std::dynamic_pointer_cast<AuthPayload>(resp_msg.GetPayload()); if (resp_payload) { switch (resp_payload->GetStatus()) { case STATUS::RET_SUCCESS: ok = true; out_msg = (resp_msg.GetWindowType() == WINDOWS::AUTH) ? "Authorization successful" : "Registration successful"; break; case STATUS::RET_ALREADY_LOGGED_IN: ok = false; out_msg = "User is already logged in on another session!"; break; case STATUS::RET_ERROR: default: ok = false; out_msg = (resp_msg.GetWindowType() == WINDOWS::AUTH) ? "Invalid login or password" : "User already exists"; break; } } else { ok = false; out_msg = "No valid response from server"; } } catch (const std::exception &e) { out_msg = std::string("Protocol error: ") + e.what(); } if (auth_result_callback_) auth_result_callback_(ok, out_msg); if (ok) { current_user_ = login; if (clear_window_callback_) clear_window_callback_(); } } void SendRegisterData(const std::string &login, const std::string &password) { std::cout << "[Session] Sending register data: " << login << "/" << password << std::endl; auto reg = std::make_shared<AuthPayload>(login, password); MsgProtocol msg{WINDOWS::REGISTER, reg}; std::vector<char> send_data = msg.GetRawPayload(); std::vector<char> recv_data; CliServIter(send_data, recv_data); bool ok = false; std::string out_msg = "No response from server"; try { MsgProtocol resp_msg; resp_msg.CreateFromRawData(recv_data); auto resp_payload = std::dynamic_pointer_cast<AuthPayload>(resp_msg.GetPayload()); if (resp_payload && resp_payload->GetStatus() == STATUS::RET_SUCCESS) { ok = true; out_msg = "Registration successful"; } else { out_msg = "User already exists"; } } catch (const std::exception &e) { out_msg = std::string("Protocol error: ") + e.what(); } if (auth_result_callback_) auth_result_callback_(ok, out_msg); if (ok) { current_user_ = login; if (clear_window_callback_) clear_window_callback_(); } } const std::string &GetCurrentUser() const { return current_user_; } void SetCurrentUser(const std::string &login) { current_user_ = login; } void SetRoomsCallback(RoomsCallback cb) { rooms_cb_ = std::move(cb); } void SetRoomCreateCallback(RoomCreateCallback cb) { room_create_cb_ = std::move(cb); } void SetChatCallback(ChatCallback cb) { chat_cb_ = std::move(cb); } void RequestRooms() { auto p = RoomsPayload::ListRequest(); MsgProtocol msg{WINDOWS::ROOMS, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto r = std::dynamic_pointer_cast<RoomsPayload>(resp.GetPayload()); if (r && r->GetAction() == ROOM_ACTION::LIST_ROOMS) { if (rooms_cb_) rooms_cb_(r->GetRooms()); } } catch (const std::exception &e) { std::cerr << "Rooms list error: " << e.what() << "\n"; } } void CreateRoom(const std::string &name) { auto p = RoomsPayload::CreateRequest(name); MsgProtocol msg{WINDOWS::ROOMS, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); bool ok = false; std::string out = "Failed to create room"; try { MsgProtocol resp; resp.CreateFromRawData(recv); auto r = std::dynamic_pointer_cast<RoomsPayload>(resp.GetPayload()); if (r && r->GetAction() == ROOM_ACTION::CREATE_ROOM) { ok = (r->GetStatus() == STATUS::RET_SUCCESS); out = ok ? "Room created" : "Room already exists"; } } catch (const std::exception &e) { out = std::string("Protocol error: ") + e.what(); } if (room_create_cb_) room_create_cb_(ok, out); if (ok && rooms_cb_) RequestRooms(); // обновим список } void DeleteRoom(const std::string &name) { auto p = RoomsPayload::DeleteRequest(name); MsgProtocol msg{WINDOWS::ROOMS, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); bool ok = false; std::string out = "Failed to delete room"; try { MsgProtocol resp; resp.CreateFromRawData(recv); auto r = std::dynamic_pointer_cast<RoomsPayload>(resp.GetPayload()); if (r && r->GetAction() == ROOM_ACTION::DELETE_ROOM) { ok = (r->GetStatus() == STATUS::RET_SUCCESS); out = ok ? "Room deleted" : "Room is not empty or not found"; } } catch (const std::exception &e) { out = std::string("Protocol error: ") + e.what(); } if (room_create_cb_) room_create_cb_(ok, out); if (ok && rooms_cb_) RequestRooms(); } STATUS JoinRoom(const std::string &name) { auto p = RoomsPayload::JoinRequest(name); MsgProtocol msg{WINDOWS::ROOMS, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); STATUS st = STATUS::RET_ERROR; try { MsgProtocol resp; resp.CreateFromRawData(recv); auto r = std::dynamic_pointer_cast<RoomsPayload>(resp.GetPayload()); if (r && r->GetAction() == ROOM_ACTION::JOIN_ROOM) { st = r->GetStatus(); } } catch (const std::exception &e) { std::cerr << "JoinRoom error: " << e.what() << "\n"; st = STATUS::RET_ERROR; } return st; } void LeaveRoom(const std::string &name) { auto p = RoomsPayload::LeaveRequest(name); MsgProtocol msg{WINDOWS::ROOMS, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); } void RequestRoomPlayers(const std::string &name) { auto p = RoomsPayload::ListPlayersRequest(name); MsgProtocol msg{WINDOWS::ROOMS, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto r = std::dynamic_pointer_cast<RoomsPayload>(resp.GetPayload()); if (r && r->GetAction() == ROOM_ACTION::LIST_PLAYERS) { if (room_players_cb_) { room_players_cb_(r->GetRoomName(), r->GetPlayers()); } } } catch (const std::exception &e) { std::cerr << "Room players error: " << e.what() << "\n"; } } // Глобальный чат (лобби) void RequestChatHistory() { auto p = ChatPayload::HistoryRequest(""); // "" => глобальный чат MsgProtocol msg{WINDOWS::CHAT, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto cp = std::dynamic_pointer_cast<ChatPayload>(resp.GetPayload()); if (cp && cp->GetAction() == CHAT_ACTION::HISTORY) { if (chat_cb_) chat_cb_(cp->GetMessages()); } } catch (const std::exception &e) { std::cerr << "Chat history error: " << e.what() << "\n"; } } void RequestTopScores(uint16_t limit) { auto p = ScoresPayload::TopRequest(limit); MsgProtocol msg{WINDOWS::SCORES, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto sp = std::dynamic_pointer_cast<ScoresPayload>(resp.GetPayload()); if (sp && top_scores_callback_) { top_scores_callback_(sp->GetScores()); } } catch (const std::exception &e) { std::cerr << "TopScores error: " << e.what() << "\n"; } } uint8_t RequestMyStars() { uint8_t stars = 0; // limit == 0 — наш специальный запрос "мои звёзды" auto p = ScoresPayload::TopRequest(0); MsgProtocol msg{WINDOWS::SCORES, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto sp = std::dynamic_pointer_cast<ScoresPayload>(resp.GetPayload()); if (sp) { const auto& list = sp->GetScores(); if (!list.empty()) { // score в этой записи = число звёзд int s = list[0].score; if (s < 0) s = 0; if (s > 3) s = 3; stars = static_cast<uint8_t>(s); } } } catch (const std::exception& e) { std::cerr << "RequestMyStars error: " << e.what() << "\n"; } return stars; } void SendChatMessage(const std::string &text) { if (text.empty()) return; auto p = ChatPayload::SendRequest(text, ""); // глобальный чат MsgProtocol msg{WINDOWS::CHAT, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto cp = std::dynamic_pointer_cast<ChatPayload>(resp.GetPayload()); if (cp && cp->GetAction() == CHAT_ACTION::HISTORY) { if (chat_cb_) chat_cb_(cp->GetMessages()); } } catch (const std::exception &e) { std::cerr << "SendChatMessage error: " << e.what() << "\n"; } } // ----------- НОВОЕ: чат внутри комнаты ------------ void RequestRoomChatHistory(const std::string &room) { auto p = ChatPayload::HistoryRequest(room); MsgProtocol msg{WINDOWS::CHAT, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto cp = std::dynamic_pointer_cast<ChatPayload>(resp.GetPayload()); if (cp && cp->GetAction() == CHAT_ACTION::HISTORY) { if (chat_cb_) chat_cb_(cp->GetMessages()); } } catch (const std::exception &e) { std::cerr << "Room chat history error: " << e.what() << "\n"; } } void SendRoomChatMessage(const std::string &room, const std::string &text) { if (room.empty() || text.empty()) return; auto p = ChatPayload::SendRequest(text, room); MsgProtocol msg{WINDOWS::CHAT, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto cp = std::dynamic_pointer_cast<ChatPayload>(resp.GetPayload()); if (cp && cp->GetAction() == CHAT_ACTION::HISTORY) { if (chat_cb_) chat_cb_(cp->GetMessages()); } } catch (const std::exception &e) { std::cerr << "SendRoomChatMessage error: " << e.what() << "\n"; } } // ------------------ Game ------------------ struct GameStateResponse { STATUS status = STATUS::RET_ERROR; uint8_t spawnIndex = 0; uint8_t countdown = 255; // 255 = waiting players std::vector<PlayerStateDTO> players; // НОВОЕ }; STATUS StartGame(const std::string &room) { auto p = GamePayload::StartRequest(room); MsgProtocol msg{WINDOWS::GAME, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); STATUS st = STATUS::RET_ERROR; try { MsgProtocol resp; resp.CreateFromRawData(recv); auto gp = std::dynamic_pointer_cast<GamePayload>(resp.GetPayload()); if (gp) { st = gp->GetStatus(); } } catch (const std::exception &e) { std::cerr << "StartGame error: " << e.what() << "\n"; } return st; } Session::GameStateResponse QueryGameState(const std::string &room) { GameStateResponse res; auto p = GamePayload::QueryStateRequest(room); MsgProtocol msg{WINDOWS::GAME, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto gp = std::dynamic_pointer_cast<GamePayload>(resp.GetPayload()); if (gp) { res.status = gp->GetStatus(); res.spawnIndex = gp->GetSpawnIndex(); res.countdown = gp->GetCountdown(); res.players = gp->GetPlayers(); // ← НОВОЕ } } catch (const std::exception &e) { std::cerr << "QueryGameState error: " << e.what() << "\n"; } return res; } void LeaveGame(const std::string &room) { auto p = GamePayload::LeaveRequest(room); MsgProtocol msg{WINDOWS::GAME, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); } void SendGameReady(const std::string &room) { auto p = GamePayload::ReadyRequest(room); MsgProtocol msg{WINDOWS::GAME, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); } GameStateResponse SendGameMove(const std::string &room, MOVE_DIR dir) { GameStateResponse res; auto p = GamePayload::MoveRequest(room, static_cast<uint8_t>(dir)); MsgProtocol msg{WINDOWS::GAME, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto gp = std::dynamic_pointer_cast<GamePayload>(resp.GetPayload()); if (gp) { res.status = gp->GetStatus(); res.spawnIndex = gp->GetSpawnIndex(); res.countdown = gp->GetCountdown(); res.players = gp->GetPlayers(); } } catch (const std::exception &e) { std::cerr << "SendGameMove error: " << e.what() << "\n"; } return res; } GameStateResponse SendGamePush(const std::string &room) { GameStateResponse res; auto p = GamePayload::PushRequest(room); MsgProtocol msg{WINDOWS::GAME, p}; auto send_data = msg.GetRawPayload(); std::vector<char> recv; CliServIter(send_data, recv); try { MsgProtocol resp; resp.CreateFromRawData(recv); auto gp = std::dynamic_pointer_cast<GamePayload>(resp.GetPayload()); if (gp) { res.status = gp->GetStatus(); res.spawnIndex = gp->GetSpawnIndex(); res.countdown = gp->GetCountdown(); res.players = gp->GetPlayers(); } } catch (const std::exception &e) { std::cerr << "SendGamePush error: " << e.what() << "\n"; } return res; } private: SOCKET client_socket_; std::function<void()> clear_window_callback_; std::function<void(bool, const std::string &)> auth_result_callback_; RoomsCallback rooms_cb_; RoomCreateCallback room_create_cb_; RoomPlayersCallback room_players_cb_; TopScoresCallback top_scores_callback_; ChatCallback chat_cb_; std::string current_user_; std::string current_room_; bool suppress_auto_game_ = false; void CliServIter(const std::vector<char> &data_to_send, std::vector<char> &data_to_recv) { SendData(data_to_send); RecvData(data_to_recv); } int SendData(const std::vector<char> &data) { int sent = 0; while (sent < (int) data.size()) { int chunk = send(client_socket_, data.data() + sent, (int) data.size() - sent, 0); if (chunk <= 0) break; sent += chunk; } return sent; } int RecvAll(SOCKET s, char *buf, int len) { int total = 0; while (total < len) { int bytes = recv(s, buf + total, len - total, 0); if (bytes <= 0) return -1; // ошибка или соединение закрыто total += bytes; } return total; } int RecvData(std::vector<char> &data) { data.clear(); char header[6]; // читаем 6 байт заголовка полностью if (RecvAll(client_socket_, header, sizeof(header)) != sizeof(header)) return -1; uint32_t payload_size; std::memcpy(&payload_size, &header[2], sizeof(payload_size)); data.insert(data.end(), header, header + 6); // читаем payload полностью std::vector<char> payload(payload_size); if (RecvAll(client_socket_, payload.data(), payload_size) != payload_size) return -1; data.insert(data.end(), payload.begin(), payload.end()); return (int) data.size(); } static inline void Trim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); } static inline bool StartsWith(const std::string &s, const std::string &prefix) { return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); } }; #endif //SESSION_H