Synchronize on network game start, so that everyone has received all avatars and player names before the game starts. Store all computer players in a separate list on the server for name retrieval.

This commit is contained in:
lotodore
2007-10-07 21:23:37 +00:00
parent d16312111f
commit 0d0d8dc810
18 changed files with 452 additions and 64 deletions
+99 -36
View File
@@ -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<NetPacket> retrieveAvatar(new NetPacketRetrieveAvatar);
NetPacketRetrieveAvatar::Data retrieveAvatarData;
retrieveAvatarData.requestId = infoData.playerId;
retrieveAvatarData.avatar = infoData.playerInfo.avatar;
static_cast<NetPacketRetrieveAvatar *>(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_ptr<Net
{
int retVal = MSG_SOCK_INTERNAL_PENDING;
if (packet->ToNetPacketGameStart())
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<PlayerData> 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<Net
//-----------------------------------------------------------------------------
ClientStateSynchronizeStart &
ClientStateSynchronizeStart::Instance()
{
static ClientStateSynchronizeStart state;
return state;
}
ClientStateSynchronizeStart::ClientStateSynchronizeStart()
{
}
ClientStateSynchronizeStart::~ClientStateSynchronizeStart()
{
}
int
ClientStateSynchronizeStart::Process(ClientThread &client)
{
int retVal = AbstractClientStateReceiving::Process(client);
if (client.IsSynchronized())
{
boost::shared_ptr<NetPacket> startAck(new NetPacketStartEventAck);
client.GetSender().Send(client.GetContext().GetSocket(), startAck);
client.SetState(ClientStateWaitStart::Instance());
}
return retVal;
}
int
ClientStateSynchronizeStart::InternalProcess(ClientThread &client, boost::shared_ptr<NetPacket> 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<NetPacket> 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<PlayerData> 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()
{
+25 -2
View File
@@ -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<NetPacket> retrieveAvatar(new NetPacketRetrieveAvatar);
NetPacketRetrieveAvatar::Data retrieveAvatarData;
retrieveAvatarData.requestId = id;
retrieveAvatarData.avatar = info.avatar;
static_cast<NetPacketRetrieveAvatar *>(retrieveAvatar.get())->SetData(retrieveAvatarData);
GetSender().Send(GetContext().GetSocket(), retrieveAvatar);
// Insert empty value in list to synchronize waiting.
m_tempAvatarMap[id] = boost::shared_ptr<AvatarData>();
}
// 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());
}
+54 -1
View File
@@ -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<NetPacket>(new NetPacketStartEvent);
break;
case NET_TYPE_START_EVENT_ACK:
tmpPacket = boost::shared_ptr<NetPacket>(new NetPacketStartEventAck);
break;
case NET_TYPE_GAME_START:
tmpPacket = boost::shared_ptr<NetPacket>(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<NetPacket>
NetPacketStartEventAck::Clone() const
{
boost::shared_ptr<NetPacket> 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)
{
+87 -20
View File
@@ -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<NetPacket> 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<PlayerData> 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<PlayerData> 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<NetPacket>(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> 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<NetPacket> 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> ServerGameStateStartGame::Ptr;
ServerGameStateStartGame &
@@ -387,6 +452,8 @@ ServerGameStateStartGame::~ServerGameStateStartGame()
int
ServerGameStateStartGame::Process(ServerGameThread &server)
{
server.InternalStartGame();
boost::shared_ptr<NetPacket> answer(new NetPacketGameStart);
NetPacketGameStart::Data gameStartData;
+16 -2
View File
@@ -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<PlayerData> 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();
}
+21
View File
@@ -176,6 +176,20 @@ ServerLobbyThread::HandleGameRetrieveAvatar(SessionWrapper session, const NetPac
HandleNetPacketRetrieveAvatar(session, tmpPacket);
}
void
ServerLobbyThread::AddComputerPlayer(boost::shared_ptr<PlayerData> player)
{
boost::mutex::scoped_lock lock(m_computerPlayersMutex);
m_computerPlayers.insert(PlayerDataMap::value_type(player->GetUniqueId(), player));
}
void
ServerLobbyThread::RemoveComputerPlayer(boost::shared_ptr<PlayerData> 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<PlayerData> 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())
{
+22 -1
View File
@@ -20,7 +20,7 @@
#include <net/sessiondata.h>
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
{
+18
View File
@@ -292,6 +292,24 @@ SessionManager::ForEach(boost::function<void (SessionWrapper)> 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()
{