#include "server.h" #include "util.h" #include "pacman.pb.h" #include "block.h" #include "anyoption.h" #include "bonuspoint.h" #include "point.h" #include #include Server::Server(QWidget *parent) : SceneHolder(parent), m_host(NULL), m_bindaddress(QHostAddress::Any), m_port(Constants::Networking::port), m_numbots(0), m_rounds(3), m_curRound(0), m_running(false), m_finishRound(false) { /* determine max players by using order array */ for(m_maxplayers = 0; Color::order[m_maxplayers] != Color::none; ++m_maxplayers); } Server::~Server() { if (m_host != NULL) { foreach(ENetPeer *peer, m_clientConnections.keys()) enet_peer_disconnect(peer, 0); /* allow up to 3 seconds for the disconnect to succeed * and drop any packets received packets */ ENetEvent event; while (m_clientConnections.count() > 0 && enet_host_service(m_host, &event, 3000) > 0) { switch (event.type) { case ENET_EVENT_TYPE_RECEIVE: enet_packet_destroy(event.packet); break; case ENET_EVENT_TYPE_DISCONNECT: m_clientConnections.remove(event.peer); return; default: break; } } enet_host_destroy(m_host); } } bool Server::run() { /* create eating order list first */ for(unsigned int i = 0; i < m_maxplayers; ++i) m_eatingorder.append(Color::order[i]); m_eatingorder.append(m_eatingorder.at(0)); m_tickTimer = new QTimer(this); m_tickTimer->setInterval(Constants::tick); connect(m_tickTimer, SIGNAL(timeout()), this, SLOT(tick())); std::cout << "[Server] Running server..." << std::endl << "[Server] Max players: " << m_maxplayers << std::endl << "[Server] Number of bots: " << m_numbots << std::endl << "[Server] Number of rounds: " << m_rounds << std::endl; if (!waitForClientConnections()) return false; connect(this, SIGNAL(allPointsRemoved()), this, SLOT(setFinishRound())); initRoundMap(); return true; } void Server::tick() { //qDebug() << "[Tick] Doing server update"; if (m_finishRound) stopGame(true); if (!m_running) { Transmission::map_t map = Util::createEmptyMap(); sendUpdate(map); Util::deleteMap(map); return; } /* fetch key updates */ keyPressUpdate(); /* let the bots move */ foreach (Color::Color color, m_bots) botCalculate(m_actors[color]); /* move on the virtual map */ Transmission::map_t map = calculateUpdates(); updateMap(map); /* add a random bonus point */ QPoint pos = addRandomPoint(map, Transmission::bonuspoint); if (!pos.isNull()) updateMap(map, pos.x(), pos.y()); /* add/remove random colorized block */ if (this->property("coloredblocks").toBool()) colorizeBlocks(map); sendUpdate(map); Util::deleteMap(map); } Transmission::map_t Server::calculateUpdates() { QMap > movements; Transmission::map_t map = Util::createEmptyMap(); QMutableMapIterator i(m_actorMovements); while (i.hasNext()) { i.next(); Color::Color color = i.key(); Actor *actor = m_actors[color]; QPoint mapPosition = CoordToMapPosition(actor->pos().toPoint()); Actor::Movement direction = i.value(); int turn = 0; invalid_direction: ++turn; qDebug() << "[Calc] Actor wants to move: color=" << color << "pos=" << mapPosition << "direction=" << direction; QPoint newMapPosition = mapPosition + Actor::movementToPoint(direction); if (newMapPosition.x() < 0) newMapPosition.setX(0); if (newMapPosition.x() >= visualMap.size()) newMapPosition.setX(visualMap.size() - 1); if (newMapPosition.y() < 0) newMapPosition.setY(0); if (newMapPosition.y() >= visualMap[newMapPosition.x()].size()) newMapPosition.setY(visualMap[newMapPosition.x()].size() - 1); /* check if there's an item at new location of actor */ GameEntity *item = visualMap[newMapPosition.x()][newMapPosition.y()]; GameEntity *oldItem = visualMap[mapPosition.x()][mapPosition.y()]; if (item != NULL && oldItem != item) { qDebug() << "[Calc] Found item at new actor location"; if (!item->checkEnter(actor)) { /* movement invalid. e.g. move against wall */ newMapPosition = mapPosition; qDebug() << "[Calc] Item blocks actor"; } else { /* apply actions of entering this field */ GameEntity::EnteredState survive = item->enter(actor); if (survive == GameEntity::DestroyedEntity) map[newMapPosition.x()][newMapPosition.y()] = Transmission::empty | actor->color(); else if (survive == GameEntity::DestroyedActor) { map[newMapPosition.x()][newMapPosition.y()] = Transmission::death | actor->color(); m_actors[item->color()]->addRoundPoints(actor->getRoundPoints()); actor->finishRound(true); setFinishRound(); } } } /* movement didn't work - e.g. was blocked */ if (mapPosition == newMapPosition) { /* check turn */ if (turn == 1 && direction != actor->direction()) { /* set direction back to last known direction and try again */ qDebug() << "[Calc] Movement was blocked. Trying last known actor direction"; direction = actor->direction(); goto invalid_direction; } else { /* second turn didn't work too -> stop movement */ direction = Actor::None; qDebug() << "[Calc] No good direction known. Movement stopped"; } } /* store movement for collision */ movements.insert(color, QPair(mapPosition, newMapPosition)); /* set direction (used for collision detection) */ actor->setDirection(direction); /* DEBUG: uncomments to disable auto-movement */ //direction = Actor::None; /* actor is not moving anymore: remove from list */ if (direction == Actor::None) i.remove(); } /* check for actor collision */ QList blocked; foreach(Color::Color color, movements.keys()) { Actor *actor = m_actors[color]; QPoint oldpos = movements[color].first; QPoint newpos = movements[color].second; QPoint scenepos = actor->pos().toPoint(); /* first move actor to new position */ actor->move(actor->direction()); /* next check for collisions */ Actor *orderer = NULL; Actor *meal = NULL; foreach(Actor *other, m_actors) { if (actor == other) continue; if (!actor->collidesWithItem(other)) continue; /* both move in the same direction */ if (actor->direction() == other->direction()) continue; if (other->canEat(actor, m_eatingorder)) { qDebug() << "[Collision] Actor" << actor->color() << "got EATEN by" << other->color(); orderer = other; meal = actor; break; } else if (actor->canEat(other, m_eatingorder)) { qDebug() << "[Collision] Actor" << actor->color() << "EATS" << other->color(); orderer = actor; meal = other; blocked.append(other); break; } else { qDebug() << "[Collision] Actor" << actor->color() << "got BLOCKED by" << other->color(); blocked.append(actor); /* no break here */ } } /* update map depending on collision */ if (orderer != NULL && meal != NULL) { map[newpos.x()][newpos.y()] |= Transmission::pacman | Transmission::death | orderer->color() | meal->color(); orderer->addRoundPoints(meal->getRoundPoints()); meal->finishRound(true); setFinishRound(); } else if (blocked.contains(actor)) { /* move actor back early cause he won't move */ actor->setPos(scenepos); map[oldpos.x()][oldpos.y()] |= Transmission::pacman | color; } else { map[newpos.x()][newpos.y()] |= Transmission::pacman | color; } } /* move all actors back to their origin position */ foreach(Color::Color color, movements.keys()) m_actors[color]->setPos(mapPositionToCoord(movements[color].first)); #ifndef QT_NO_DEBUG /* collision sanity check: colors must be unique */ foreach(Color::Color col, m_eatingorder.toSet()) { QList found; for (unsigned int x = 0; x < Constants::map_size.width; ++x) { for (unsigned int y = 0; y < Constants::map_size.height; ++y) { if ((map[x][y] & Transmission::color_mask) & col) found.append(QPoint(x, y)); } } if (found.count() > 1) qCritical() << "[Collision] found" << found.count() << "fields with color=" << col; } #endif return map; } QPoint Server::addRandomPoint(Transmission::map_t map, Transmission::field_t type) { int chance = Constants::Game::bouns_point_chance - Constants::Game::bouns_point_chance_playerfactor * (m_maxplayers - 1); int rand = (int) (chance * (qrand() / (RAND_MAX + 1.0))); if (rand != 0) return QPoint(); /* get list of valid positions */ QList validpos; for (unsigned int x = 0; x < Constants::map_size.width; ++x) { for (unsigned int y = 0; y < Constants::map_size.height; ++y) { if (visualMap[x][y] == NULL) validpos.append(QPoint(x, y)); } } /* remove actors positions from list * performance would be better if actors would be listed in visualMap too * but this isn't possible that easily. see comment in updateMap(map) */ foreach (Actor *actor, m_actors) { QPoint actorpos = CoordToMapPosition(actor->pos().toPoint()); validpos.removeAll(actorpos); } if (validpos.empty()) return QPoint(); rand = (int) (validpos.size() * (qrand() / (RAND_MAX + 1.0))); QPoint pos = validpos.at(rand); map[pos.x()][pos.y()] = type; return pos; } void Server::colorizeBlocks(Transmission::map_t map) { /* decrement tickcount of colored blocks */ if (!m_coloredBlocks.empty()) { QMutableMapIterator i(m_coloredBlocks); while(i.hasNext()) { i.next(); unsigned val = i.value(); if (val > 0) i.setValue(--val); else { QPoint block = i.key(); /* check for actor collision */ bool skip = false; foreach (Actor *actor, m_actors) { if (CoordToMapPosition(actor->pos().toPoint()) == block) skip = true; if (skip) break; } if (skip) continue; map[block.x()][block.y()] |= Transmission::block | Color::none; updateMap(map, block.x(), block.y()); m_blocks.append(block); i.remove(); } } } /* colorize a random block */ int rand = (int) (Constants::Game::colorize_block_chance * (qrand() / (RAND_MAX + 1.0))); if (rand == 0) { rand = (int) (m_blocks.size() * (qrand() / (RAND_MAX + 1.0))); QPoint block = m_blocks.at(rand); m_blocks.removeAt(rand); unsigned int min = Constants::Game::colorize_block_tickcount_min; unsigned int max = Constants::Game::colorize_block_tickcount_max; int tickcount = min + (int) ((max - min + 1) * (qrand() / (RAND_MAX + 1.0))); m_coloredBlocks.insert(block, tickcount); unsigned int color = (int) (m_maxplayers * (qrand() / (RAND_MAX + 1.0))); map[block.x()][block.y()] |= Transmission::block | Color::order[color]; updateMap(map, block.x(), block.y()); } } bool Server::waitForClientConnections() { // server must stay alive as long as sockets (qt parent mem mechanism) ENetAddress address; address.host = ENET_HOST_ANY; if (m_bindaddress != QHostAddress::Any) enet_address_set_host(&address, qPrintable(m_bindaddress.toString())); address.port = m_port; m_host = enet_host_create(&address, m_maxplayers - m_numbots, 1, 0, 0); if (m_host == NULL) { qCritical() << "An error occurred while trying to create an ENet server host"; return false; } char buf[1024]; enet_address_get_host_ip(&m_host->address, buf, 1024); std::cout << "[Server] Listening on: " << qPrintable(QString("%1:%2").arg(buf).arg(m_host->address.port)) << std::endl; /* add bots first */ for (unsigned int i = (m_maxplayers - m_numbots); i < m_maxplayers; ++i) { m_bots.append(Color::order[i]); m_actorMovements[Color::order[i]] = Actor::None; } std::cout << "[Server] Waiting for clients" << std::endl; ProtoBuf::Init packet; packet.set_maxplayers(m_maxplayers); unsigned int i = 0; while(m_clientConnections.count() < int(m_maxplayers - m_numbots)) { ENetEvent event; if (enet_host_service(m_host, &event, 3000) > 0) { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: { /* assign color and notify client */ Color::Color color = Color::order[i++]; m_clientConnections[event.peer] = color; packet.set_color(color); Util::sendPacket(packet, event.peer, m_host); std::cout << "[Connect] New Player: color=" << qPrintable(Util::colorToString(color)) << std::endl; } break; case ENET_EVENT_TYPE_RECEIVE: keyPressUpdate(&event); break; case ENET_EVENT_TYPE_DISCONNECT: keyPressUpdate(&event); break; default: break; } } } qDebug() << "[Server] All Clients connected"; return true; } void Server::sendUpdate(Transmission::map_t map, bool firstPacket) { m_updatepacket.Clear(); for (unsigned int x = 0; x < Constants::map_size.width; ++x) { for (unsigned int y = 0; y < Constants::map_size.height; ++y) m_updatepacket.add_field(map[x][y]); } for(unsigned int i = 0; i < m_maxplayers; ++i) { m_updatepacket.add_round_points(m_actors.value(Color::order[i])->getRoundPoints()); m_updatepacket.add_game_points(m_actors.value(Color::order[i])->getGamePoints()); } /* we send the eating_order inside the first packet */ if (firstPacket) { foreach(Color::Color color, m_eatingorder) m_updatepacket.add_eating_order(color); } QSharedPointer data = Util::createPacket(m_updatepacket); QMutableMapIterator i(m_clientConnections); while(i.hasNext()) { i.next(); ENetPeer *peer = i.key(); if (!Util::sendPacket(data.data(), peer, m_host)) { qWarning() << "[Connect] Error while sending data to client" << i.value() << "-> Disconnecting..."; std::cout << "[Connect] Actor color=" << qPrintable(Util::colorToString(i.value())) << " is now a bot" << std::endl; m_bots.append(i.value()); ++m_numbots; i.remove(); } } if (m_maxplayers == m_numbots) { std::cout << "[Connect] No more real players left. Exiting..." << std::endl; qApp->quit(); } } void Server::botCalculate(Actor *actor) { QPoint actorpos = CoordToMapPosition(actor->pos().toPoint()); /* first make list of possible directions based on current actor position */ QHash directions; if (actorpos.x() > 0) directions.insert(Actor::Left, 0); if (actorpos.x() < visualMap.size() - 1) directions.insert(Actor::Right, 0); if (actorpos.y() > 0) directions.insert(Actor::Up, 0); if (actorpos.y() < visualMap[actorpos.x()].size() - 1) directions.insert(Actor::Down, 0); /* check neighbours for blocks first */ QMutableHashIterator i(directions); while(i.hasNext()) { i.next(); QPoint newpos = actorpos + Actor::movementToPoint(i.key()); if (newpos.x() < 0 || newpos.x() >= visualMap.size()) continue; if (newpos.y() < 0 || newpos.y() >= visualMap[newpos.x()].size()) continue; GameEntity *item = visualMap[newpos.x()][newpos.y()]; /* check if neighbour is a block */ Block *block = qgraphicsitem_cast(item); if (block != NULL && block->color() != actor->color()) i.remove(); } /* we're enclosed by blocks */ if (directions.empty()) return; /* determine if other actors are in range to be afraid/to hunt */ int mindistance = Constants::AI::player_minimum_distance; QList pos_afraid; QList pos_hunt; foreach (Actor *other, m_actors) { if (actor == other) continue; QList *ptr = NULL; if (other->canEat(actor, m_eatingorder)) ptr = &pos_afraid; else if (actor->canEat(other, m_eatingorder)) ptr = &pos_hunt; if (ptr == NULL) continue; QPoint otherpos = CoordToMapPosition(other->pos().toPoint()); QPoint distance = actorpos - otherpos; if (distance.manhattanLength() < mindistance) ptr->append(otherpos); } /* check new directions and change direction-weight */ i = directions; while(i.hasNext()) { i.next(); QPoint newpos = actorpos + Actor::movementToPoint(i.key()); /* check for new positions in afraid list */ foreach(QPoint otherpos, pos_afraid) { int olddistance = (actorpos - otherpos).manhattanLength(); int newdistance = (newpos - otherpos).manhattanLength(); if (newdistance >= olddistance) i.setValue(i.value() + Constants::AI::weight_afraid); /* check for blocks of own color: other pacman can't follow their */ GameEntity *item = visualMap[newpos.x()][newpos.y()]; Block *block = qgraphicsitem_cast(item); if (block != NULL && block->color() == actor->color()) i.setValue(i.value() + Constants::AI::weight_colorblock); } /* check for new positions in hunt list */ foreach(QPoint otherpos, pos_hunt) { int olddistance = (actorpos - otherpos).manhattanLength(); int newdistance = (newpos - otherpos).manhattanLength(); if (newdistance <= olddistance) i.setValue(i.value() + Constants::AI::weight_hunt); } /* check for bonuspoint */ GameEntity *item = visualMap[newpos.x()][newpos.y()]; BonusPoint *bpoint = qgraphicsitem_cast(item); if (bpoint != NULL) i.setValue(i.value() + Constants::AI::weight_bonus_point); /* check for normal point */ Point *point = qgraphicsitem_cast(item); if (point != NULL) i.setValue(i.value() + Constants::AI::weight_point); } /* sort directions */ QList weightlist = directions.values(); qSort(weightlist.begin(), weightlist.end(), qGreater()); /* remove directions with lesser weight */ unsigned int max = weightlist.at(0); i = directions; while(i.hasNext()) { i.next(); if (i.value() < max) i.remove(); } QList list = directions.keys(); /* default is no direction change */ if (list.contains(actor->direction())) return; /* random direction */ int rand = (int) (list.size() * (qrand() / (RAND_MAX + 1.0))); m_actorMovements[actor->color()] = list.at(rand); } void Server::keyPressUpdate() { ProtoBuf::KeyPressUpdate packet; ENetEvent event; while (enet_host_service(m_host, &event, 1) > 0) keyPressUpdate(&event); } void Server::keyPressUpdate(ENetEvent *event) { ProtoBuf::KeyPressUpdate packet; switch(event->type) { case ENET_EVENT_TYPE_RECEIVE: { QSharedPointer data = Util::receivePacket(event->packet); enet_packet_destroy(event->packet); bool worked = packet.ParseFromArray(data->data(), data->size()); Q_ASSERT(worked); Q_UNUSED(worked); Transmission::field_t direction = packet.newkey(); Color::Color color = m_clientConnections[event->peer]; qDebug() << "[KeyPress] actor=" << color << "direction=" << direction; m_actorMovements[color] = Util::transmissionMovementToActor(direction); } break; case ENET_EVENT_TYPE_DISCONNECT: { Color::Color color = m_clientConnections[event->peer]; std::cout << "[Connect] Actor color=" << qPrintable(Util::colorToString(color)) << " is now a bot" << std::endl; m_bots.append(color); ++m_numbots; m_clientConnections.remove(event->peer); } break; default: break; } } void Server::initRoundMap() { std::cout << "[Game] New round starts..." << std::endl; m_tickTimer->stop(); /* reset scene and clean up items */ reset(); /* randomize color eating order */ m_eatingorder.removeLast(); random_shuffle(m_eatingorder.begin(), m_eatingorder.end()); m_eatingorder.append(m_eatingorder.at(0)); /* create new map */ Transmission::map_t map = Util::createDemoMap(); Util::placeActors(map, m_maxplayers, Color::order); Util::fillPoints(map); #if 0 // actor eating actor tests - TODO: remove m_actorMovements.clear(); #if 0 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][1] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); #elif 0 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][3] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); #elif 0 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][4] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); #elif 0 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][5] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); #elif 0 //works map[0][0] = Color::order[1] | Transmission::pacman; map[0][5] = Color::order[0] | Transmission::pacman; m_actorMovements.insert(Color::order[1], Actor::Down); #elif 0 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][5] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); m_actorMovements.insert(Color::order[1], Actor::Up); #elif 0 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][6] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); m_actorMovements.insert(Color::order[1], Actor::Up); #elif 0 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][7] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); m_actorMovements.insert(Color::order[1], Actor::Up); #elif 1 //works map[0][0] = Color::order[0] | Transmission::pacman; map[0][1] = Color::order[1] | Transmission::pacman; m_actorMovements.insert(Color::order[0], Actor::Down); m_actorMovements.insert(Color::order[1], Actor::Down); #endif #endif /* save positions of blocks for later usage */ m_blocks.clear(); for (unsigned int x = 0; x < Constants::map_size.width; ++x) { for (unsigned int y = 0; y < Constants::map_size.height; ++y) { Transmission::field_t &cur = map[x][y]; if (cur & Transmission::block) m_blocks.append(QPoint(x, y)); } } updateMap(map); sendUpdate(map, true); Util::deleteMap(map); map = NULL; m_actorMovements.clear(); disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL); connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(startGame())); AudioManager::self()->play(Sound::Intro, true); m_tickTimer->start(); } void Server::startGame() { m_running = true; } void Server::stopGame(bool delay) { /* first finish previous round */ foreach(Actor *actor, m_actors) actor->finishRound(); m_finishRound = false; m_running = false; /* add delay if requested */ if (delay) { disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL); connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(stopGame())); AudioManager::self()->play(Sound::Die, true); return; } std::cout << "[Game] Round finished..." << std::endl; /* do next-round work */ ++m_curRound; if(m_rounds == 0 || m_curRound < m_rounds) initRoundMap(); else { /* end of game */ std::cout << "[Game] All round finished. Exiting..." << std::endl; qApp->quit(); } } void Server::setFinishRound() { m_finishRound = true; } bool Server::parseCommandline() { AnyOption opt; opt.setVerbose(); /* usage strings must remain valid until parsing is done */ QString exec = QFileInfo(qApp->applicationFilePath()).fileName(); QByteArray usage; QTextStream out(&usage, QIODevice::ReadWrite | QIODevice::Text); out << "Usage: " << exec << " [OPTION]" << endl << "Usage: " << exec << " -h" << endl << endl; out << " -b, --bind " << endl << " Specifies the ip address on which the server listens for connections" << endl << " Default: " << m_bindaddress.toString() << endl << endl; opt.setOption("bind", 'b'); out << " -p, --port " << endl << " Specifies the port on which the server listens for connections" << endl << " Default: " << m_port << endl << endl; opt.setOption("port", 'p'); out << " -m, --maxplayers [1.." << m_maxplayers << "]" << endl << " Specifies the maximum number of players/pacmans" << endl << " Default: " << m_maxplayers << endl << endl; opt.setOption("maxplayers", 'm'); out << " --bots [0..maxplayers-1]" << endl << " Specifies the number of AI pacmans/bots" << endl << " Default: " << m_numbots << endl << endl; opt.setOption("bots"); out << " --nocolorblocks" << endl << " Disable random colorized blocks" << endl << endl; opt.setOption("rounds", 'r'); out << " -r, --rounds [0 | 1..n]" << endl << " Number of rounds to play" << endl << " Default: " << m_rounds << endl << endl; opt.setFlag("nocolorblocks"); out << " -h, --help" << endl << " Prints this help message" << endl; opt.setFlag("help", 'h'); out.flush(); opt.addUsage(usage.constData()); opt.processCommandArgs(qApp->argc(), qApp->argv()); if (opt.getFlag("help") || opt.getFlag('h')) { opt.printUsage(); return false; } if (opt.getValue("port") != NULL) { bool ok; m_port = QString(opt.getValue("port")).toUInt(&ok); if (!ok || m_port < 1 || m_port > 65535) { qCritical() << "Invalid port-option:" << opt.getValue("port") << endl << "Port must be between 1 and 65535"; return false; } } if (opt.getValue("bind") != NULL) { m_bindaddress = opt.getValue("bind"); if (m_bindaddress.isNull()) { qCritical() << "Invalid bind-option:" << opt.getValue("bind") << endl << "Bind address must be an ip address"; return false; } } if (opt.getValue("maxplayers") != NULL) { bool ok; unsigned int maxplayers = QString(opt.getValue("maxplayers")).toUInt(&ok); if (!ok || maxplayers < 1 || maxplayers > m_maxplayers) { qCritical() << "Invalid maxplayers-option:" << opt.getValue("maxplayers") << endl << "Maxplayers must be between 1 and" << m_maxplayers; return false; } m_maxplayers = maxplayers; if (m_maxplayers == 2) { qCritical() << "2 player game is not supported (who wins if a player gets eaten?)"; return false; } } if (opt.getValue("bots") != NULL) { bool ok; unsigned int numbots = QString(opt.getValue("bots")).toUInt(&ok); if (!ok || numbots >= m_maxplayers) { qCritical() << "Invalid numbots-options:" << opt.getValue("bots") << endl << "AI pacmans/bots must be between 0 and" << m_maxplayers - 1; return false; } m_numbots = numbots; } if (opt.getValue("rounds") != NULL) { bool ok; unsigned int rounds = QString(opt.getValue("rounds")).toUInt(&ok); if (!ok) { qCritical() << "Invalid number of rounds: " << opt.getValue("rounds") << endl; return false; } m_rounds = rounds; } this->setProperty("coloredblocks", !opt.getFlag("nocolorblocks")); return true; } bool operator<(const QPoint& lhs, const QPoint& rhs) { if (lhs.x() < rhs.x()) return true; else if (lhs.x() == rhs.x()) return lhs.y() < rhs.y(); else return false; } bool Constants::server = true; int main(int argc, char **argv) { /* Verify that the version of the library that we linked against is * compatible with the version of the headers we compiled against. */ GOOGLE_PROTOBUF_VERIFY_VERSION; QApplication app(argc, argv, false); app.setApplicationName("Pacman Server"); app.setWindowIcon(QIcon(":/appicon")); qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime())); int ret = 0; Server server; if (!ret && !server.parseCommandline()) ret = 1; if (!ret && !server.run()) ret = 1; if (!ret) ret = app.exec(); /* Delete all global objects allocated by libprotobuf */ google::protobuf::ShutdownProtobufLibrary(); return ret; }