diff --git a/docs/net_protocol.txt b/docs/net_protocol.txt index 651d9549..40ecec0a 100644 --- a/docs/net_protocol.txt +++ b/docs/net_protocol.txt @@ -427,7 +427,7 @@ Client Request: Leave Current Game (will be answered with a Server Notification: Removed From Game) -Client Request: Start Event +Client Request/Server Request: Start Event 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -436,10 +436,23 @@ Client Request: Start Event +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Start Flags | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +(sent by one client, repeated to everyone by the server) Start Flags: 0x01 set: Fill up with computer opponents. +Client Reply: Start Event ACK + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Message Type = 67 | Message Length = 8 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +(every client must confirm the start event) + + Server Notification: Game Start 0 1 2 3 diff --git a/src/net/clientstate.h b/src/net/clientstate.h index 0a49ba43..b13269a7 100644 --- a/src/net/clientstate.h +++ b/src/net/clientstate.h @@ -234,6 +234,41 @@ protected: virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet); }; +// State: Synchronize on game start. +class ClientStateSynchronizeStart : public AbstractClientStateReceiving +{ +public: + // Access the state singleton. + static ClientStateSynchronizeStart &Instance(); + + virtual ~ClientStateSynchronizeStart(); + + virtual int Process(ClientThread &client); +protected: + + // Protected constructor - this is a singleton. + ClientStateSynchronizeStart(); + + virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet); +}; + +// State: Wait for game start. +class ClientStateWaitStart : public AbstractClientStateReceiving +{ +public: + // Access the state singleton. + static ClientStateWaitStart &Instance(); + + virtual ~ClientStateWaitStart(); + +protected: + + // Protected constructor - this is a singleton. + ClientStateWaitStart(); + + virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet); +}; + // State: Wait for start of the next hand. class ClientStateWaitHand : public AbstractClientStateReceiving { diff --git a/src/net/clientthread.h b/src/net/clientthread.h index c43fe903..947e2240 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -86,7 +86,7 @@ protected: bool GetCachedPlayerInfo(unsigned id, PlayerInfo &info) const; void RequestPlayerInfo(unsigned id); - void SetPlayerInfo(unsigned id, const PlayerInfo &info); + void SetPlayerInfo(unsigned id, const PlayerInfo &info, bool retrieveAvatar = true); void SetNewGameAdmin(unsigned id); void AddTempAvatarData(unsigned playerId, unsigned avatarSize, AvatarFileType type); @@ -134,6 +134,8 @@ protected: bool IsSessionEstablished() const; void SetSessionEstablished(bool flag); + bool IsSynchronized() const; + private: NetPacketList m_outPacketList; @@ -177,6 +179,8 @@ friend class ClientStateStartSession; friend class ClientStateWaitSession; friend class ClientStateWaitJoin; friend class ClientStateWaitGame; +friend class ClientStateSynchronizeStart; +friend class ClientStateWaitStart; friend class ClientStateWaitHand; friend class ClientStateRunHand; friend class ClientStateFinal; diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index 00241ce8..f8129b39 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -395,16 +395,6 @@ AbstractClientStateReceiving::Process(ClientThread &client) NetPacketPlayerInfo::Data infoData; tmpPacket->ToNetPacketPlayerInfo()->GetData(infoData); client.SetPlayerInfo(infoData.playerId, infoData.playerInfo); - // Retrieve avatar if needed. - if (infoData.playerInfo.hasAvatar && !client.GetAvatarManager().HasAvatar(infoData.playerInfo.avatar)) - { - boost::shared_ptr retrieveAvatar(new NetPacketRetrieveAvatar); - NetPacketRetrieveAvatar::Data retrieveAvatarData; - retrieveAvatarData.requestId = infoData.playerId; - retrieveAvatarData.avatar = infoData.playerInfo.avatar; - static_cast(retrieveAvatar.get())->SetData(retrieveAvatarData); - client.GetSender().Send(client.GetContext().GetSocket(), retrieveAvatar); - } } else if (tmpPacket->ToNetPacketRemovedFromGame()) { @@ -651,33 +641,9 @@ ClientStateWaitGame::InternalProcess(ClientThread &client, boost::shared_ptrToNetPacketGameStart()) + if (packet->ToNetPacketStartEvent()) { - // Start the network game as client. - NetPacketGameStart::Data gameStartData; - packet->ToNetPacketGameStart()->GetData(gameStartData); - - client.SetStartData(gameStartData.startData); - - // Set player numbers using the game start data slots. - NetPacketGameStart::PlayerSlotList::const_iterator slot_i = gameStartData.playerSlots.begin(); - NetPacketGameStart::PlayerSlotList::const_iterator slot_end = gameStartData.playerSlots.end(); - int num = 0; - - while (slot_i != slot_end) - { - unsigned playerId = (*slot_i).playerId; - boost::shared_ptr tmpPlayer = client.GetPlayerDataByUniqueId(playerId); - if (!tmpPlayer.get()) - throw ClientException(ERR_NET_UNKNOWN_PLAYER_ID, 0); - tmpPlayer->SetNumber(num); - - ++num; - ++slot_i; - } - - client.SetState(ClientStateWaitHand::Instance()); - retVal = MSG_NET_GAME_CLIENT_START; + client.SetState(ClientStateSynchronizeStart::Instance()); } else if (packet->ToNetPacketPlayerJoined()) { @@ -719,6 +685,103 @@ ClientStateWaitGame::InternalProcess(ClientThread &client, boost::shared_ptr startAck(new NetPacketStartEventAck); + client.GetSender().Send(client.GetContext().GetSocket(), startAck); + client.SetState(ClientStateWaitStart::Instance()); + } + + return retVal; +} + +int +ClientStateSynchronizeStart::InternalProcess(ClientThread &client, boost::shared_ptr packet) +{ + int retVal = MSG_SOCK_INTERNAL_PENDING; + + if (packet->ToNetPacketGameStart()) + throw ClientException(ERR_NET_START_TIMEOUT, 0); + + return retVal; +} + +//----------------------------------------------------------------------------- + +ClientStateWaitStart & +ClientStateWaitStart::Instance() +{ + static ClientStateWaitStart state; + return state; +} + +ClientStateWaitStart::ClientStateWaitStart() +{ +} + +ClientStateWaitStart::~ClientStateWaitStart() +{ +} + +int +ClientStateWaitStart::InternalProcess(ClientThread &client, boost::shared_ptr packet) +{ + int retVal = MSG_SOCK_INTERNAL_PENDING; + + if (packet->ToNetPacketGameStart()) + { + // Start the network game as client. + NetPacketGameStart::Data gameStartData; + packet->ToNetPacketGameStart()->GetData(gameStartData); + + client.SetStartData(gameStartData.startData); + + // Set player numbers using the game start data slots. + NetPacketGameStart::PlayerSlotList::const_iterator slot_i = gameStartData.playerSlots.begin(); + NetPacketGameStart::PlayerSlotList::const_iterator slot_end = gameStartData.playerSlots.end(); + int num = 0; + + while (slot_i != slot_end) + { + unsigned playerId = (*slot_i).playerId; + boost::shared_ptr tmpPlayer = client.GetPlayerDataByUniqueId(playerId); + if (!tmpPlayer.get()) + throw ClientException(ERR_NET_UNKNOWN_PLAYER_ID, 0); + tmpPlayer->SetNumber(num); + + ++num; + ++slot_i; + } + + client.SetState(ClientStateWaitHand::Instance()); + retVal = MSG_NET_GAME_CLIENT_START; + } + + return retVal; +} + +//----------------------------------------------------------------------------- + ClientStateWaitHand & ClientStateWaitHand::Instance() { diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index 7177d0b0..05596fe8 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -381,7 +381,7 @@ ClientThread::RequestPlayerInfo(unsigned id) } void -ClientThread::SetPlayerInfo(unsigned id, const PlayerInfo &info) +ClientThread::SetPlayerInfo(unsigned id, const PlayerInfo &info, bool retrieveAvatar) { { boost::mutex::scoped_lock lock(m_playerInfoMapMutex); @@ -404,6 +404,23 @@ ClientThread::SetPlayerInfo(unsigned id, const PlayerInfo &info) } } + // Retrieve avatar if needed. + if (retrieveAvatar && info.hasAvatar && !GetAvatarManager().HasAvatar(info.avatar)) + { + boost::shared_ptr retrieveAvatar(new NetPacketRetrieveAvatar); + NetPacketRetrieveAvatar::Data retrieveAvatarData; + retrieveAvatarData.requestId = id; + retrieveAvatarData.avatar = info.avatar; + static_cast(retrieveAvatar.get())->SetData(retrieveAvatarData); + GetSender().Send(GetContext().GetSocket(), retrieveAvatar); + + // Insert empty value in list to synchronize waiting. + m_tempAvatarMap[id] = boost::shared_ptr(); + } + + // Remove from request list. + m_playerInfoRequestList.remove(id); + // Notify GUI GetCallback().SignalNetClientPlayerChanged(id, info.playerName); @@ -464,7 +481,7 @@ ClientThread::CompleteTempAvatarData(unsigned playerId) m_tempAvatarMap.erase(pos); // Update player info, but never re-request avatar. - SetPlayerInfo(playerId, tmpPlayerInfo); + SetPlayerInfo(playerId, tmpPlayerInfo, false); } const ClientContext & @@ -822,3 +839,9 @@ ClientThread::SetSessionEstablished(bool flag) m_sessionEstablished = flag; } +bool +ClientThread::IsSynchronized() const +{ + return (m_playerInfoRequestList.empty() && m_tempAvatarMap.empty()); +} + diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp index f04a9a07..1db4ef4a 100644 --- a/src/net/common/netpacket.cpp +++ b/src/net/common/netpacket.cpp @@ -49,6 +49,7 @@ using namespace std; #define NET_TYPE_KICK_PLAYER 0x0040 #define NET_TYPE_LEAVE_CURRENT_GAME 0x0041 #define NET_TYPE_START_EVENT 0x0042 +#define NET_TYPE_START_EVENT_ACK 0x0043 #define NET_TYPE_GAME_START 0x0050 #define NET_TYPE_HAND_START 0x0051 #define NET_TYPE_PLAYERS_TURN 0x0052 @@ -297,6 +298,12 @@ struct GCC_PACKED NetPacketStartEventData u_int16_t reserved; }; +struct GCC_PACKED NetPacketStartEventAckData +{ + NetPacketHeader head; + u_int32_t reserved; +}; + struct GCC_PACKED NetPacketGameStartData { NetPacketHeader head; @@ -646,6 +653,9 @@ NetPacket::Create(char *data, unsigned &dataSize) case NET_TYPE_START_EVENT: tmpPacket = boost::shared_ptr(new NetPacketStartEvent); break; + case NET_TYPE_START_EVENT_ACK: + tmpPacket = boost::shared_ptr(new NetPacketStartEventAck); + break; case NET_TYPE_GAME_START: tmpPacket = boost::shared_ptr(new NetPacketGameStart); break; @@ -913,6 +923,12 @@ NetPacket::ToNetPacketStartEvent() const return NULL; } +const NetPacketStartEventAck * +NetPacket::ToNetPacketStartEventAck() const +{ + return NULL; +} + const NetPacketGameStart * NetPacket::ToNetPacketGameStart() const { @@ -2641,7 +2657,7 @@ NetPacketKickPlayer::InternalCheck(const NetPacketHeader*) const //----------------------------------------------------------------------------- NetPacketLeaveCurrentGame::NetPacketLeaveCurrentGame() -: NetPacket(NET_TYPE_LEAVE_CURRENT_GAME, sizeof(NetPacketLeaveCurrentGame), sizeof(NetPacketLeaveCurrentGame)) +: NetPacket(NET_TYPE_LEAVE_CURRENT_GAME, sizeof(NetPacketLeaveCurrentGameData), sizeof(NetPacketLeaveCurrentGameData)) { } @@ -2735,6 +2751,43 @@ NetPacketStartEvent::InternalCheck(const NetPacketHeader*) const //----------------------------------------------------------------------------- +NetPacketStartEventAck::NetPacketStartEventAck() +: NetPacket(NET_TYPE_START_EVENT_ACK, sizeof(NetPacketStartEventAckData), sizeof(NetPacketStartEventAckData)) +{ +} + +NetPacketStartEventAck::~NetPacketStartEventAck() +{ +} + +boost::shared_ptr +NetPacketStartEventAck::Clone() const +{ + boost::shared_ptr newPacket(new NetPacketStartEventAck); + try + { + newPacket->SetRawData(GetRawData()); + } catch (const NetException &) + { + // Need to return the new packet anyway. + } + return newPacket; +} + +const NetPacketStartEventAck * +NetPacketStartEventAck::ToNetPacketStartEventAck() const +{ + return this; +} + +void +NetPacketStartEventAck::InternalCheck(const NetPacketHeader*) const +{ + // Nothing to do. +} + +//----------------------------------------------------------------------------- + NetPacketGameStart::NetPacketGameStart() : NetPacket(NET_TYPE_GAME_START, sizeof(NetPacketGameStartData), MAX_PACKET_SIZE) { diff --git a/src/net/common/servergamestate.cpp b/src/net/common/servergamestate.cpp index 71f80587..d14aea79 100644 --- a/src/net/common/servergamestate.cpp +++ b/src/net/common/servergamestate.cpp @@ -46,6 +46,7 @@ using namespace std; #define SERVER_SHOW_CARDS_DELAY_SEC 2 #define SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC 2 #define SERVER_COMPUTER_ACTION_DELAY_SEC 2 +#define SERVER_START_GAME_TIMEOUT_SEC 10 #define SERVER_COMPUTER_PLAYER_NAME "Computer" @@ -286,35 +287,41 @@ ServerGameStateInit::HandleNewSession(ServerGameThread &server, SessionWrapper s int ServerGameStateInit::InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet) { - int retVal = MSG_SOCK_INIT_DONE; + int retVal = MSG_SOCK_INTERNAL_PENDING; if (packet->ToNetPacketStartEvent()) { - NetPacketStartEvent::Data startData; - packet->ToNetPacketStartEvent()->GetData(startData); - - server.ResetComputerPlayerList(); - - if (startData.fillUpWithCpuPlayers) + // Only admins are allowed to start the game. + if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN) { - int remainingSlots = server.GetGameData().maxNumberOfPlayers - server.GetCurNumberOfPlayers(); - for (int i = 1; i <= remainingSlots; i++) + NetPacketStartEvent::Data startData; + packet->ToNetPacketStartEvent()->GetData(startData); + + // Fill up with computer players. + server.ResetComputerPlayerList(); + + if (startData.fillUpWithCpuPlayers) { - boost::shared_ptr tmpPlayerData( - new PlayerData(server.GetLobbyThread().GetNextUniquePlayerId(), 0, PLAYER_TYPE_COMPUTER, PLAYER_RIGHTS_NORMAL)); + int remainingSlots = server.GetGameData().maxNumberOfPlayers - server.GetCurNumberOfPlayers(); + for (int i = 1; i <= remainingSlots; i++) + { + boost::shared_ptr tmpPlayerData( + new PlayerData(server.GetLobbyThread().GetNextUniquePlayerId(), 0, PLAYER_TYPE_COMPUTER, PLAYER_RIGHTS_NORMAL)); - ostringstream name; - name << SERVER_COMPUTER_PLAYER_NAME << i; - tmpPlayerData->SetName(name.str()); - server.AddComputerPlayer(tmpPlayerData); + ostringstream name; + name << SERVER_COMPUTER_PLAYER_NAME << i; + tmpPlayerData->SetName(name.str()); + server.AddComputerPlayer(tmpPlayerData); - // Send "Player Joined" to other fully connected clients. - server.SendToAllPlayers(CreateNetPacketPlayerJoined(*tmpPlayerData), SessionData::Game); + // Send "Player Joined" to other fully connected clients. + server.SendToAllPlayers(CreateNetPacketPlayerJoined(*tmpPlayerData), SessionData::Game); + } } - } + // Wait for all players to confirm start of game. + server.SendToAllPlayers(boost::shared_ptr(packet->Clone()), SessionData::Game); - server.InternalStartGame(); - server.SetState(SERVER_START_GAME_STATE::Instance()); + server.SetState(ServerGameStateWaitAck::Instance()); + } } else if (packet->ToNetPacketKickPlayer()) { @@ -365,6 +372,64 @@ AbstractServerGameStateRunning::HandleNewSession(ServerGameThread &server, Sessi //----------------------------------------------------------------------------- +boost::thread_specific_ptr ServerGameStateWaitAck::Ptr; + +ServerGameStateWaitAck & +ServerGameStateWaitAck::Instance() +{ + if (!Ptr.get()) + Ptr.reset(new ServerGameStateWaitAck); + + return *Ptr; +} + +ServerGameStateWaitAck::ServerGameStateWaitAck() +{ +} + +ServerGameStateWaitAck::~ServerGameStateWaitAck() +{ +} + +int +ServerGameStateWaitAck::Process(ServerGameThread &server) +{ + int retVal; + + if (GetTimer().elapsed().total_seconds() >= SERVER_START_GAME_TIMEOUT_SEC) + { + // On timeout: start anyway. + server.SetState(SERVER_START_GAME_STATE::Instance()); + retVal = MSG_SOCK_INIT_DONE; + } + else + retVal = AbstractServerGameStateReceiving::Process(server); + + return retVal; +} + +int +ServerGameStateWaitAck::InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet) +{ + int retVal = MSG_SOCK_INTERNAL_PENDING; + + if (packet->ToNetPacketStartEventAck()) + { + session.sessionData->SetReadyFlag(); + if (server.GetSessionManager().CountReadySessions() == server.GetSessionManager().GetRawSessionCount()) + { + // Everyone is ready. + server.SetState(SERVER_START_GAME_STATE::Instance()); + retVal = MSG_SOCK_INIT_DONE; + } + retVal = MSG_SOCK_INIT_DONE; + } + + return retVal; +} + +//----------------------------------------------------------------------------- + boost::thread_specific_ptr ServerGameStateStartGame::Ptr; ServerGameStateStartGame & @@ -387,6 +452,8 @@ ServerGameStateStartGame::~ServerGameStateStartGame() int ServerGameStateStartGame::Process(ServerGameThread &server) { + server.InternalStartGame(); + boost::shared_ptr answer(new NetPacketGameStart); NetPacketGameStart::Data gameStartData; diff --git a/src/net/common/servergamethread.cpp b/src/net/common/servergamethread.cpp index 5dffd923..9d8b7a4d 100644 --- a/src/net/common/servergamethread.cpp +++ b/src/net/common/servergamethread.cpp @@ -152,6 +152,7 @@ ServerGameThread::Main() GetSender().SignalTermination(); GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT); + ResetComputerPlayerList(); GetLobbyThread().RemoveGame(GetId()); } @@ -273,14 +274,27 @@ ServerGameThread::IsRunning() const void ServerGameThread::AddComputerPlayer(boost::shared_ptr player) { - boost::mutex::scoped_lock lock(m_computerPlayerListMutex); - m_computerPlayerList.push_back(player); + { + boost::mutex::scoped_lock lock(m_computerPlayerListMutex); + m_computerPlayerList.push_back(player); + } + GetLobbyThread().AddComputerPlayer(player); } void ServerGameThread::ResetComputerPlayerList() { boost::mutex::scoped_lock lock(m_computerPlayerListMutex); + + PlayerDataList::iterator i = m_computerPlayerList.begin(); + PlayerDataList::iterator end = m_computerPlayerList.end(); + + while (i != end) + { + GetLobbyThread().RemoveComputerPlayer(*i); + ++i; + } + m_computerPlayerList.clear(); } diff --git a/src/net/common/serverlobbythread.cpp b/src/net/common/serverlobbythread.cpp index fa6cba39..66da09c1 100644 --- a/src/net/common/serverlobbythread.cpp +++ b/src/net/common/serverlobbythread.cpp @@ -176,6 +176,20 @@ ServerLobbyThread::HandleGameRetrieveAvatar(SessionWrapper session, const NetPac HandleNetPacketRetrieveAvatar(session, tmpPacket); } +void +ServerLobbyThread::AddComputerPlayer(boost::shared_ptr player) +{ + boost::mutex::scoped_lock lock(m_computerPlayersMutex); + m_computerPlayers.insert(PlayerDataMap::value_type(player->GetUniqueId(), player)); +} + +void +ServerLobbyThread::RemoveComputerPlayer(boost::shared_ptr player) +{ + boost::mutex::scoped_lock lock(m_computerPlayersMutex); + m_computerPlayers.erase(player->GetUniqueId()); +} + void ServerLobbyThread::RemoveGame(unsigned id) { @@ -446,6 +460,13 @@ ServerLobbyThread::HandleNetPacketRetrievePlayerInfo(SessionWrapper session, con boost::shared_ptr tmpPlayer = m_sessionManager.GetSessionByUniquePlayerId(request.playerId).playerData; if (!tmpPlayer.get()) tmpPlayer = m_gameSessionManager.GetSessionByUniquePlayerId(request.playerId).playerData; + if (!tmpPlayer.get()) + { + boost::mutex::scoped_lock lock(m_computerPlayersMutex); + PlayerDataMap::const_iterator pos = m_computerPlayers.find(request.playerId); + if (pos != m_computerPlayers.end()) + tmpPlayer = pos->second; + } if (tmpPlayer.get()) { diff --git a/src/net/common/sessiondata.cpp b/src/net/common/sessiondata.cpp index 36fd4553..d761926d 100644 --- a/src/net/common/sessiondata.cpp +++ b/src/net/common/sessiondata.cpp @@ -20,7 +20,7 @@ #include SessionData::SessionData(SOCKET sockfd, unsigned id) -: m_sockfd(sockfd), m_id(id), m_state(SessionData::Init) +: m_sockfd(sockfd), m_id(id), m_state(SessionData::Init), m_readyFlag(false) { } @@ -58,6 +58,27 @@ SessionData::GetSocket() const return m_sockfd; } +void +SessionData::SetReadyFlag() +{ + boost::mutex::scoped_lock lock(m_dataMutex); + m_readyFlag = true; +} + +void +SessionData::ResetReadyFlag() +{ + boost::mutex::scoped_lock lock(m_dataMutex); + m_readyFlag = false; +} + +bool +SessionData::IsReady() const +{ + boost::mutex::scoped_lock lock(m_dataMutex); + return m_readyFlag; +} + const std::string & SessionData::GetClientAddr() const { diff --git a/src/net/common/sessionmanager.cpp b/src/net/common/sessionmanager.cpp index 33f70420..19e0d7a5 100644 --- a/src/net/common/sessionmanager.cpp +++ b/src/net/common/sessionmanager.cpp @@ -292,6 +292,24 @@ SessionManager::ForEach(boost::function func) } } +unsigned +SessionManager::CountReadySessions() const +{ + unsigned counter = 0; + boost::mutex::scoped_lock lock(m_sessionMapMutex); + + SessionMap::const_iterator i = m_sessionMap.begin(); + SessionMap::const_iterator end = m_sessionMap.end(); + + while (i != end) + { + if ((*i).second.sessionData->IsReady()) + ++counter; + ++i; + } + return counter; +} + void SessionManager::Clear() { diff --git a/src/net/netpacket.h b/src/net/netpacket.h index 79fdf14d..189d7d01 100644 --- a/src/net/netpacket.h +++ b/src/net/netpacket.h @@ -68,6 +68,7 @@ class NetPacketGameAdminChanged; class NetPacketKickPlayer; class NetPacketLeaveCurrentGame; class NetPacketStartEvent; +class NetPacketStartEventAck; class NetPacketGameStart; class NetPacketHandStart; class NetPacketPlayersTurn; @@ -125,6 +126,7 @@ public: virtual const NetPacketKickPlayer *ToNetPacketKickPlayer() const; virtual const NetPacketLeaveCurrentGame *ToNetPacketLeaveCurrentGame() const; virtual const NetPacketStartEvent *ToNetPacketStartEvent() const; + virtual const NetPacketStartEventAck *ToNetPacketStartEventAck() const; virtual const NetPacketGameStart *ToNetPacketGameStart() const; virtual const NetPacketHandStart *ToNetPacketHandStart() const; virtual const NetPacketPlayersTurn *ToNetPacketPlayersTurn() const; @@ -681,6 +683,22 @@ protected: virtual void InternalCheck(const NetPacketHeader* data) const; }; +class NetPacketStartEventAck : public NetPacket +{ +public: + + NetPacketStartEventAck(); + virtual ~NetPacketStartEventAck(); + + virtual boost::shared_ptr Clone() const; + + virtual const NetPacketStartEventAck *ToNetPacketStartEventAck() const; + +protected: + + virtual void InternalCheck(const NetPacketHeader* data) const; +}; + class NetPacketGameStart : public NetPacket { public: diff --git a/src/net/servergamestate.h b/src/net/servergamestate.h index 43e7f85a..4d40b8fa 100644 --- a/src/net/servergamestate.h +++ b/src/net/servergamestate.h @@ -129,6 +129,30 @@ private: static boost::thread_specific_ptr Ptr; }; +// Wait for Ack of start event. +class ServerGameStateWaitAck : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer +{ +public: + // Access the state singleton. + static ServerGameStateWaitAck &Instance(); + + virtual ~ServerGameStateWaitAck(); + + // Timeout if waiting takes too long. + virtual int Process(ServerGameThread &server); + +protected: + + // Protected constructor - this is a singleton. + ServerGameStateWaitAck(); + + virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); + +private: + + static boost::thread_specific_ptr Ptr; +}; + // State: Start server game. class ServerGameStateStartGame : public AbstractServerGameStateRunning { diff --git a/src/net/servergamethread.h b/src/net/servergamethread.h index ea4807e2..c97ba7b9 100644 --- a/src/net/servergamethread.h +++ b/src/net/servergamethread.h @@ -143,6 +143,7 @@ private: friend class AbstractServerGameStateReceiving; friend class AbstractServerGameStateRunning; friend class ServerGameStateInit; +friend class ServerGameStateWaitAck; friend class ServerGameStateStartGame; friend class ServerGameStateStartHand; friend class ServerGameStateStartRound; diff --git a/src/net/serverlobbythread.h b/src/net/serverlobbythread.h index 7221b943..cb9b612b 100644 --- a/src/net/serverlobbythread.h +++ b/src/net/serverlobbythread.h @@ -63,6 +63,9 @@ public: void HandleGameRetrievePlayerInfo(SessionWrapper session, const NetPacketRetrievePlayerInfo &tmpPacket); void HandleGameRetrieveAvatar(SessionWrapper session, const NetPacketRetrieveAvatar &tmpPacket); + void AddComputerPlayer(boost::shared_ptr player); + void RemoveComputerPlayer(boost::shared_ptr player); + void RemoveGame(unsigned id); u_int32_t GetNextUniquePlayerId(); @@ -145,6 +148,9 @@ private: RemoveGameList m_removeGameList; mutable boost::mutex m_removeGameListMutex; + PlayerDataMap m_computerPlayers; + mutable boost::mutex m_computerPlayersMutex; + GameMap m_gameMap; std::auto_ptr m_receiver; diff --git a/src/net/sessiondata.h b/src/net/sessiondata.h index 72ce06f5..9a1400da 100644 --- a/src/net/sessiondata.h +++ b/src/net/sessiondata.h @@ -42,6 +42,10 @@ public: SOCKET GetSocket() const; + void SetReadyFlag(); + void ResetReadyFlag(); + bool IsReady() const; + const std::string &GetClientAddr() const; void SetClientAddr(const std::string &addr); @@ -53,6 +57,7 @@ private: State m_state; std::string m_clientAddr; ReceiveBuffer m_receiveBuffer; + bool m_readyFlag; mutable boost::mutex m_dataMutex; }; diff --git a/src/net/sessionmanager.h b/src/net/sessionmanager.h index 7fda762f..ab6bcfeb 100644 --- a/src/net/sessionmanager.h +++ b/src/net/sessionmanager.h @@ -64,6 +64,7 @@ public: bool IsPlayerConnected(unsigned uniqueId) const; void ForEach(boost::function func); + unsigned CountReadySessions() const; void Clear(); unsigned GetRawSessionCount(); diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index 8b8542e1..0674cd6f 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -61,6 +61,7 @@ #define ERR_NET_BUF_INVALID_SIZE 117 #define ERR_NET_INVALID_REQUEST_ID 118 #define ERR_NET_WRONG_AVATAR_SIZE 119 +#define ERR_NET_START_TIMEOUT 120 #define ERR_IRC_INTERNAL 151 #define ERR_IRC_CONNECT_FAILED 152