/
yaseeeeechka
/
CenterControl
Обзор
Документация
Войти
/
yaseeeeechka
/
CenterControl
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
CenterControl_UDP/include/client/Session.h
591 строка
20 KB
Yaroslava
Add UDP impl
11 дек 2025, 21:44
11 дек 2025, 21:44
b29a632
Код
Авторство
О чём код?
#ifndef SESSION_H #define SESSION_H #include <functional> #include <string> #include <iostream> #include <vector> #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_UDP\\fonts\\arial\\arial.ttf"; #else const std::string kFontPath = "/home/user/dir/programming/C++/Yaroslava/CenterControl/fonts/arial/arial.ttf"; #endif // просто потолок для размера одного UDP-пакета constexpr size_t kUdpMaxPacketSize = 4096; // ==================== 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); } // =============== AUTH / REGISTER ================= 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; } // =============== ROOMS ================= void SetRoomsCallback(RoomsCallback cb) { rooms_cb_ = std::move(cb); } void SetRoomCreateCallback(RoomCreateCallback cb) { room_create_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"; } } // =============== CHAT ================= void SetChatCallback(ChatCallback cb) { chat_cb_ = std::move(cb); } // Глобальный чат (лобби) 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 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"; } } // =============== SCORES ================= 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; 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()) { 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; } // =============== GAME ================= struct GameStateResponse { STATUS status = STATUS::RET_ERROR; uint8_t spawnIndex = 0; uint8_t countdown = 255; 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; } 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) { // для UDP достаточно одного send, но цикл пусть останется — хуже не будет 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; } // ВАЖНО: для UDP читаем ОДИН дейтаграмм целиком и отдаём MsgProtocol'у int RecvData(std::vector<char> &data) { data.clear(); std::vector<char> buf(kUdpMaxPacketSize); int received = recv(client_socket_, buf.data(), (int)buf.size(), 0); if (received <= 0) { return -1; } buf.resize(received); data = std::move(buf); return received; } }; #endif //SESSION_H