improved code for automatic joining of persistent games; disconnecting + reconnecting works as it should with ISL

This commit is contained in:
Max-Wilhelm Bruker 2012-03-18 21:57:21 +01:00
parent 5db0fcd6ae
commit af8e357487
13 changed files with 215 additions and 135 deletions

View file

@ -129,6 +129,24 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
return authState;
}
void Server::addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId)
{
QWriteLocker locker(&persistentPlayersLock);
persistentPlayers.insert(userName, PlayerReference(roomId, gameId, playerId));
}
void Server::removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId)
{
QWriteLocker locker(&persistentPlayersLock);
persistentPlayers.remove(userName, PlayerReference(roomId, gameId, playerId));
}
QList<PlayerReference> Server::getPersistentPlayerReferences(const QString &userName) const
{
QReadLocker locker(&persistentPlayersLock);
return persistentPlayers.values(userName);
}
void Server::addClient(Server_ProtocolHandler *client)
{
QWriteLocker locker(&clientsLock);
@ -181,24 +199,56 @@ void Server::externalUserJoined(const ServerInfo_User &userInfo)
if (clients[i]->getAcceptsUserListChanges())
clients[i]->sendProtocolItem(*se);
delete se;
clientsLock.unlock();
ResponseContainer rc(-1);
newUser->joinPersistentGames(rc);
newUser->sendResponseContainer(rc, Response::RespNothing);
}
void Server::externalUserLeft(const QString &userName)
{
// This function is always called from the main thread via signal/slot.
QWriteLocker locker(&clientsLock);
clientsLock.lockForWrite();
Server_AbstractUserInterface *user = externalUsers.take(userName);
externalUsersBySessionId.remove(user->getUserInfo()->session_id());
clientsLock.unlock();
QMap<int, QPair<int, int> > userGames(user->getGames());
QMapIterator<int, QPair<int, int> > userGamesIterator(userGames);
roomsLock.lockForRead();
while (userGamesIterator.hasNext()) {
userGamesIterator.next();
Server_Room *room = rooms.value(userGamesIterator.value().first);
if (!room)
continue;
QMutexLocker roomGamesLocker(&room->gamesMutex);
Server_Game *game = room->getGames().value(userGamesIterator.key());
if (!game)
continue;
QMutexLocker gameLocker(&game->gameMutex);
Server_Player *player = game->getPlayers().value(userGamesIterator.value().second);
if (!player)
continue;
player->disconnectClient();
}
roomsLock.unlock();
delete user;
Event_UserLeft event;
event.set_name(userName.toStdString());
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
clientsLock.lockForRead();
for (int i = 0; i < clients.size(); ++i)
if (clients[i]->getAcceptsUserListChanges())
clients[i]->sendProtocolItem(*se);
clientsLock.unlock();
delete se;
}

View file

@ -4,9 +4,11 @@
#include <QObject>
#include <QStringList>
#include <QMap>
#include <QMultiMap>
#include <QMutex>
#include <QReadWriteLock>
#include "pb/serverinfo_user.pb.h"
#include "server_player_reference.h"
class Server_Game;
class Server_Room;
@ -77,6 +79,13 @@ public:
void addExternalUser(const ServerInfo_User &userInfo);
void removeExternalUser(const QString &userName);
const QMap<QString, Server_AbstractUserInterface *> &getExternalUsers() const { return externalUsers; }
void addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId);
void removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId);
QList<PlayerReference> getPersistentPlayerReferences(const QString &userName) const;
private:
QMultiMap<QString, PlayerReference> persistentPlayers;
mutable QReadWriteLock persistentPlayersLock;
protected slots:
void externalUserJoined(const ServerInfo_User &userInfo);
void externalUserLeft(const QString &userName);

View file

@ -1,7 +1,15 @@
#include <QList>
#include <QPair>
#include <QDebug>
#include "server_abstractuserinterface.h"
#include "server_game.h"
#include "server_response_containers.h"
#include "server_player_reference.h"
#include "server.h"
#include "server_room.h"
#include "server_game.h"
#include "pb/event_game_joined.pb.h"
#include "pb/event_game_state_changed.pb.h"
#include <google/protobuf/descriptor.h>
void Server_AbstractUserInterface::sendProtocolItemByType(ServerMessage::MessageType type, const ::google::protobuf::Message &item)
@ -27,15 +35,61 @@ void Server_AbstractUserInterface::sendResponseContainer(const ResponseContainer
for (int i = 0; i < preResponseQueue.size(); ++i)
sendProtocolItemByType(preResponseQueue[i].first, *preResponseQueue[i].second);
Response response;
response.set_cmd_id(responseContainer.getCmdId());
response.set_response_code(responseCode);
::google::protobuf::Message *responseExtension = responseContainer.getResponseExtension();
if (responseExtension)
response.GetReflection()->MutableMessage(&response, responseExtension->GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(*responseExtension);
sendProtocolItem(response);
if (responseCode != Response::RespNothing) {
Response response;
response.set_cmd_id(responseContainer.getCmdId());
response.set_response_code(responseCode);
::google::protobuf::Message *responseExtension = responseContainer.getResponseExtension();
if (responseExtension)
response.GetReflection()->MutableMessage(&response, responseExtension->GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(*responseExtension);
sendProtocolItem(response);
}
const QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *> > &postResponseQueue = responseContainer.getPostResponseQueue();
for (int i = 0; i < postResponseQueue.size(); ++i)
sendProtocolItemByType(postResponseQueue[i].first, *postResponseQueue[i].second);
}
void Server_AbstractUserInterface::playerRemovedFromGame(Server_Game *game)
{
qDebug() << "Server_AbstractUserInterface::playerRemovedFromGame(): gameId =" << game->getGameId();
QMutexLocker locker(&gameListMutex);
games.remove(game->getGameId());
}
void Server_AbstractUserInterface::playerAddedToGame(int gameId, int roomId, int playerId)
{
qDebug() << "Server_AbstractUserInterface::playerAddedToGame(): gameId =" << gameId;
QMutexLocker locker(&gameListMutex);
games.insert(gameId, QPair<int, int>(roomId, playerId));
}
void Server_AbstractUserInterface::joinPersistentGames(ResponseContainer &rc)
{
QList<PlayerReference> gamesToJoin = server->getPersistentPlayerReferences(QString::fromStdString(userInfo->name()));
server->roomsLock.lockForRead();
for (int i = 0; i < gamesToJoin.size(); ++i) {
const PlayerReference &pr = gamesToJoin.at(i);
Server_Room *room = server->getRooms().value(pr.getRoomId());
if (!room)
continue;
QMutexLocker roomGamesLocker(&room->gamesMutex);
Server_Game *game = room->getGames().value(pr.getGameId());
if (!game)
continue;
QMutexLocker gameLocker(&game->gameMutex);
Server_Player *player = game->getPlayers().value(pr.getPlayerId());
player->setUserInterface(this);
playerAddedToGame(game->getGameId(), room->getId(), player->getPlayerId());
game->createGameJoinedEvent(player, rc, true);
}
server->roomsLock.unlock();
}

View file

@ -1,6 +1,9 @@
#ifndef SERVER_ABSTRACTUSERINTERFACE
#define SERVER_ABSTRACTUSERINTERFACE
#include <QMutex>
#include <QMap>
#include <QPair>
#include "serverinfo_user_container.h"
#include "pb/server_message.pb.h"
#include "pb/response.pb.h"
@ -14,6 +17,9 @@ class Server;
class Server_Game;
class Server_AbstractUserInterface : public ServerInfo_User_Container {
private:
mutable QMutex gameListMutex;
QMap<int, QPair<int, int> > games; // gameId -> (roomId, playerId)
protected:
Server *server;
public:
@ -23,8 +29,11 @@ public:
virtual int getLastCommandTime() const = 0;
virtual void playerRemovedFromGame(Server_Game *game) = 0;
virtual void playerAddedToGame(int gameId, int roomId, int playerId) = 0;
void playerRemovedFromGame(Server_Game *game);
void playerAddedToGame(int gameId, int roomId, int playerId);
void joinPersistentGames(ResponseContainer &rc);
QMap<int, QPair<int, int> > getGames() const { QMutexLocker locker(&gameListMutex); return games; }
virtual void sendProtocolItem(const Response &item) = 0;
virtual void sendProtocolItem(const SessionEvent &item) = 0;

View file

@ -48,6 +48,7 @@
Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const QString &_description, const QString &_password, int _maxPlayers, const QList<int> &_gameTypes, bool _onlyBuddies, bool _onlyRegistered, bool _spectatorsAllowed, bool _spectatorsNeedPassword, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, Server_Room *_room)
: QObject(),
room(_room),
nextPlayerId(0),
hostId(0),
creatorInfo(new ServerInfo_User(_creatorInfo)),
gameStarted(false),
@ -371,58 +372,38 @@ void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface, Respons
{
QMutexLocker locker(&gameMutex);
const QList<int> &keyList = players.keys();
int playerId = keyList.isEmpty() ? 0 : (keyList.last() + 1);
Server_Player *newPlayer = new Server_Player(this, playerId, userInterface->copyUserInfo(true), spectator, userInterface);
Server_Player *newPlayer = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true), spectator, userInterface);
newPlayer->moveToThread(thread());
Event_Join joinEvent;
joinEvent.mutable_player_properties()->CopyFrom(newPlayer->getProperties(true));
sendGameEventContainer(prepareGameEvent(joinEvent, -1));
const QString playerName = QString::fromStdString(newPlayer->getUserInfo()->name());
if (spectator)
allSpectatorsEver.insert(QString::fromStdString(newPlayer->getUserInfo()->name()));
allSpectatorsEver.insert(playerName);
else
allPlayersEver.insert(QString::fromStdString(newPlayer->getUserInfo()->name()));
players.insert(playerId, newPlayer);
allPlayersEver.insert(playerName);
players.insert(newPlayer->getPlayerId(), newPlayer);
if (newPlayer->getUserInfo()->name() == creatorInfo->name()) {
hostId = playerId;
sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), playerId));
hostId = newPlayer->getPlayerId();
sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId));
}
if (broadcastUpdate)
emit gameInfoChanged(getInfo());
if ((newPlayer->getUserInfo()->user_level() & ServerInfo_User::IsRegistered) && !spectator)
room->getServer()->addPersistentPlayer(playerName, room->getId(), gameId, newPlayer->getPlayerId());
userInterface->playerAddedToGame(gameId, room->getId(), newPlayer->getPlayerId());
Event_GameJoined event1;
event1.set_room_id(room->getId());
event1.set_game_id(gameId);
event1.set_game_description(description.toStdString());
event1.set_host_id(hostId);
event1.set_player_id(newPlayer->getPlayerId());
event1.set_spectator(newPlayer->getSpectator());
event1.set_spectators_can_talk(spectatorsCanTalk);
event1.set_spectators_see_everything(spectatorsSeeEverything);
event1.set_resuming(false);
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, Server_AbstractUserInterface::prepareSessionEvent(event1));
Event_GameStateChanged event2;
QListIterator<ServerInfo_Player> gameStateIterator(getGameState(newPlayer, false, true));
while (gameStateIterator.hasNext())
event2.add_player_list()->CopyFrom(gameStateIterator.next());
event2.set_seconds_elapsed(secondsElapsed);
event2.set_game_started(gameStarted);
event2.set_active_player_id(activePlayer);
event2.set_active_phase(activePhase);
rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1));
createGameJoinedEvent(newPlayer, rc, false);
}
void Server_Game::removePlayer(Server_Player *player)
{
QMutexLocker locker(&gameMutex);
room->getServer()->removePersistentPlayer(QString::fromStdString(player->getUserInfo()->name()), room->getId(), gameId, player->getPlayerId());
players.remove(player->getPlayerId());
GameEventStorage ges;
@ -688,6 +669,31 @@ QList<ServerInfo_Player> Server_Game::getGameState(Server_Player *playerWhosAski
return result;
}
void Server_Game::createGameJoinedEvent(Server_Player *player, ResponseContainer &rc, bool resuming)
{
Event_GameJoined event1;
event1.set_room_id(room->getId());
event1.set_game_id(gameId);
event1.set_game_description(description.toStdString());
event1.set_host_id(hostId);
event1.set_player_id(player->getPlayerId());
event1.set_spectator(player->getSpectator());
event1.set_spectators_can_talk(spectatorsCanTalk);
event1.set_spectators_see_everything(spectatorsSeeEverything);
event1.set_resuming(resuming);
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, Server_AbstractUserInterface::prepareSessionEvent(event1));
Event_GameStateChanged event2;
QListIterator<ServerInfo_Player> gameStateIterator(getGameState(player, player->getSpectator() && spectatorsSeeEverything, true));
while (gameStateIterator.hasNext())
event2.add_player_list()->CopyFrom(gameStateIterator.next());
event2.set_seconds_elapsed(secondsElapsed);
event2.set_game_started(gameStarted);
event2.set_active_player_id(activePlayer);
event2.set_active_phase(activePhase);
rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1));
}
void Server_Game::sendGameEventContainer(GameEventContainer *cont, GameEventStorageItem::EventRecipients recipients, int privatePlayerId)
{
QMutexLocker locker(&gameMutex);

View file

@ -42,6 +42,7 @@ class Server_Game : public QObject {
Q_OBJECT
private:
Server_Room *room;
int nextPlayerId;
int hostId;
ServerInfo_User *creatorInfo;
QMap<int, Server_Player *> players;
@ -108,6 +109,7 @@ public:
void nextTurn();
int getSecondsElapsed() const { return secondsElapsed; }
void createGameJoinedEvent(Server_Player *player, ResponseContainer &rc, bool resuming);
void sendGameStateToPlayers();
QList<ServerInfo_Player> getGameState(Server_Player *playerWhosAsking, bool omniscient = false, bool withUserInfo = false) const;

View file

@ -1578,3 +1578,11 @@ void Server_Player::setUserInterface(Server_AbstractUserInterface *_userInterfac
ges.enqueueGameEvent(event, playerId);
ges.sendToGame(game);
}
void Server_Player::disconnectClient()
{
if (!(userInfo->user_level() & ServerInfo_User::IsRegistered) || spectator)
game->removePlayer(this);
else
setUserInterface(0);
}

View file

@ -83,6 +83,7 @@ public:
void prepareDestroy();
Server_AbstractUserInterface *getUserInterface() const { return userInterface; }
void setUserInterface(Server_AbstractUserInterface *_userInterface);
void disconnectClient();
void setPlayerId(int _id) { playerId = _id; }
bool getReadyStart() const { return readyStart; }

View file

@ -0,0 +1,17 @@
#ifndef SERVER_PLAYER_REFERENCE_H
#define SERVER_PLAYER_REFERENCE_H
class PlayerReference {
private:
int roomId;
int gameId;
int playerId;
public:
PlayerReference(int _roomId, int _gameId, int _playerId) : roomId(_roomId), gameId(_gameId), playerId(_playerId) { }
int getRoomId() const { return roomId; }
int getGameId() const { return gameId; }
int getPlayerId() const { return playerId; }
bool operator==(const PlayerReference &other) { return ((roomId == other.roomId) && (gameId == other.gameId) && (playerId == other.playerId)); }
};
#endif

View file

@ -49,15 +49,11 @@ void Server_ProtocolHandler::prepareDestroy()
{
qDebug("Server_ProtocolHandler::prepareDestroy");
server->removeClient(this);
QMapIterator<int, Server_Room *> roomIterator(rooms);
while (roomIterator.hasNext())
roomIterator.next().value()->removeClient(this);
gameListMutex.lock();
QMap<int, QPair<int, int> > tempGames(games);
gameListMutex.unlock();
QMap<int, QPair<int, int> > tempGames(getGames());
server->roomsLock.lockForRead();
QMapIterator<int, QPair<int, int> > gameIterator(tempGames);
@ -81,35 +77,18 @@ void Server_ProtocolHandler::prepareDestroy()
continue;
}
if ((authState == UnknownUser) || p->getSpectator())
g->removePlayer(p);
else
p->setUserInterface(0);
p->disconnectClient();
g->gameMutex.unlock();
r->gamesMutex.unlock();
}
server->roomsLock.unlock();
server->removeClient(this);
deleteLater();
}
void Server_ProtocolHandler::playerRemovedFromGame(Server_Game *game)
{
qDebug() << "Server_ProtocolHandler::playerRemovedFromGame(): gameId =" << game->getGameId();
QMutexLocker locker(&gameListMutex);
games.remove(game->getGameId());
}
void Server_ProtocolHandler::playerAddedToGame(int gameId, int roomId, int playerId)
{
qDebug() << "Server_ProtocolHandler::playerAddedToGame(): gameId =" << gameId;
QMutexLocker locker(&gameListMutex);
games.insert(gameId, QPair<int, int>(roomId, playerId));
}
void Server_ProtocolHandler::sendProtocolItem(const Response &item)
{
ServerMessage msg;
@ -217,13 +196,10 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const
qDebug() << QString::fromStdString(cont.DebugString());
gameListMutex.lock();
if (!games.contains(cont.game_id())) {
gameListMutex.unlock();
QMap<int, QPair<int, int> > gameMap = getGames();
if (!gameMap.contains(cont.game_id()))
return Response::RespNotInRoom;
}
const QPair<int, int> roomIdAndPlayerId = games.value(cont.game_id());
gameListMutex.unlock();
const QPair<int, int> roomIdAndPlayerId = gameMap.value(cont.game_id());
QReadLocker roomsLocker(&server->roomsLock);
Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first);
@ -394,53 +370,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
re->add_ignore_list()->CopyFrom(ignoreIterator.next().value());
}
server->roomsLock.lockForRead();
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
while (roomIterator.hasNext()) {
Server_Room *room = roomIterator.next().value();
room->gamesMutex.lock();
QMapIterator<int, Server_Game *> gameIterator(room->getGames());
while (gameIterator.hasNext()) {
Server_Game *game = gameIterator.next().value();
QMutexLocker gameLocker(&game->gameMutex);
const QList<Server_Player *> &gamePlayers = game->getPlayers().values();
for (int j = 0; j < gamePlayers.size(); ++j)
if (gamePlayers[j]->getUserInfo()->name() == userInfo->name()) {
gamePlayers[j]->setUserInterface(this);
gameListMutex.lock();
games.insert(game->getGameId(), QPair<int, int>(room->getId(), gamePlayers[j]->getPlayerId()));
gameListMutex.unlock();
Event_GameJoined event1;
event1.set_room_id(room->getId());
event1.set_game_id(game->getGameId());
event1.set_game_description(game->getDescription().toStdString());
event1.set_host_id(game->getHostId());
event1.set_player_id(gamePlayers[j]->getPlayerId());
event1.set_spectator(gamePlayers[j]->getSpectator());
event1.set_spectators_can_talk(game->getSpectatorsCanTalk());
event1.set_spectators_see_everything(game->getSpectatorsSeeEverything());
event1.set_resuming(true);
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event1));
Event_GameStateChanged event2;
QListIterator<ServerInfo_Player> gameStateIterator(game->getGameState(gamePlayers[j], gamePlayers[j]->getSpectator() && game->getSpectatorsSeeEverything(), true));
while (gameStateIterator.hasNext())
event2.add_player_list()->CopyFrom(gameStateIterator.next());
event2.set_seconds_elapsed(game->getSecondsElapsed());
event2.set_game_started(game->getGameStarted());
event2.set_active_player_id(game->getActivePlayer());
event2.set_active_phase(game->getActivePhase());
rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, game->prepareGameEvent(event2, -1));
break;
}
}
room->gamesMutex.unlock();
}
server->roomsLock.unlock();
joinPersistentGames(rc);
rc.setResponseExtension(re);
return Response::RespOk;

View file

@ -57,14 +57,12 @@ class Command_ShutdownServer;
class Server_ProtocolHandler : public QObject, public Server_AbstractUserInterface {
Q_OBJECT
protected:
QMap<int, QPair<int, int> > games; // gameId -> (roomId, playerId)
QMap<int, Server_Room *> rooms;
AuthenticationResult authState;
bool acceptsUserListChanges;
bool acceptsRoomListChanges;
private:
QMutex gameListMutex;
QList<int> messageSizeOverTime, messageCountOverTime;
int timeRunning, lastDataReceived;
@ -113,8 +111,6 @@ public slots:
public:
Server_ProtocolHandler(Server *_server, QObject *parent = 0);
~Server_ProtocolHandler();
void playerRemovedFromGame(Server_Game *game);
void playerAddedToGame(int gameId, int roomId, int playerId);
bool getAcceptsUserListChanges() const { return acceptsUserListChanges; }
bool getAcceptsRoomListChanges() const { return acceptsRoomListChanges; }

View file

@ -9,9 +9,6 @@ public:
int getLastCommandTime() const { return 0; }
void playerRemovedFromGame(Server_Game * /*game*/) { }
void playerAddedToGame(int /*gameId*/, int /*roomId*/, int /*playerId*/) { }
void sendProtocolItem(const Response &item);
void sendProtocolItem(const SessionEvent &item);
void sendProtocolItem(const GameEventContainer &item);

View file

@ -49,14 +49,6 @@ IslInterface::~IslInterface()
// As these signals are connected with Qt::QueuedConnection implicitly,
// we don't need to worry about them modifying the lists while we're iterating.
server->clientsLock.lockForRead();
QMapIterator<QString, Server_AbstractUserInterface *> extUsers(server->getExternalUsers());
while (extUsers.hasNext()) {
extUsers.next();
if (extUsers.value()->getUserInfo()->server_id() == serverId)
emit externalUserLeft(extUsers.key());
}
server->clientsLock.unlock();
server->roomsLock.lockForRead();
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
@ -72,6 +64,15 @@ IslInterface::~IslInterface()
room->usersLock.unlock();
}
server->roomsLock.unlock();
server->clientsLock.lockForRead();
QMapIterator<QString, Server_AbstractUserInterface *> extUsers(server->getExternalUsers());
while (extUsers.hasNext()) {
extUsers.next();
if (extUsers.value()->getUserInfo()->server_id() == serverId)
emit externalUserLeft(extUsers.key());
}
server->clientsLock.unlock();
}
void IslInterface::initServer()