#pragma once #include #include #include #include #include #include #include using json = nlohmann::json; struct PerSocketData { std::string player_id; std::string nickname; std::string room_id; // пусто, если игрок в лобби, а не в комнате }; using WS = uWS::WebSocket; class Game; struct RoomPlayer { std::string player_id; std::string nickname; WS *ws; bool ready = false; }; struct Room { std::string id; std::string mode; // "coop" | "pvp" std::string state = "waiting"; // waiting | playing | finished std::vector players; }; // Лобби и реестр комнат (этап 3 из TZ.md). Игровая логика — этапы 4-5. class Lobby { public: Lobby(); ~Lobby(); void onOpen(WS *ws); void onMessage(WS *ws, std::string_view message); void onClose(WS *ws); private: static constexpr size_t kMaxPlayersPerRoom = 2; // Верхняя граница на общее число комнат — без неё клиент, создающий и не // покидающий комнаты, мог бы разогнать rooms_ до исчерпания памяти. static constexpr size_t kMaxRooms = 200; std::unordered_map rooms_; std::unordered_map> games_; std::unordered_set sockets_; int next_player_id_ = 1; int next_room_id_ = 1; void handleHello(WS *ws, const json &payload); void handleListRooms(WS *ws); void handleCreateRoom(WS *ws, const json &payload); void handleJoinRoom(WS *ws, const json &payload); void handleLeaveRoom(WS *ws); void handleGameReady(WS *ws); void handleGameInput(WS *ws, const json &payload); void removePlayerFromRoom(WS *ws); void maybeStartGame(const std::string &room_id); void endActiveGameIfAny(const std::string &room_id, WS *leavingWs); void broadcastRoomsToLobby(); void broadcastRoomUpdated(const Room &room, WS *exclude = nullptr); json roomsSummary() const; json roomPlayers(const Room &room) const; std::string generateRoomId(); static void send(WS *ws, const std::string &type, json payload); static void sendError(WS *ws, const std::string &code, const std::string &message); };