(Hopefully) solved the player number problem, when players join and leave an existing game. Numbers are now assigned on game start, not before. Note: Network play is not compatible to the current official version.

This commit is contained in:
lotodore
2007-07-24 14:55:14 +00:00
parent 561b974cdc
commit bd08245c1c
10 changed files with 146 additions and 60 deletions
+25 -11
View File
@@ -1,3 +1,9 @@
Changelog:
07-24-2007: Join Game ACK / Player Joined do not specify player number.
Player order is transferred on game start.
This is not compatible to the previous protocol.
PokerTH general message format:
0 1 2 3
@@ -59,19 +65,15 @@ Server Reply: Join Game ACK
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Your Session ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Your Player ID | Your Player Number |
| Your Player ID | Max Number of Players |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Max Number of Players | Small Blind |
| Small Blind | Hands before raise |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Hands before raise | Proposed GUI Speed |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Player Action Timeout | Reserved |
| Proposed GUI Speed | Player Action Timeout |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Start Money |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Player Number:
0 to (NumberOfPlayers - 1), "position".
Player ID:
Unique Player ID
Proposed GUI Speed:
@@ -87,9 +89,9 @@ Server Notification: Player Joined
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type = 3 | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Player ID | Player Number |
| Player ID | Player Flags |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Player Flags | Name Length |
| Name Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| \
\ Name (UTF-8) /
@@ -105,7 +107,7 @@ Server Notification: Player Left
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type = 4 | Message Length = 8 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Player ID | Reserved |
| Player ID | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -114,10 +116,22 @@ Server Notification: Game Start
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 = 5 | Message Length = 8 |
| Message Type = 5 | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Start Dealer Player Id | Number of Players |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Player ID Slot #1 | Player ID Slot #2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| \
\ Additional Player Slots /
/ |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Player ID Slot #n | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The player slots are the positions of the players on the table. Each slot
contains the ID for the corresponding player. The slots are ordered.
The number of player slots is the number of players.
[ Dealer Pos can be different for each game, therefore it is not
transmitted in Join Game ACK. ]
+3 -3
View File
@@ -77,8 +77,8 @@ protected:
void SetGameData(const GameData &gameData);
const StartData &GetStartData() const;
void SetStartData(const StartData &startData);
int GetGuiPlayerNum() const;
void SetGuiPlayerNum(int guiPlayerNum);
int GetGuiPlayerId() const;
void SetGuiPlayerId(int guiPlayerId);
boost::shared_ptr<Game> GetGame();
@@ -109,7 +109,7 @@ private:
boost::shared_ptr<Game> m_game;
unsigned m_curGameId;
int m_guiPlayerNum;
unsigned m_guiPlayerId;
friend class AbstractClientStateReceiving;
friend class ClientStateInit;
+21 -4
View File
@@ -443,11 +443,12 @@ ClientStateWaitSession::InternalProcess(ClientThread &client, boost::shared_ptr<
NetPacketJoinGameAck::Data joinGameAckData;
packet->ToNetPacketJoinGameAck()->GetData(joinGameAckData);
client.SetGameData(joinGameAckData.gameData);
client.SetGuiPlayerNum(joinGameAckData.yourPlayerNum);
client.SetGuiPlayerId(joinGameAckData.yourPlayerUniqueId);
// TODO: Type Human is fixed here.
// Player number is 0 on join. Will be set when the game starts.
boost::shared_ptr<PlayerData> playerData(
new PlayerData(joinGameAckData.yourPlayerUniqueId, joinGameAckData.yourPlayerNum, PLAYER_TYPE_HUMAN));
new PlayerData(joinGameAckData.yourPlayerUniqueId, 0, PLAYER_TYPE_HUMAN));
playerData->SetName(context.GetPlayerName());
client.AddPlayerData(playerData);
@@ -489,6 +490,23 @@ ClientStateWaitGame::InternalProcess(ClientThread &client, boost::shared_ptr<Net
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;
}
@@ -499,11 +517,10 @@ ClientStateWaitGame::InternalProcess(ClientThread &client, boost::shared_ptr<Net
packet->ToNetPacketPlayerJoined()->GetData(netPlayerData);
boost::shared_ptr<PlayerData> playerData(
new PlayerData(netPlayerData.playerId, netPlayerData.playerNumber, netPlayerData.ptype));
new PlayerData(netPlayerData.playerId, 0, netPlayerData.ptype));
playerData->SetName(netPlayerData.playerName);
client.AddPlayerData(playerData);
}
// TODO: handle error packet (kicked from server)
return retVal;
}
+15 -10
View File
@@ -53,7 +53,7 @@ private:
ClientThread::ClientThread(GuiInterface &gui)
: m_curState(NULL), m_gui(gui), m_curGameId(1), m_guiPlayerNum(0)
: m_curState(NULL), m_gui(gui), m_curGameId(1), m_guiPlayerId(0)
{
m_context.reset(new ClientContext);
m_senderCallback.reset(new ClientSenderCallback(*this));
@@ -244,15 +244,15 @@ ClientThread::SetStartData(const StartData &startData)
}
int
ClientThread::GetGuiPlayerNum() const
ClientThread::GetGuiPlayerId() const
{
return m_guiPlayerNum;
return m_guiPlayerId;
}
void
ClientThread::SetGuiPlayerNum(int guiPlayerNum)
ClientThread::SetGuiPlayerId(int guiPlayerId)
{
m_guiPlayerNum = guiPlayerNum;
m_guiPlayerId = guiPlayerId;
}
boost::shared_ptr<Game>
@@ -303,21 +303,27 @@ ClientThread::RemovePlayerData(unsigned playerId)
void
ClientThread::MapPlayerDataList()
{
// Retrieve the GUI player.
boost::shared_ptr<PlayerData> guiPlayer = GetPlayerDataByUniqueId(GetGuiPlayerId());
assert(guiPlayer.get());
int guiPlayerNum = guiPlayer->GetNumber();
// Create a copy of the player list so that the GUI player
// is player 0. This is mapped because the GUI depends on it.
PlayerDataList mappedList;
PlayerDataList::const_iterator i = m_playerDataList.begin();
PlayerDataList::const_iterator end = m_playerDataList.end();
int numPlayers = GetStartData().numberOfPlayers;
// Create a copy of the player list so that the GUI player
// is player 0. This is mapped because the GUI depends on it.
while (i != end)
{
boost::shared_ptr<PlayerData> tmpData(new PlayerData(*(*i)));
int numberDiff = tmpData->GetNumber() - GetGuiPlayerNum();
int numberDiff = tmpData->GetNumber() - guiPlayerNum;
if (numberDiff >= 0)
tmpData->SetNumber(numberDiff);
else
tmpData->SetNumber(GetStartData().numberOfPlayers + numberDiff);
tmpData->SetNumber(numPlayers + numberDiff);
mappedList.push_back(tmpData);
++i;
}
@@ -326,7 +332,6 @@ ClientThread::MapPlayerDataList()
mappedList.sort(*boost::lambda::_1 < *boost::lambda::_2);
m_playerDataList = mappedList;
SetGuiPlayerNum(0);
}
const PlayerDataList &
+46 -10
View File
@@ -93,13 +93,11 @@ struct GCC_PACKED NetPacketJoinGameAckData
NetPacketHeader head;
u_int32_t sessionId;
u_int16_t playerId;
u_int16_t playerNumber;
u_int16_t maxNumberOfPlayers;
u_int16_t smallBlind;
u_int16_t handsBeforeRaise;
u_int16_t proposedGuiSpeed;
u_int16_t playerActionTimeout;
u_int16_t reserved;
u_int32_t startMoney;
};
@@ -114,9 +112,9 @@ struct GCC_PACKED NetPacketPlayerJoinedData
{
NetPacketHeader head;
u_int16_t playerId;
u_int16_t playerNumber;
u_int16_t playerFlags;
u_int16_t playerNameLength;
u_int16_t reserved;
};
struct GCC_PACKED NetPacketPlayerLeftData
@@ -133,6 +131,11 @@ struct GCC_PACKED NetPacketGameStartData
u_int16_t numberOfPlayers;
};
struct GCC_PACKED PlayerSlotData
{
u_int16_t playerId;
};
struct GCC_PACKED NetPacketHandStartData
{
NetPacketHeader head;
@@ -736,7 +739,6 @@ NetPacketJoinGameAck::SetData(const NetPacketJoinGameAck::Data &inData)
tmpData->sessionId = htonl(inData.sessionId);
tmpData->playerId = htons(inData.yourPlayerUniqueId);
tmpData->playerNumber = htons(inData.yourPlayerNum);
tmpData->maxNumberOfPlayers = htons(inData.gameData.maxNumberOfPlayers);
tmpData->smallBlind = htons(inData.gameData.smallBlind);
tmpData->handsBeforeRaise = htons(inData.gameData.handsBeforeRaise);
@@ -753,7 +755,6 @@ NetPacketJoinGameAck::GetData(NetPacketJoinGameAck::Data &outData) const
outData.sessionId = ntohl(tmpData->sessionId);
outData.yourPlayerUniqueId = ntohs(tmpData->playerId);
outData.yourPlayerNum = ntohs(tmpData->playerNumber);
outData.gameData.maxNumberOfPlayers = ntohs(tmpData->maxNumberOfPlayers);
outData.gameData.smallBlind = ntohs(tmpData->smallBlind);
outData.gameData.handsBeforeRaise = ntohs(tmpData->handsBeforeRaise);
@@ -824,7 +825,6 @@ NetPacketPlayerJoined::SetData(const NetPacketPlayerJoined::Data &inData)
// Set the data.
tmpData->playerFlags = htons((inData.ptype == PLAYER_TYPE_HUMAN) ? NET_PLAYER_FLAG_HUMAN : 0);
tmpData->playerId = htons(inData.playerId);
tmpData->playerNumber = htons(inData.playerNumber);
tmpData->playerNameLength = htons(playerNameLen);
char *namePtr = (char *)tmpData + sizeof(NetPacketPlayerJoinedData);
memcpy(namePtr, inData.playerName.c_str(), playerNameLen);
@@ -839,7 +839,6 @@ NetPacketPlayerJoined::GetData(NetPacketPlayerJoined::Data &outData) const
outData.ptype = (ntohs(tmpData->playerFlags) & NET_PLAYER_FLAG_HUMAN) ? PLAYER_TYPE_HUMAN : PLAYER_TYPE_COMPUTER;
outData.playerId = ntohs(tmpData->playerId);
outData.playerNumber = ntohs(tmpData->playerNumber);
char *namePtr = (char *)tmpData + sizeof(NetPacketPlayerJoinedData);
outData.playerName = string(namePtr, ntohs(tmpData->playerNameLength));
}
@@ -969,11 +968,33 @@ NetPacketGameStart::Clone() const
void
NetPacketGameStart::SetData(const NetPacketGameStart::Data &inData)
{
u_int16_t numPlayers = (u_int16_t)inData.playerSlots.size();
if (!numPlayers || numPlayers > MAX_NUMBER_OF_PLAYERS || numPlayers != inData.startData.numberOfPlayers)
throw NetException(ERR_NET_INVALID_PLAYER_COUNT, 0);
// Resize the packet so that the data fits in.
Resize((u_int16_t)
(sizeof(NetPacketGameStartData) + ADD_PADDING(numPlayers * sizeof(PlayerSlotData))));
NetPacketGameStartData *tmpData = (NetPacketGameStartData *)GetRawData();
assert(tmpData);
tmpData->startDealerPlayerId = htons(inData.startData.startDealerPlayerId);
tmpData->numberOfPlayers = htons(inData.startData.numberOfPlayers);
tmpData->numberOfPlayers = htons(numPlayers);
PlayerSlotList::const_iterator i = inData.playerSlots.begin();
PlayerSlotList::const_iterator end = inData.playerSlots.end();
// Copy the player slot data to continous memory
PlayerSlotData *curPlayerSlotData =
(PlayerSlotData *)((char *)tmpData + sizeof(NetPacketGameStartData));
while (i != end)
{
curPlayerSlotData->playerId = htons((*i).playerId);
++curPlayerSlotData;
++i;
}
}
void
@@ -983,7 +1004,21 @@ NetPacketGameStart::GetData(NetPacketGameStart::Data &outData) const
assert(tmpData);
outData.startData.startDealerPlayerId = ntohs(tmpData->startDealerPlayerId);
outData.startData.numberOfPlayers = ntohs(tmpData->numberOfPlayers);
u_int16_t numPlayers = ntohs(tmpData->numberOfPlayers);
outData.startData.numberOfPlayers = numPlayers;
PlayerSlotData *curPlayerSlotData =
(PlayerSlotData *)((char *)tmpData + sizeof(NetPacketGameStartData));
// Store all available player slots.
for (int i = 0; i < numPlayers; i++)
{
PlayerSlot tmpPlayerSlot;
tmpPlayerSlot.playerId = ntohs(curPlayerSlotData->playerId);
outData.playerSlots.push_back(tmpPlayerSlot);
++curPlayerSlotData;
}
}
const NetPacketGameStart *
@@ -998,10 +1033,11 @@ NetPacketGameStart::Check(const NetPacketHeader* data) const
assert(data);
u_int16_t dataLen = ntohs(data->length);
if (dataLen != sizeof(NetPacketGameStartData))
if (dataLen < sizeof(NetPacketGameStartData))
{
throw NetException(ERR_SOCK_INVALID_PACKET, 0);
}
// TODO additional checking
}
//-----------------------------------------------------------------------------
+15 -4
View File
@@ -290,7 +290,7 @@ ServerRecvStateInit::InternalProcess(ServerRecvThread &server, SessionWrapper se
// Create player data object.
boost::shared_ptr<PlayerData> tmpPlayerData(
new PlayerData(m_curUniquePlayerId++, server.GetNextPlayerNumber(), joinGameData.ptype));
new PlayerData(m_curUniquePlayerId++, 0, joinGameData.ptype));
tmpPlayerData->SetName(joinGameData.playerName);
tmpPlayerData->SetNetSessionData(session.sessionData);
@@ -299,7 +299,6 @@ ServerRecvStateInit::InternalProcess(ServerRecvThread &server, SessionWrapper se
NetPacketJoinGameAck::Data joinGameAckData;
joinGameAckData.sessionId = session.sessionData->GetId(); // TODO: currently unused.
joinGameAckData.yourPlayerUniqueId = tmpPlayerData->GetUniqueId();
joinGameAckData.yourPlayerNum = tmpPlayerData->GetNumber();
joinGameAckData.gameData = server.GetGameData();
static_cast<NetPacketJoinGameAck *>(answer.get())->SetData(joinGameAckData);
server.GetSender().Send(session.sessionData->GetSocket(), answer);
@@ -314,7 +313,6 @@ ServerRecvStateInit::InternalProcess(ServerRecvThread &server, SessionWrapper se
NetPacketPlayerJoined::Data otherPlayerJoinedData;
otherPlayerJoinedData.playerId = (*player_i)->GetUniqueId();
otherPlayerJoinedData.playerName = (*player_i)->GetName();
otherPlayerJoinedData.playerNumber = (*player_i)->GetNumber();
otherPlayerJoinedData.ptype = (*player_i)->GetType();
static_cast<NetPacketPlayerJoined *>(otherPlayerJoined.get())->SetData(otherPlayerJoinedData);
server.GetSender().Send(session.sessionData->GetSocket(), otherPlayerJoined);
@@ -327,7 +325,6 @@ ServerRecvStateInit::InternalProcess(ServerRecvThread &server, SessionWrapper se
NetPacketPlayerJoined::Data thisPlayerJoinedData;
thisPlayerJoinedData.playerId = tmpPlayerData->GetUniqueId();
thisPlayerJoinedData.playerName = tmpPlayerData->GetName();
thisPlayerJoinedData.playerNumber = tmpPlayerData->GetNumber();
thisPlayerJoinedData.ptype = tmpPlayerData->GetType();
static_cast<NetPacketPlayerJoined *>(thisPlayerJoined.get())->SetData(thisPlayerJoinedData);
server.SendToAllPlayers(thisPlayerJoined);
@@ -382,6 +379,20 @@ ServerRecvStateStartGame::Process(ServerRecvThread &server)
NetPacketGameStart::Data gameStartData;
gameStartData.startData = server.GetStartData();
// Assign player numbers. Assume Player List is sorted by number.
PlayerDataList tmpPlayerList = server.GetPlayerDataList();
PlayerDataList::iterator player_i = tmpPlayerList.begin();
PlayerDataList::iterator player_end = tmpPlayerList.end();
while (player_i != player_end)
{
NetPacketGameStart::PlayerSlot tmpPlayerSlot;
tmpPlayerSlot.playerId = (*player_i)->GetUniqueId();
gameStartData.playerSlots.push_back(tmpPlayerSlot);
++player_i;
}
static_cast<NetPacketGameStart *>(answer.get())->SetData(gameStartData);
server.SendToAllPlayers(answer);
+8 -13
View File
@@ -253,6 +253,8 @@ ServerRecvThread::InternalStartGame()
// Kick all players which are not fully connected.
RemoveNotEstablishedSessions();
// Set order of players.
AssignPlayerNumbers();
// Initialize the game.
GuiInterface &gui = GetGui();
@@ -559,31 +561,24 @@ ServerRecvThread::GetPlayerDataList() const
}
++session_i;
}
// Sort the list by player number.
playerList.sort(*boost::lambda::_1 < *boost::lambda::_2);
return playerList;
}
int
ServerRecvThread::GetNextPlayerNumber() const
void
ServerRecvThread::AssignPlayerNumbers()
{
int playerNumber = 0;
PlayerDataList playerList = GetPlayerDataList();
PlayerDataList::const_iterator player_i = playerList.begin();
PlayerDataList::const_iterator player_end = playerList.end();
PlayerDataList::iterator player_i = playerList.begin();
PlayerDataList::iterator player_end = playerList.end();
// Assume the player list is sorted by player number.
while (player_i != player_end)
{
if ((*player_i)->GetNumber() == playerNumber)
playerNumber++;
else
break;
(*player_i)->SetNumber(playerNumber);
++playerNumber;
++player_i;
}
return playerNumber;
}
void
+11 -4
View File
@@ -26,7 +26,7 @@
#include <gamedata.h>
#include <net/socket_helper.h>
#define NET_VERSION_MAJOR 1
#define NET_VERSION_MAJOR 2
#define NET_VERSION_MINOR 0
#define MIN_PACKET_SIZE 4
@@ -143,7 +143,6 @@ public:
struct Data
{
u_int32_t sessionId;
int16_t yourPlayerNum;
u_int16_t yourPlayerUniqueId;
GameData gameData;
};
@@ -169,7 +168,6 @@ public:
struct Data
{
u_int16_t playerId;
u_int16_t playerNumber;
PlayerType ptype;
std::string playerName;
};
@@ -215,9 +213,18 @@ protected:
class NetPacketGameStart : public NetPacket
{
public:
struct PlayerSlot
{
unsigned playerId;
};
typedef std::list<PlayerSlot> PlayerSlotList;
struct Data
{
StartData startData;
StartData startData;
PlayerSlotList playerSlots;
};
NetPacketGameStart();
+1 -1
View File
@@ -121,7 +121,7 @@ protected:
void SetSessionPlayerData(boost::shared_ptr<SessionData> sessionData, boost::shared_ptr<PlayerData> playerData);
PlayerDataList GetPlayerDataList() const;
int GetNextPlayerNumber() const;
void AssignPlayerNumbers();
ServerRecvState &GetState();
void SetState(ServerRecvState &newState);
+1
View File
@@ -55,6 +55,7 @@
#define ERR_NET_INVALID_ROUND 112
#define ERR_NET_PLAYER_KICKED 113
#define ERR_NET_INVALID_PLAYER_COUNT 114
#define ERR_NET_PLAYER_NOT_IN_GAME 115
// This is an internal message which is not reported.
#define MSG_SOCK_INTERNAL_PENDING 0